// expense.js — 费用管理模块
function renderExpense() {
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, '"'); };
var EXPENSE_TYPES = ['通用费用','平台采购','人力成本','研发费用','奖金','其他'];
// 筛选状态
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 = '';
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 += '';
}
// 季度下拉
var qLabels = ['Q1 (1-3月)','Q2 (4-6月)','Q3 (7-9月)','Q4 (10-12月)'];
var quarterOpts = '';
for (var qi = 0; qi < 4; qi++) {
quarterOpts += '';
}
// 月份/季度日期选择器
var dateSelect = '';
if (view === 'monthly') {
dateSelect = '月份:';
} else if (view === 'quarterly') {
dateSelect = '季度:';
}
// Toolbar
var toolbar = '视图:类型:' + dateSelect;
// 表格行
var tableRows = '';
if (filtered.length) {
filtered.forEach(function(r) {
tableRows += '
| ' + esc(r.expense_type) + ' | ' + (r.expense_month || '—') + ' | ' + money(r.amount) + ' | ' + money(r.incurred_amount) + ' | ' + (esc(r.notes) || '—') + ' |
';
});
} else {
var emptyMsg = view === 'monthly' ? '该月份暂无费用记录' : view === 'quarterly' ? '该季度暂无费用记录' : '暂无费用记录';
tableRows = '| ' + emptyMsg + ' |
';
}
// 模态框
var modal = '';
document.querySelector("#expense").innerHTML = '' +
'
' +
'
' +
'
费用记录' + filtered.length + '
' +
'
总费用' + money(totalAmount) + '
' +
'
已发生金额' + money(totalIncurred) + '
' +
'
' +
modal +
'
| 费用类型 | 月份 | 金额 | 已发生金额 | 费用说明 |
' + tableRows + '
' +
'
';
if (window.lucide) window.lucide.createIcons();
}
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 = "新增费用";
document.querySelector("#expenseDeleteBtn").classList.add("hidden");
document.querySelector("#expenseModal").classList.remove("hidden");
};
window.closeExpenseModal = function() {
document.querySelector("#expenseModal").classList.add("hidden");
};
window.openExpenseEdit = function(id) {
var r = (state.data.expense || []).find(function(x) { return x.id === id; });
if (!r) return;
var form = document.querySelector("#expenseModal form");
form.reset();
var setVal = function(name, val) { var el = form.querySelector('[name="' + name + '"]'); if (el) el.value = val || ""; };
setVal("expense_id", r.id);
setVal("expense_type", r.expense_type);
setVal("expense_month", r.expense_month);
setVal("amount", r.amount);
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 function(event) {
event.preventDefault();
var form = event.target;
var data = Object.fromEntries(new FormData(form).entries());
var isEdit = !!data.expense_id;
delete data.expense_id;
var id = form.querySelector('[name="expense_id"]').value;
var url = isEdit ? "/api/expense/" + id : "/api/expense";
var method = isEdit ? "PUT" : "POST";
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; }
var bResp = await fetch("/api/bootstrap?tenant=" + encodeURIComponent(state.tenant));
if (bResp.ok) {
var bd = await bResp.json();
state.data = bd;
applyUserTenants();
renderExpense();
}
closeExpenseModal();
};
window.deleteExpenseItem = async function() {
var id = document.querySelector("#expenseModal form [name='expense_id']").value;
if (!id || !confirm("确定删除?")) return;
var resp = await fetch("/api/expense/" + id, { method: "DELETE" });
if (!resp.ok) { alert("删除失败"); return; }
var bResp = await fetch("/api/bootstrap?tenant=" + encodeURIComponent(state.tenant));
if (bResp.ok) {
var bd = await bResp.json();
state.data = bd;
applyUserTenants();
renderExpense();
}
};