feat: 费用模块重构 + 项目模块优化 + 数据库迁移
费用模块: - 字段精简为5个:费用类型(下拉6选)、月份(3年36月下拉)、金额、已发生金额、费用说明(textarea) - 三视图切换(总/季度/月度),按expense_month筛选,汇总卡片实时更新 - 类型筛选功能,Toolbar+汇总+表格三段式布局 - 删除按钮移至关闭按钮左侧,间距8px - 费用记录删除合计行,列宽固定200px,居中 项目模块: - 卡片网格6→7列(14卡片7×2满排),配置驱动 METRIC_CARDS - 新增费用/利润字段,利润=毛利-费用 - 表格行添加费用、利润列,比率列text-xs→text-sm - 项目流水tab费用与已付列对调,汇总卡片6→1×6 - 修复月度/季度切换时汇总卡片不更新 - 筛选区统一为卡片,去除'筛选:'前缀 数据库: - expense_records新增expense_type/expense_month/incurred_amount列 - migrations/columns.py幂等迁移,启动时自动执行
This commit is contained in:
@@ -1,35 +1,110 @@
|
||||
// expense.js — 费用管理模块
|
||||
|
||||
function renderExpense() {
|
||||
const records = state.data.expense || [];
|
||||
const money = (v) => `¥ ${(Number(v || 0)).toLocaleString("zh-CN", { minimumFractionDigits: 0, maximumFractionDigits: 2 })}`;
|
||||
const badge = (s) => {
|
||||
const m = { "已审批": "green", "已采购": "blue", "已完成": "slate", "待审批": "amber" };
|
||||
const c = m[s] || "amber";
|
||||
return `<span class="text-xs px-2 py-0.5 rounded-full bg-${c}-100 text-${c}-700 font-medium">${s}</span>`;
|
||||
};
|
||||
const esc = (s) => (s || "").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
||||
var records = state.data.expense || [];
|
||||
var money = function(v) { return '¥ ' + (Number(v || 0)).toLocaleString('zh-CN', { minimumFractionDigits: 0, maximumFractionDigits: 2 }); };
|
||||
var esc = function(s) { return (s || '').replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"'); };
|
||||
var EXPENSE_TYPES = ['通用费用','平台采购','人力成本','研发费用','奖金','其他'];
|
||||
|
||||
const totalAmount = records.reduce((s, r) => s + (r.amount || 0), 0);
|
||||
// 筛选状态
|
||||
var view = state.expenseView || 'total';
|
||||
var typeFilter = state.expenseFilter || '全部';
|
||||
var filtered = typeFilter === '全部' ? records : records.filter(function(r) { return r.expense_type === typeFilter; });
|
||||
|
||||
// 视图数据预处理
|
||||
var now = new Date();
|
||||
var thisYear = now.getFullYear();
|
||||
var defaultMonth = thisYear + '-' + String(now.getMonth() + 1).padStart(2, '0');
|
||||
|
||||
if (!state.expenseMonth) state.expenseMonth = defaultMonth;
|
||||
if (state.expenseQuarter === '') state.expenseQuarter = String(Math.floor(now.getMonth() / 3));
|
||||
|
||||
// 月度筛选
|
||||
if (view === 'monthly') {
|
||||
filtered = filtered.filter(function(r) { return r.expense_month === state.expenseMonth; });
|
||||
}
|
||||
|
||||
// 季度筛选
|
||||
if (view === 'quarterly') {
|
||||
var q = parseInt(state.expenseQuarter) || 0;
|
||||
var qRanges = [[1,2,3],[4,5,6],[7,8,9],[10,11,12]];
|
||||
var qRange = qRanges[q];
|
||||
filtered = filtered.filter(function(r) {
|
||||
var m = parseInt((r.expense_month || '0').substring(5)) || 0;
|
||||
return qRange.indexOf(m) >= 0;
|
||||
});
|
||||
}
|
||||
|
||||
var totalAmount = filtered.reduce(function(s, r) { return s + (r.amount || 0); }, 0);
|
||||
var totalIncurred = filtered.reduce(function(s, r) { return s + (r.incurred_amount || 0); }, 0);
|
||||
|
||||
// 月份下拉选项(去年/今年/明年)
|
||||
var monthOpts = '<option value="">请选择</option>';
|
||||
for (var yr = thisYear - 1; yr <= thisYear + 1; yr++)
|
||||
for (var m = 1; m <= 12; m++) {
|
||||
var mv = yr + '-' + String(m).padStart(2, '0');
|
||||
monthOpts += '<option value="' + mv + '"' + (mv === state.expenseMonth ? ' selected' : '') + '>' + mv + '</option>';
|
||||
}
|
||||
|
||||
// 季度下拉
|
||||
var qLabels = ['Q1 (1-3月)','Q2 (4-6月)','Q3 (7-9月)','Q4 (10-12月)'];
|
||||
var quarterOpts = '';
|
||||
for (var qi = 0; qi < 4; qi++) {
|
||||
quarterOpts += '<option value="' + qi + '"' + (String(qi) === state.expenseQuarter ? ' selected' : '') + '>' + qLabels[qi] + '</option>';
|
||||
}
|
||||
|
||||
// 月份/季度日期选择器
|
||||
var dateSelect = '';
|
||||
if (view === 'monthly') {
|
||||
dateSelect = '<span class="text-xs text-slate-500 ml-2">月份:</span><select onchange="setExpenseMonth(this.value)" class="text-xs font-medium py-1 px-2.5 cursor-pointer" style="appearance:none;-webkit-appearance:none;background-color:transparent;border:0;outline:none;background:url('data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%2212%22 height=%2212%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22%236b7280%22 stroke-width=%222%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22%3E%3Cpolyline points=%226 9 12 15 18 9%22%3E%3C/polyline%3E%3C/svg%3E') no-repeat right 4px center;padding-right:22px;min-height:30px">' + monthOpts + '</select>';
|
||||
} else if (view === 'quarterly') {
|
||||
dateSelect = '<span class="text-xs text-slate-500 ml-2">季度:</span><select onchange="setExpenseQuarter(this.value)" class="text-xs font-medium py-1 px-2.5 cursor-pointer" style="appearance:none;-webkit-appearance:none;background-color:transparent;border:0;outline:none;background:url('data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%2212%22 height=%2212%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22%236b7280%22 stroke-width=%222%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22%3E%3Cpolyline points=%226 9 12 15 18 9%22%3E%3C/polyline%3E%3C/svg%3E') no-repeat right 4px center;padding-right:22px;min-height:30px">' + quarterOpts + '</select>';
|
||||
}
|
||||
|
||||
// Toolbar
|
||||
var toolbar = '<span class="text-xs text-slate-500">视图:</span><select onchange="setExpenseView(this.value)" class="text-xs font-medium py-1 px-2.5 cursor-pointer" style="appearance:none;-webkit-appearance:none;background-color:transparent;border:0;outline:none;background:url('data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%2212%22 height=%2212%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22%236b7280%22 stroke-width=%222%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22%3E%3Cpolyline points=%226 9 12 15 18 9%22%3E%3C/polyline%3E%3C/svg%3E') no-repeat right 4px center;padding-right:22px;min-height:30px"><option value="total"' + (view==='total'?' selected':'') + '>总视图</option><option value="quarterly"' + (view==='quarterly'?' selected':'') + '>季度视图</option><option value="monthly"' + (view==='monthly'?' selected':'') + '>月度视图</option></select><span class="text-xs text-slate-500 ml-3">类型:</span><select onchange="setExpenseFilter(this.value)" class="text-xs font-medium py-1 px-2.5 cursor-pointer" style="appearance:none;-webkit-appearance:none;background-color:transparent;border:0;outline:none;background:url('data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%2212%22 height=%2212%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22%236b7280%22 stroke-width=%222%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22%3E%3Cpolyline points=%226 9 12 15 18 9%22%3E%3C/polyline%3E%3C/svg%3E') no-repeat right 4px center;padding-right:22px;min-height:30px"><option value="全部"' + (typeFilter==='全部'?' selected':'') + '>全部 (' + records.length + ')</option>';
|
||||
EXPENSE_TYPES.forEach(function(t) {
|
||||
toolbar += '<option value="' + t + '"' + (typeFilter===t?' selected':'') + '>' + t + ' (' + records.filter(function(r){return r.expense_type===t}).length + ')</option>';
|
||||
});
|
||||
toolbar += '</select>' + dateSelect;
|
||||
|
||||
// 表格行
|
||||
var tableRows = '';
|
||||
if (filtered.length) {
|
||||
filtered.forEach(function(r) {
|
||||
tableRows += '<tr class="border-b border-slate-100 hover:bg-slate-50 cursor-pointer" onclick="openExpenseEdit(' + r.id + ')"><td class="p-2 font-medium text-center align-middle" style="width:200px">' + esc(r.expense_type) + '</td><td class="p-2 text-center align-middle" style="width:200px">' + (r.expense_month || '—') + '</td><td class="p-2 text-center align-middle font-medium" style="width:200px">' + money(r.amount) + '</td><td class="p-2 text-center align-middle font-medium" style="width:200px">' + money(r.incurred_amount) + '</td><td class="p-2 text-center align-middle text-slate-500" style="width:200px">' + (esc(r.notes) || '—') + '</td></tr>';
|
||||
});
|
||||
} else {
|
||||
var emptyMsg = view === 'monthly' ? '该月份暂无费用记录' : view === 'quarterly' ? '该季度暂无费用记录' : '暂无费用记录';
|
||||
tableRows = '<tr><td colspan="5" class="p-6 text-center text-slate-400">' + emptyMsg + '</td></tr>';
|
||||
}
|
||||
|
||||
// 模态框
|
||||
var modal = '<div id="expenseModal" class="hidden fixed inset-0 z-50 flex items-center justify-center bg-black/40" onclick="closeExpenseModal()"><div class="bg-white rounded-2xl shadow-2xl w-full max-w-lg mx-4" onclick="event.stopPropagation()"><div class="sticky top-0 z-10 bg-white/95 backdrop-blur border-b border-slate-100 px-6 py-4 flex items-center justify-between"><h3 class="text-lg font-bold text-slate-800" id="expenseModalTitle">新增费用</h3><div class="flex items-center gap-2"><button class="btn btn-ghost btn-sm text-red-600 hidden" id="expenseDeleteBtn" onclick="deleteExpenseItem()">删除</button><button class="btn btn-ghost btn-sm rounded-full w-8 h-8 p-0" onclick="closeExpenseModal()"><i data-lucide="x"></i></button></div></div><form onsubmit="saveExpense(event)" class="p-6 grid gap-4" novalidate><input type="hidden" name="expense_id" value=""><div class="grid grid-cols-2 gap-3"><label class="block"><span class="text-xs text-slate-500 mb-1 block">费用类型 <span class="text-red-500">*</span></span><select name="expense_type" required class="form-ctrl bg-white"><option value="">请选择</option>';
|
||||
EXPENSE_TYPES.forEach(function(t) { modal += '<option>' + t + '</option>'; });
|
||||
modal += '</select></label><label class="block"><span class="text-xs text-slate-500 mb-1 block">月份 <span class="text-red-500">*</span></span><select name="expense_month" required class="form-ctrl bg-white">' + monthOpts + '</select></label></div><div class="grid grid-cols-2 gap-3"><label class="block"><span class="text-xs text-slate-500 mb-1 block">金额(元)</span><input name="amount" type="number" step="0.01" class="form-ctrl" placeholder="0.00"></label><label class="block"><span class="text-xs text-slate-500 mb-1 block">已发生金额(元)</span><input name="incurred_amount" type="number" step="0.01" class="form-ctrl" placeholder="0.00"></label></div><label class="block"><span class="text-xs text-slate-500 mb-1 block">费用说明</span><textarea name="notes" class="form-ctrl" rows="3" placeholder="备注信息"></textarea></label><div class="flex justify-end gap-3 pt-2"><button type="button" class="btn btn-ghost btn-sm px-6" onclick="closeExpenseModal()">取消</button><button type="submit" class="btn btn-primary btn-sm px-8">保存</button></div></form></div></div>';
|
||||
|
||||
document.querySelector("#expense").innerHTML = '<div class="grid gap-4">' +
|
||||
'<div class="card p-3"><div class="flex justify-between items-center"><div class="flex items-center gap-2">' + toolbar + '</div><button class="btn btn-primary btn-sm" onclick="openExpenseModal()">新增费用</button></div></div>' +
|
||||
'<div class="grid grid-cols-3 gap-3">' +
|
||||
'<div class="metric-card p-3"><span class="flex items-center gap-2 text-xs text-slate-500">费用记录</span><strong class="mt-2 block text-xl">' + filtered.length + '</strong></div>' +
|
||||
'<div class="metric-card p-3"><span class="flex items-center gap-2 text-xs text-slate-500">总费用</span><strong class="mt-2 block text-xl">' + money(totalAmount) + '</strong></div>' +
|
||||
'<div class="metric-card p-3"><span class="flex items-center gap-2 text-xs text-slate-500">已发生金额</span><strong class="mt-2 block text-xl">' + money(totalIncurred) + '</strong></div>' +
|
||||
'</div>' +
|
||||
modal +
|
||||
'<div class="card p-4"><div class="overflow-x-auto"><table class="w-full text-sm"><thead><tr class="bg-slate-50 border-b border-slate-200"><th class="p-2 text-center font-semibold" style="width:200px">费用类型</th><th class="p-2 text-center font-semibold" style="width:200px">月份</th><th class="p-2 text-center font-semibold" style="width:200px">金额</th><th class="p-2 text-center font-semibold" style="width:200px">已发生金额</th><th class="p-2 text-center font-semibold" style="width:200px">费用说明</th></tr></thead><tbody>' + tableRows + '</tbody></table></div></div>' +
|
||||
'</div>';
|
||||
|
||||
document.querySelector("#expense").innerHTML = `<div class="grid gap-4">
|
||||
<div class="grid grid-cols-3 gap-3">
|
||||
<div class="metric-card"><span class="flex items-center gap-2 text-xs text-slate-500">费用记录</span><strong class="mt-2 block text-2xl">${records.length}</strong></div>
|
||||
<div class="metric-card"><span class="flex items-center gap-2 text-xs text-slate-500">费用金额</span><strong class="mt-2 block text-2xl">${money(totalAmount)}</strong></div>
|
||||
<div class="metric-card"><span class="flex items-center gap-2 text-xs text-slate-500">待审批</span><strong class="mt-2 block text-2xl">${records.filter(r => r.status === '待审批').length}</strong></div>
|
||||
</div>
|
||||
<div class="flex justify-between items-center">
|
||||
<h3 class="font-bold text-slate-700">费用列表 <span class="text-slate-400 font-normal">(${records.length})</span></h3>
|
||||
<button class="btn btn-primary btn-sm" onclick="openExpenseModal()">新增费用</button>
|
||||
</div>
|
||||
<div id="purchaseModal" class="hidden fixed inset-0 z-50 flex items-center justify-center bg-black/40" onclick="closeExpenseModal()"><div class="bg-white rounded-2xl shadow-2xl w-full max-w-lg mx-4" onclick="event.stopPropagation()"><div class="sticky top-0 z-10 bg-white/95 backdrop-blur border-b border-slate-100 px-6 py-4 flex items-center justify-between"><h3 class="text-lg font-bold text-slate-800" id="purchaseModalTitle">新增费用</h3><button class="btn btn-ghost btn-sm rounded-full w-8 h-8 p-0" onclick="closeExpenseModal()"><i data-lucide="x"></i></button></div><form onsubmit="saveExpense(event)" class="p-6 grid gap-4" novalidate><input type="hidden" name="expense_id" id="purchase-id-input" value=""><div class="grid grid-cols-2 gap-3"><label class="block"><span class="text-xs text-slate-500 mb-1 block">项目名称 <span class="text-red-500">*</span></span><input name="project_name" required class="form-ctrl" placeholder="请输入项目名称"></label><label class="block"><span class="text-xs text-slate-500 mb-1 block">采购内容 <span class="text-red-500">*</span></span><input name="purchase_item" required class="form-ctrl" placeholder="请输入采购内容"></label></div><div class="grid grid-cols-2 gap-3"><label class="block"><span class="text-xs text-slate-500 mb-1 block">供应商</span><input name="supplier" class="form-ctrl" placeholder="请输入供应商"></label><label class="block"><span class="text-xs text-slate-500 mb-1 block">金额(元)</span><input name="amount" type="number" step="0.01" class="form-ctrl" placeholder="0.00"></label></div><div class="grid grid-cols-2 gap-3"><label class="block"><span class="text-xs text-slate-500 mb-1 block">采购日期</span><input name="purchase_date" type="date" class="form-ctrl"></label><label class="block"><span class="text-xs text-slate-500 mb-1 block">状态</span><select name="status" class="form-ctrl bg-white"><option>待审批</option><option>已审批</option><option>已采购</option><option>已完成</option></select></label></div><div class="grid grid-cols-2 gap-3"><label class="block"><span class="text-xs text-slate-500 mb-1 block">分类</span><input name="category" class="form-ctrl" placeholder="如:办公用品、设备等"></label></div><div class="grid"><label class="block"><span class="text-xs text-slate-500 mb-1 block">备注</span><textarea name="notes" class="form-ctrl" rows="2" placeholder="备注信息"></textarea></label></div><div class="flex justify-end gap-2 pt-2 border-t border-slate-100"><button class="btn btn-ghost btn-sm text-red-600 hidden" id="purchaseDeleteBtn" onclick="deleteExpenseItem()" type="button">删除</button><button class="btn btn-ghost btn-sm" onclick="closeExpenseModal()" type="button">取消</button><button class="btn btn-primary btn-sm" type="submit">保存</button></div></form></div></div>
|
||||
<div class="overflow-x-auto card p-4"><table class="w-full text-sm"><thead><tr class="bg-slate-50 border-b border-slate-200"><th class="p-2 text-left font-semibold">项目名称</th><th class="p-2 text-left font-semibold">采购内容</th><th class="p-2 text-left font-semibold">供应商</th><th class="p-2 text-right font-semibold">金额</th><th class="p-2 text-center font-semibold">采购日期</th><th class="p-2 text-center font-semibold">状态</th><th class="p-2 text-left font-semibold">分类</th><th class="p-2 text-left font-semibold">备注</th></tr></thead><tbody>${records.length ? records.map(r => `<tr class="border-b border-slate-100 hover:bg-slate-50 cursor-pointer" onclick="openExpenseEdit(${r.id})"><td class="p-2 font-medium">${esc(r.project_name)}</td><td class="p-2">${esc(r.purchase_item)}</td><td class="p-2">${esc(r.supplier)}</td><td class="p-2 text-right font-medium">${money(r.amount)}</td><td class="p-2 text-center text-xs">${r.purchase_date || '—'}</td><td class="p-2 text-center">${badge(r.status)}</td><td class="p-2">${esc(r.category)}</td><td class="p-2 text-xs text-slate-500">${esc(r.notes) || '—'}</td></tr>`).join("") : '<tr><td colspan="8" class="p-6 text-center text-slate-400">暂无费用记录</td></tr>'}<tr class="border-t-2 border-slate-200 bg-slate-50 font-bold"><td class="p-2" colspan="3">合计(${records.length} 条)</td><td class="p-2 text-right">${money(totalAmount)}</td><td class="p-2" colspan="4"></td></tr></tbody></table></div>
|
||||
</div>`;
|
||||
if (window.lucide) window.lucide.createIcons();
|
||||
}
|
||||
|
||||
window.openExpenseModal = () => {
|
||||
const form = document.querySelector("#expenseModal form");
|
||||
window.setExpenseView = function(v) { state.expenseView = v; renderExpense(); };
|
||||
window.setExpenseFilter = function(v) { state.expenseFilter = v; renderExpense(); };
|
||||
window.setExpenseMonth = function(v) { state.expenseMonth = v; renderExpense(); };
|
||||
window.setExpenseQuarter = function(v) { state.expenseQuarter = v; renderExpense(); };
|
||||
|
||||
window.openExpenseModal = function() {
|
||||
var form = document.querySelector("#expenseModal form");
|
||||
form.reset();
|
||||
form.querySelector('[name="expense_id"]').value = "";
|
||||
document.querySelector("#expenseModalTitle").textContent = "新增费用";
|
||||
@@ -37,49 +112,44 @@ window.openExpenseModal = () => {
|
||||
document.querySelector("#expenseModal").classList.remove("hidden");
|
||||
};
|
||||
|
||||
window.closeExpenseModal = () => {
|
||||
window.closeExpenseModal = function() {
|
||||
document.querySelector("#expenseModal").classList.add("hidden");
|
||||
};
|
||||
|
||||
window.openExpenseEdit = (id) => {
|
||||
const r = (state.data.expense || []).find(x => x.id === id);
|
||||
window.openExpenseEdit = function(id) {
|
||||
var r = (state.data.expense || []).find(function(x) { return x.id === id; });
|
||||
if (!r) return;
|
||||
const form = document.querySelector("#expenseModal form");
|
||||
var form = document.querySelector("#expenseModal form");
|
||||
form.reset();
|
||||
const setVal = (name, val) => { const el = form.querySelector(`[name="${name}"]`); if (el) el.value = val || ""; };
|
||||
var setVal = function(name, val) { var el = form.querySelector('[name="' + name + '"]'); if (el) el.value = val || ""; };
|
||||
setVal("expense_id", r.id);
|
||||
setVal("project_name", r.project_name);
|
||||
setVal("purchase_item", r.purchase_item);
|
||||
setVal("supplier", r.supplier);
|
||||
setVal("expense_type", r.expense_type);
|
||||
setVal("expense_month", r.expense_month);
|
||||
setVal("amount", r.amount);
|
||||
setVal("purchase_date", r.purchase_date);
|
||||
setVal("status", r.status);
|
||||
setVal("category", r.category);
|
||||
setVal("incurred_amount", r.incurred_amount);
|
||||
setVal("notes", r.notes);
|
||||
document.querySelector("#expenseModalTitle").textContent = "编辑费用";
|
||||
document.querySelector("#expenseDeleteBtn").classList.remove("hidden");
|
||||
document.querySelector("#expenseModal").classList.remove("hidden");
|
||||
};
|
||||
|
||||
window.saveExpense = async (event) => {
|
||||
window.saveExpense = async function(event) {
|
||||
event.preventDefault();
|
||||
const form = event.target;
|
||||
const data = Object.fromEntries(new FormData(form).entries());
|
||||
const isEdit = !!data.expense_id;
|
||||
var form = event.target;
|
||||
var data = Object.fromEntries(new FormData(form).entries());
|
||||
var isEdit = !!data.expense_id;
|
||||
delete data.expense_id;
|
||||
|
||||
const url = isEdit
|
||||
? `/api/expense/${form.querySelector('[name="expense_id"]').value}`
|
||||
: "/api/expense";
|
||||
const method = isEdit ? "PUT" : "POST";
|
||||
var id = form.querySelector('[name="expense_id"]').value;
|
||||
var url = isEdit ? "/api/expense/" + id : "/api/expense";
|
||||
var method = isEdit ? "PUT" : "POST";
|
||||
|
||||
const resp = await fetch(url, { method, headers: { "Content-Type": "application/json" }, body: JSON.stringify({ data: { ...data, tenant: state.tenant } }) });
|
||||
var resp = await fetch(url, { method: method, headers: { "Content-Type": "application/json" }, body: JSON.stringify({ data: Object.assign({}, data, { tenant: state.tenant }) }) });
|
||||
if (!resp.ok) { alert("保存失败"); return; }
|
||||
|
||||
// Refresh data
|
||||
const bResp = await fetch(`/api/bootstrap?tenant=${encodeURIComponent(state.tenant)}`);
|
||||
var bResp = await fetch("/api/bootstrap?tenant=" + encodeURIComponent(state.tenant));
|
||||
if (bResp.ok) {
|
||||
const bd = await bResp.json();
|
||||
var bd = await bResp.json();
|
||||
state.data = bd;
|
||||
applyUserTenants();
|
||||
renderExpense();
|
||||
@@ -87,17 +157,16 @@ window.saveExpense = async (event) => {
|
||||
closeExpenseModal();
|
||||
};
|
||||
|
||||
window.deleteExpenseItem = async () => {
|
||||
const id = document.querySelector("#expenseModal form [name='expense_id']").value;
|
||||
window.deleteExpenseItem = async function() {
|
||||
var id = document.querySelector("#expenseModal form [name='expense_id']").value;
|
||||
if (!id || !confirm("确定删除?")) return;
|
||||
const resp = await fetch(`/api/expense/${id}`, { method: "DELETE" });
|
||||
var resp = await fetch("/api/expense/" + id, { method: "DELETE" });
|
||||
if (!resp.ok) { alert("删除失败"); return; }
|
||||
const bResp = await fetch(`/api/bootstrap?tenant=${encodeURIComponent(state.tenant)}`);
|
||||
var bResp = await fetch("/api/bootstrap?tenant=" + encodeURIComponent(state.tenant));
|
||||
if (bResp.ok) {
|
||||
const bd = await bResp.json();
|
||||
var bd = await bResp.json();
|
||||
state.data = bd;
|
||||
applyUserTenants();
|
||||
renderExpense();
|
||||
}
|
||||
closeExpenseModal();
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user