feat: 新增总览工作台(仅管理员)

- 新增总览工作台,聚合学术/科普/科研/医患/MCN数据
- 4个聚合API: 预算/发生月报季报
- 新增预算总览/发生总览侧边栏tab
- 月报/季报视图切换
- 总览仅管理员可见
- 版本号 v1.2.0.24
This commit is contained in:
mac
2026-07-11 12:26:29 +08:00
parent 75227c51c3
commit 536b165a9b
6 changed files with 438 additions and 14 deletions

View File

@@ -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();
};