feat: 新增总览工作台(仅管理员)
- 新增总览工作台,聚合学术/科普/科研/医患/MCN数据 - 4个聚合API: 预算/发生月报季报 - 新增预算总览/发生总览侧边栏tab - 月报/季报视图切换 - 总览仅管理员可见 - 版本号 v1.2.0.24
This commit is contained in:
112
static/modules/overview_finance.js
Normal file
112
static/modules/overview_finance.js
Normal file
@@ -0,0 +1,112 @@
|
||||
// overview_finance.js — 发生总览模块(5 个工作台数据聚合)
|
||||
|
||||
function renderOverviewFinance() {
|
||||
const view = state.overviewView || "monthly";
|
||||
const api = view === "quarterly" ? "/api/overview/finance-quarterly" : "/api/overview/finance-monthly";
|
||||
const container = document.querySelector("#overview_finance");
|
||||
if (!container) return;
|
||||
container.innerHTML = '<div class="p-4 text-center text-slate-400">加载中...</div>';
|
||||
|
||||
fetch(api).then(r => r.json()).then(data => {
|
||||
renderOverviewFinanceData(data, view);
|
||||
}).catch(() => {
|
||||
container.innerHTML = '<div class="p-4 text-center text-slate-400">加载失败</div>';
|
||||
});
|
||||
}
|
||||
|
||||
function renderOverviewFinanceData(data, view) {
|
||||
const container = document.querySelector("#overview_finance");
|
||||
if (!container) return;
|
||||
|
||||
const moneyIntL = (v) => Math.round(Number(v || 0)).toLocaleString("zh-CN");
|
||||
const card = (body, cls) => '<div class="card ' + (cls || 'p-4') + '">' + body + '</div>';
|
||||
|
||||
const isQuarterly = view === "quarterly";
|
||||
const headers = isQuarterly ? ['Q1','Q2','Q3','Q4'] : ['1月','2月','3月','4月','5月','6月','7月','8月','9月','10月','11月','12月'];
|
||||
const cols = isQuarterly ? 4 : 12;
|
||||
|
||||
const d = data.dept;
|
||||
const cf = data.dept_cf;
|
||||
const p = data.project;
|
||||
|
||||
// 视图切换按钮
|
||||
var viewBtns = '<div class="flex items-center gap-2 mb-2"><button onclick="setOverviewFinanceView(\'monthly\')" class="px-3 py-0.5 rounded text-sm font-medium ' + (view === 'monthly' ? 'bg-blue-600 text-white' : 'bg-slate-100 text-slate-600 hover:bg-slate-200') + '">月报总览</button><button onclick="setOverviewFinanceView(\'quarterly\')" class="px-3 py-0.5 rounded text-sm font-medium ' + (view === 'quarterly' ? 'bg-blue-600 text-white' : 'bg-slate-100 text-slate-600 hover:bg-slate-200') + '">季报总览</button></div>';
|
||||
|
||||
// ====== 部门损益情况 ======
|
||||
var deptThead = '<tr class="border-b border-slate-200"><th class="py-1.5 text-left text-slate-500 whitespace-nowrap">指标</th><th class="py-1.5 text-right font-semibold text-slate-700 whitespace-nowrap">年度累计</th>';
|
||||
for (var hi = 0; hi < cols; hi++) { deptThead += '<th class="py-1.5 text-right font-semibold text-slate-600 whitespace-nowrap">' + headers[hi] + '</th>'; }
|
||||
deptThead += '</tr>';
|
||||
|
||||
var deptRows = [
|
||||
{ label: '项目毛利', vals: d.project_gross, annual: d.annual[0] },
|
||||
{ label: '部门费用', vals: d.dept_expense, annual: d.annual[1] },
|
||||
{ label: '利润', vals: d.profit, annual: d.annual[2] }
|
||||
];
|
||||
var deptTbody = '';
|
||||
for (var dri = 0; dri < 3; dri++) {
|
||||
var dr = deptRows[dri];
|
||||
var rowCls = dri === 2 ? 'font-semibold' : '';
|
||||
var rowHtml = '<tr class="border-b border-slate-100 last:border-0"><td class="py-1.5 text-left ' + rowCls + ' text-slate-700 whitespace-nowrap">' + dr.label + '</td>';
|
||||
var annualCls = dri === 2 ? (dr.annual >= 0 ? 'text-green-600' : 'text-red-600') : 'text-slate-700';
|
||||
rowHtml += '<td class="py-1.5 text-right ' + rowCls + ' ' + annualCls + ' whitespace-nowrap">' + moneyIntL(dr.annual) + '</td>';
|
||||
for (var i = 0; i < cols; i++) {
|
||||
var cellCls = dri === 2 ? (dr.vals[i] >= 0 ? 'text-green-600' : 'text-red-600') : 'text-slate-700';
|
||||
rowHtml += '<td class="py-1.5 text-right text-sm whitespace-nowrap ' + cellCls + '">' + moneyIntL(dr.vals[i]) + '</td>';
|
||||
}
|
||||
deptTbody += rowHtml + '</tr>';
|
||||
}
|
||||
var deptCard = card('<h3 class="text-base font-bold text-slate-700 mb-3">部门损益情况 <span class="text-sm text-slate-500 font-normal ml-3">部门利润 = 所有项目产生的毛利 - 部门 1 级产生的人力成本,研发成本,差旅和其他支出</span></h3><div class="overflow-x-auto"><table class="w-full text-sm"><thead>' + deptThead + '</thead><tbody>' + deptTbody + '</tbody></table></div>', 'p-4');
|
||||
|
||||
// ====== 部门现金流 ======
|
||||
var cfRows = [
|
||||
{ label: '项目现金流', vals: cf.project_cf, annual: cf.annual[0] },
|
||||
{ label: '部门现金流', vals: cf.dept_cf_item, annual: cf.annual[1] },
|
||||
{ label: '经营现金流', vals: cf.bl_cf, annual: cf.annual[2] }
|
||||
];
|
||||
var cfTbody = '';
|
||||
for (var cfri = 0; cfri < 3; cfri++) {
|
||||
var cfr = cfRows[cfri];
|
||||
var cfRowCls = cfri === 2 ? 'font-semibold' : '';
|
||||
var rowHtml = '<tr class="border-b border-slate-100 last:border-0"><td class="py-1.5 text-left ' + cfRowCls + ' text-slate-700 whitespace-nowrap">' + cfr.label + '</td>';
|
||||
var cAnnualCls = cfri === 2 ? (cfr.annual >= 0 ? 'text-green-600' : 'text-red-600') : 'text-slate-700';
|
||||
rowHtml += '<td class="py-1.5 text-right ' + cfRowCls + ' ' + cAnnualCls + ' whitespace-nowrap">' + moneyIntL(cfr.annual) + '</td>';
|
||||
for (var i = 0; i < cols; i++) {
|
||||
var cCellCls = cfri === 2 ? (cfr.vals[i] >= 0 ? 'text-green-600' : 'text-red-600') : 'text-slate-700';
|
||||
rowHtml += '<td class="py-1.5 text-right text-sm whitespace-nowrap ' + cCellCls + '">' + moneyIntL(cfr.vals[i]) + '</td>';
|
||||
}
|
||||
cfTbody += rowHtml + '</tr>';
|
||||
}
|
||||
var cfCard = card('<h3 class="text-base font-bold text-slate-700 mb-3">部门现金流 <span class="text-sm text-slate-500 font-normal ml-3">部门现金流=所有项目产生的现金流 - 部门一级因费用产生的现金流出或流入</span></h3><div class="overflow-x-auto"><table class="w-full text-sm"><thead>' + deptThead + '</thead><tbody>' + cfTbody + '</tbody></table></div>', 'p-4');
|
||||
|
||||
// ====== 项目经营详情 ======
|
||||
var LABELS = ['合同金额','确收金额','确收毛利','成本','支出','回款','项目现金流'];
|
||||
var projVals = [p.revenue, p.gross, p.cost, p.paid, p.payment, p.cashflow];
|
||||
var projAnnual = p.annual;
|
||||
|
||||
var projThead = '<tr class="border-b border-slate-200"><th class="py-1.5 text-left text-slate-500 whitespace-nowrap">指标</th><th class="py-1.5 text-right font-semibold text-slate-700 whitespace-nowrap">年度累计</th>';
|
||||
for (var phi = 0; phi < cols; phi++) { projThead += '<th class="py-1.5 text-right font-semibold text-slate-600 whitespace-nowrap">' + headers[phi] + '</th>'; }
|
||||
projThead += '</tr>';
|
||||
|
||||
var projTbody = '';
|
||||
for (var ri = 0; ri < 7; ri++) {
|
||||
var rowHtml = '<tr class="border-b border-slate-100 last:border-0"><td class="py-1.5 text-left font-semibold text-slate-700 whitespace-nowrap">' + LABELS[ri] + '</td>';
|
||||
rowHtml += '<td class="py-1.5 text-right font-semibold text-slate-800 whitespace-nowrap">' + (ri === 0 ? moneyIntL(p.sign) : moneyIntL(projAnnual[ri])) + '</td>';
|
||||
for (var i = 0; i < cols; i++) {
|
||||
var val = ri === 0 ? 0 : (projVals[ri - 1] ? projVals[ri - 1][i] : 0);
|
||||
var cls = 'text-slate-700';
|
||||
if (ri === 2) cls = 'text-green-600';
|
||||
if (ri === 6) cls = val >= 0 ? 'text-green-600' : 'text-red-600';
|
||||
rowHtml += '<td class="py-1.5 text-right text-sm whitespace-nowrap ' + cls + '">' + moneyIntL(val) + '</td>';
|
||||
}
|
||||
projTbody += rowHtml + '</tr>';
|
||||
}
|
||||
var projCard = card('<h3 class="text-base font-bold text-slate-700 mb-3">项目经营详情 <span class="text-sm text-slate-500 font-normal ml-3">所有项目产生的签单,确收,毛利,回款,当期成本,当期流出,现金流</span></h3><div class="overflow-x-auto"><table class="w-full text-sm"><thead>' + projThead + '</thead><tbody>' + projTbody + '</tbody></table></div>', 'p-4');
|
||||
|
||||
container.innerHTML = '<div class="grid gap-2.5">' + viewBtns + deptCard + cfCard + projCard + '</div>';
|
||||
if (window.lucide) window.lucide.createIcons();
|
||||
}
|
||||
|
||||
window.setOverviewFinanceView = (v) => {
|
||||
state.overviewView = v;
|
||||
renderOverviewFinance();
|
||||
};
|
||||
112
static/modules/overview_plan.js
Normal file
112
static/modules/overview_plan.js
Normal file
@@ -0,0 +1,112 @@
|
||||
// overview_plan.js — 预算总览模块(5 个工作台数据聚合)
|
||||
|
||||
function renderOverviewPlan() {
|
||||
const view = state.overviewView || "monthly";
|
||||
const api = view === "quarterly" ? "/api/overview/plan-quarterly" : "/api/overview/plan-monthly";
|
||||
const container = document.querySelector("#overview_plan");
|
||||
if (!container) return;
|
||||
container.innerHTML = '<div class="p-4 text-center text-slate-400">加载中...</div>';
|
||||
|
||||
fetch(api).then(r => r.json()).then(data => {
|
||||
renderOverviewPlanData(data, view);
|
||||
}).catch(() => {
|
||||
container.innerHTML = '<div class="p-4 text-center text-slate-400">加载失败</div>';
|
||||
});
|
||||
}
|
||||
|
||||
function renderOverviewPlanData(data, view) {
|
||||
const container = document.querySelector("#overview_plan");
|
||||
if (!container) return;
|
||||
|
||||
const moneyIntL = (v) => Math.round(Number(v || 0)).toLocaleString("zh-CN");
|
||||
const card = (body, cls) => '<div class="card ' + (cls || 'p-4') + '">' + body + '</div>';
|
||||
|
||||
const isQuarterly = view === "quarterly";
|
||||
const headers = isQuarterly ? ['Q1','Q2','Q3','Q4'] : ['1月','2月','3月','4月','5月','6月','7月','8月','9月','10月','11月','12月'];
|
||||
const cols = isQuarterly ? 4 : 12;
|
||||
|
||||
const d = data.dept;
|
||||
const cf = data.dept_cf;
|
||||
const p = data.project;
|
||||
|
||||
// ====== 部门损益情况 ======
|
||||
var deptThead = '<tr class="border-b border-slate-200"><th class="py-1.5 text-left text-slate-500 whitespace-nowrap">指标</th><th class="py-1.5 text-right font-semibold text-slate-700 whitespace-nowrap">年度累计</th>';
|
||||
for (var hi = 0; hi < cols; hi++) { deptThead += '<th class="py-1.5 text-right font-semibold text-slate-600 whitespace-nowrap">' + headers[hi] + '</th>'; }
|
||||
deptThead += '</tr>';
|
||||
|
||||
var deptRows = [
|
||||
{ label: '项目毛利', vals: d.project_gross, annual: d.annual[0] },
|
||||
{ label: '部门费用', vals: d.dept_expense, annual: d.annual[1] },
|
||||
{ label: '利润', vals: d.profit, annual: d.annual[2] }
|
||||
];
|
||||
var deptTbody = '';
|
||||
for (var dri = 0; dri < 3; dri++) {
|
||||
var dr = deptRows[dri];
|
||||
var rowCls = dri === 2 ? 'font-semibold' : '';
|
||||
var rowHtml = '<tr class="border-b border-slate-100 last:border-0"><td class="py-1.5 text-left ' + rowCls + ' text-slate-700 whitespace-nowrap">' + dr.label + '</td>';
|
||||
var annualCls = dri === 2 ? (dr.annual >= 0 ? 'text-green-600' : 'text-red-600') : 'text-slate-700';
|
||||
rowHtml += '<td class="py-1.5 text-right ' + rowCls + ' ' + annualCls + ' whitespace-nowrap">' + moneyIntL(dr.annual) + '</td>';
|
||||
for (var i = 0; i < cols; i++) {
|
||||
var cellCls = dri === 2 ? (dr.vals[i] >= 0 ? 'text-green-600' : 'text-red-600') : 'text-slate-700';
|
||||
rowHtml += '<td class="py-1.5 text-right text-sm whitespace-nowrap ' + cellCls + '">' + moneyIntL(dr.vals[i]) + '</td>';
|
||||
}
|
||||
deptTbody += rowHtml + '</tr>';
|
||||
}
|
||||
var deptCard = card('<h3 class="text-base font-bold text-slate-700 mb-3">部门损益情况 <span class="text-sm text-slate-500 font-normal ml-3">部门利润 = 所有项目产生的毛利 - 部门 1 级产生的人力成本,研发成本,差旅和其他支出</span></h3><div class="overflow-x-auto"><table class="w-full text-sm"><thead>' + deptThead + '</thead><tbody>' + deptTbody + '</tbody></table></div>', 'p-4');
|
||||
|
||||
// ====== 部门现金流 ======
|
||||
var cfRows = [
|
||||
{ label: '项目现金流', vals: cf.project_cf, annual: cf.annual[0] },
|
||||
{ label: '部门现金流', vals: cf.dept_cf_item, annual: cf.annual[1] },
|
||||
{ label: '经营现金流', vals: cf.bl_cf, annual: cf.annual[2] }
|
||||
];
|
||||
var cfTbody = '';
|
||||
for (var cfri = 0; cfri < 3; cfri++) {
|
||||
var cfr = cfRows[cfri];
|
||||
var cfRowCls = cfri === 2 ? 'font-semibold' : '';
|
||||
var rowHtml = '<tr class="border-b border-slate-100 last:border-0"><td class="py-1.5 text-left ' + cfRowCls + ' text-slate-700 whitespace-nowrap">' + cfr.label + '</td>';
|
||||
var cAnnualCls = cfri === 2 ? (cfr.annual >= 0 ? 'text-green-600' : 'text-red-600') : 'text-slate-700';
|
||||
rowHtml += '<td class="py-1.5 text-right ' + cfRowCls + ' ' + cAnnualCls + ' whitespace-nowrap">' + moneyIntL(cfr.annual) + '</td>';
|
||||
for (var i = 0; i < cols; i++) {
|
||||
var cCellCls = cfri === 2 ? (cfr.vals[i] >= 0 ? 'text-green-600' : 'text-red-600') : 'text-slate-700';
|
||||
rowHtml += '<td class="py-1.5 text-right text-sm whitespace-nowrap ' + cCellCls + '">' + moneyIntL(cfr.vals[i]) + '</td>';
|
||||
}
|
||||
cfTbody += rowHtml + '</tr>';
|
||||
}
|
||||
var cfCard = card('<h3 class="text-base font-bold text-slate-700 mb-3">部门现金流 <span class="text-sm text-slate-500 font-normal ml-3">部门现金流=所有项目产生的现金流 - 部门一级因费用产生的现金流出或流入</span></h3><div class="overflow-x-auto"><table class="w-full text-sm"><thead>' + deptThead + '</thead><tbody>' + cfTbody + '</tbody></table></div>', 'p-4');
|
||||
|
||||
// ====== 项目经营详情 ======
|
||||
var LABELS = ['合同金额','确收金额','确收毛利','成本','支出','回款','项目现金流'];
|
||||
var projVals = [p.revenue, p.gross, p.cost, p.paid, p.payment, p.cashflow];
|
||||
var projAnnual = p.annual; // [sign, revenue_sum, gross_sum, cost_sum, paid_sum, payment_sum, cf_sum]
|
||||
|
||||
var projThead = '<tr class="border-b border-slate-200"><th class="py-1.5 text-left text-slate-500 whitespace-nowrap">指标</th><th class="py-1.5 text-right font-semibold text-slate-700 whitespace-nowrap">年度累计</th>';
|
||||
for (var phi = 0; phi < cols; phi++) { projThead += '<th class="py-1.5 text-right font-semibold text-slate-600 whitespace-nowrap">' + headers[phi] + '</th>'; }
|
||||
projThead += '</tr>';
|
||||
|
||||
var projTbody = '';
|
||||
for (var ri = 0; ri < 7; ri++) {
|
||||
var rowHtml = '<tr class="border-b border-slate-100 last:border-0"><td class="py-1.5 text-left font-semibold text-slate-700 whitespace-nowrap">' + LABELS[ri] + '</td>';
|
||||
rowHtml += '<td class="py-1.5 text-right font-semibold text-slate-800 whitespace-nowrap">' + (ri === 0 ? moneyIntL(p.sign) : moneyIntL(projAnnual[ri])) + '</td>';
|
||||
for (var i = 0; i < cols; i++) {
|
||||
var val = ri === 0 ? 0 : (projVals[ri - 1] ? projVals[ri - 1][i] : 0);
|
||||
var cls = 'text-slate-700';
|
||||
if (ri === 2) cls = 'text-green-600';
|
||||
if (ri === 6) cls = val >= 0 ? 'text-green-600' : 'text-red-600';
|
||||
rowHtml += '<td class="py-1.5 text-right text-sm whitespace-nowrap ' + cls + '">' + moneyIntL(val) + '</td>';
|
||||
}
|
||||
projTbody += rowHtml + '</tr>';
|
||||
}
|
||||
var projCard = card('<h3 class="text-base font-bold text-slate-700 mb-3">项目经营详情 <span class="text-sm text-slate-500 font-normal ml-3">所有项目产生的签单,确收,毛利,回款,当期成本,当期流出,现金流</span></h3><div class="overflow-x-auto"><table class="w-full text-sm"><thead>' + projThead + '</thead><tbody>' + projTbody + '</tbody></table></div>', 'p-4');
|
||||
|
||||
// 视图切换按钮
|
||||
var viewBtns = '<div class="flex items-center gap-2 mb-2"><button onclick="setOverviewView(\'monthly\')" class="px-3 py-0.5 rounded text-sm font-medium ' + (view === 'monthly' ? 'bg-blue-600 text-white' : 'bg-slate-100 text-slate-600 hover:bg-slate-200') + '">月报总览</button><button onclick="setOverviewView(\'quarterly\')" class="px-3 py-0.5 rounded text-sm font-medium ' + (view === 'quarterly' ? 'bg-blue-600 text-white' : 'bg-slate-100 text-slate-600 hover:bg-slate-200') + '">季报总览</button></div>';
|
||||
|
||||
container.innerHTML = '<div class="grid gap-2.5">' + viewBtns + deptCard + cfCard + projCard + '</div>';
|
||||
if (window.lucide) window.lucide.createIcons();
|
||||
}
|
||||
|
||||
window.setOverviewView = (v) => {
|
||||
state.overviewView = v;
|
||||
renderOverviewPlan();
|
||||
};
|
||||
@@ -32,7 +32,7 @@ window.toggleTenantMenu = (event) => {
|
||||
if (menu) { menu.remove(); return; }
|
||||
const btn = event.currentTarget;
|
||||
const rect = btn.getBoundingClientRect();
|
||||
const tenants = (state.allowedTenants || []).slice().sort(function(a, b) { var order = ['学术·无界','MCN·无界','科普·无界','医患·无界','科研·无界','测试·无界']; var ai = order.indexOf(a); var bi = order.indexOf(b); if (ai < 0) ai = 99; if (bi < 0) bi = 99; return ai - bi; });
|
||||
const tenants = (state.allowedTenants || []).slice().sort(function(a, b) { var order = ['总览','学术·无界','MCN·无界','科普·无界','医患·无界','科研·无界','测试·无界']; var ai = order.indexOf(a); var bi = order.indexOf(b); if (ai < 0) ai = 99; if (bi < 0) bi = 99; return ai - bi; });
|
||||
menu = document.createElement("div");
|
||||
menu.id = "tenantMenu";
|
||||
menu.className = "fixed bg-white rounded-lg shadow-xl border border-slate-200 py-1 min-w-[160px] z-[9999]";
|
||||
@@ -63,7 +63,7 @@ window.switchTenantFromMenu = (tenant) => {
|
||||
};
|
||||
|
||||
function updateTenantLabel() {
|
||||
const tenants = (state.allowedTenants || []).slice().sort(function(a, b) { var order = ['学术·无界','MCN·无界','科普·无界','医患·无界','科研·无界','测试·无界']; var ai = order.indexOf(a); var bi = order.indexOf(b); if (ai < 0) ai = 99; if (bi < 0) bi = 99; return ai - bi; });
|
||||
const tenants = (state.allowedTenants || []).slice().sort(function(a, b) { var order = ['总览','学术·无界','MCN·无界','科普·无界','医患·无界','科研·无界','测试·无界']; var ai = order.indexOf(a); var bi = order.indexOf(b); if (ai < 0) ai = 99; if (bi < 0) bi = 99; return ai - bi; });
|
||||
var tenantIcons = {
|
||||
'学术·无界': 'graduation-cap',
|
||||
'MCN·无界': 'video',
|
||||
@@ -92,7 +92,7 @@ window.toggleTenantMenu = (event) => {
|
||||
if (menu) { menu.remove(); return; }
|
||||
const btn = event.currentTarget;
|
||||
const rect = btn.getBoundingClientRect();
|
||||
const tenants = (state.allowedTenants || []).slice().sort(function(a, b) { var order = ['学术·无界','MCN·无界','科普·无界','医患·无界','科研·无界','测试·无界']; var ai = order.indexOf(a); var bi = order.indexOf(b); if (ai < 0) ai = 99; if (bi < 0) bi = 99; return ai - bi; });
|
||||
const tenants = (state.allowedTenants || []).slice().sort(function(a, b) { var order = ['总览','学术·无界','MCN·无界','科普·无界','医患·无界','科研·无界','测试·无界']; var ai = order.indexOf(a); var bi = order.indexOf(b); if (ai < 0) ai = 99; if (bi < 0) bi = 99; return ai - bi; });
|
||||
var tenantIcons = {
|
||||
'学术·无界': 'graduation-cap',
|
||||
'MCN·无界': 'video',
|
||||
|
||||
@@ -17,6 +17,8 @@ const state = {
|
||||
planStatusFilter: "all",
|
||||
planView: "overview",
|
||||
planSort: null,
|
||||
overviewView: "monthly", // 总览视图: monthly | quarterly
|
||||
overviewFilter: "plan", // 总览筛选: plan | finance
|
||||
proposalTab: "standard",
|
||||
chart: null,
|
||||
chart2: null,
|
||||
@@ -120,6 +122,8 @@ function renderTable(headers, rows, rowClicks) {
|
||||
|
||||
var _loadingSteps = [
|
||||
{ fn: 'renderHome', label: '正在加载首页仪表盘(指标卡片+图表)', skip: function() { return state.tenant !== '总览'; } },
|
||||
{ fn: 'renderOverviewPlan', label: '正在加载预算总览', skip: function() { return state.tenant !== '总览'; } },
|
||||
{ fn: 'renderOverviewFinance', label: '正在加载发生总览', skip: function() { return state.tenant !== '总览'; } },
|
||||
{ fn: 'renderProjects', label: '正在加载业务机会列表' },
|
||||
{ fn: 'renderProposals', label: '正在加载业务方案列表' },
|
||||
{ fn: 'renderProducts', label: '正在加载产品迭代列表' },
|
||||
@@ -232,7 +236,7 @@ var _tabLoaded = {};
|
||||
var _proposalsInitialized = false;
|
||||
|
||||
async function ensureTabData(tab) {
|
||||
if (tab === 'home' || tab === 'performance') return;
|
||||
if (tab === 'home' || tab === 'performance' || tab === 'overview_plan' || tab === 'overview_finance') return;
|
||||
var resource = _tabResourceMap[tab];
|
||||
if (!resource) return;
|
||||
var dataKey = resource;
|
||||
@@ -262,7 +266,7 @@ function switchTab(tab) {
|
||||
document.querySelectorAll(".panel").forEach((panel) => panel.classList.toggle("active", panel.id === tab));
|
||||
|
||||
// 更新顶部标题
|
||||
const titles = { home: 'OPC 工作台', finance: '执行管理', plan: '预算管理', projects: '重点工作台账', proposals: '业务方案', products: '版本与运营', performance: '季度绩效考核' };
|
||||
const titles = { home: 'OPC 工作台', finance: '执行管理', plan: '预算管理', projects: '重点工作台账', proposals: '业务方案', products: '版本与运营', performance: '季度绩效考核', overview_plan: '预算总览', overview_finance: '发生总览' };
|
||||
const titleEl = document.querySelector('#workspaceTitle');
|
||||
if (titleEl) titleEl.textContent = titles[tab] || state.tenant + ' OPC 工作台';
|
||||
|
||||
@@ -278,9 +282,9 @@ function updateSidebarTabs() {
|
||||
document.querySelectorAll(".sidebar-tab").forEach((btn) => {
|
||||
const tab = btn.dataset.tab;
|
||||
if (isOverview) {
|
||||
btn.style.display = (tab === "home" || tab === "plan" || tab === "finance") ? "" : "none";
|
||||
btn.style.display = (tab === "home" || tab === "overview_plan" || tab === "overview_finance") ? "" : "none";
|
||||
} else {
|
||||
btn.style.display = tab === "home" ? "none" : "";
|
||||
btn.style.display = (tab === "overview_plan" || tab === "overview_finance" || tab === "home") ? "none" : "";
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -301,6 +305,8 @@ function renderActive() {
|
||||
if (!state.data) return;
|
||||
const tab = state.active;
|
||||
if (tab === "home") renderHome();
|
||||
else if (tab === "overview_plan") renderOverviewPlan();
|
||||
else if (tab === "overview_finance") renderOverviewFinance();
|
||||
else if (tab === "projects") renderProjects();
|
||||
else if (tab === "proposals") renderProposals();
|
||||
else if (tab === "products") renderProducts();
|
||||
@@ -388,12 +394,22 @@ window.switchTenant = async (tenant) => {
|
||||
state.tenant = tenant;
|
||||
state.selectedProject = null;
|
||||
localStorage.setItem("opc-active-tenant", tenant);
|
||||
document.querySelector("#workspaceTitle").textContent = tenant.replace("·无界", "") + " OPC 工作台";
|
||||
document.querySelector("#workspaceTitle").textContent = tenant === "总览" ? "总览 OPC 工作台" : tenant.replace("·无界", "") + " OPC 工作台";
|
||||
if (typeof updateTenantLabel === "function") updateTenantLabel();
|
||||
state.planFilter = 'overview';
|
||||
state.finFilter = 'overview';
|
||||
updateSidebarTabs();
|
||||
if (state.active === 'home') { state.active = 'plan'; localStorage.setItem("opc-active-tab", "plan"); document.querySelectorAll(".sidebar-tab").forEach((btn) => btn.classList.toggle("active", btn.dataset.tab === "plan")); document.querySelectorAll(".panel").forEach((panel) => panel.classList.toggle("active", panel.id === "plan")); }
|
||||
if (tenant === "总览") {
|
||||
state.active = "overview_plan";
|
||||
localStorage.setItem("opc-active-tab", "overview_plan");
|
||||
document.querySelectorAll(".sidebar-tab").forEach((btn) => btn.classList.toggle("active", btn.dataset.tab === "overview_plan"));
|
||||
document.querySelectorAll(".panel").forEach((panel) => panel.classList.toggle("active", panel.id === "overview_plan"));
|
||||
} else if (state.active === 'home' || state.active === 'overview_plan' || state.active === 'overview_finance') {
|
||||
state.active = 'plan';
|
||||
localStorage.setItem("opc-active-tab", "plan");
|
||||
document.querySelectorAll(".sidebar-tab").forEach((btn) => btn.classList.toggle("active", btn.dataset.tab === "plan"));
|
||||
document.querySelectorAll(".panel").forEach((panel) => panel.classList.toggle("active", panel.id === "plan"));
|
||||
}
|
||||
await load();
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user