feat: 计划模块 + 待签约迁移 + 表格排序 + 已签约→项目

- 新增计划模块(plan_finances/plan_expense_records, 侧边栏计划tab)
- 待签约项目自动迁移到计划模块(data_fixes 幂等)
- 已签约tab改名为项目(业务+计划两个模块)
- 业务模块表格点击列头排序(月度/季度/总视图)
- 计划与业务侧边栏位置对调
- plan模块CSS修复(使用finance-tab类)
- 版本号 v1.0.0.17
This commit is contained in:
mac
2026-07-08 12:13:06 +08:00
parent e233c294b0
commit d2ea5d0d5d
9 changed files with 1442 additions and 44 deletions

View File

@@ -10,6 +10,10 @@ const state = {
taskQuery: "",
taskView: localStorage.getItem("opc-task-view") || "detail",
finView: "overview",
finSort: null, // { key: 'col', asc: true }
planFilter: "已签约",
planView: "overview",
planSort: null,
proposalTab: "standard",
chart: null,
chart2: null,
@@ -132,7 +136,7 @@ function switchTab(tab) {
document.querySelectorAll(".panel").forEach((panel) => panel.classList.toggle("active", panel.id === tab));
// 更新顶部标题
const titles = { home: 'OPC 工作台', finance: '业务管理', projects: '重点工作台账', proposals: '业务方案', products: '版本与运营', performance: '季度绩效考核' };
const titles = { home: 'OPC 工作台', finance: '业务管理', plan: '计划管理', projects: '重点工作台账', proposals: '业务方案', products: '版本与运营', performance: '季度绩效考核' };
const titleEl = document.querySelector('#workspaceTitle');
if (titleEl) titleEl.textContent = titles[tab] || state.tenant + ' OPC 工作台';
@@ -159,6 +163,7 @@ function render() {
renderProducts();
renderPerformance();
renderFinance();
renderPlan();
if (window.lucide) window.lucide.createIcons();
}
@@ -171,6 +176,7 @@ function renderActive() {
else if (tab === "products") renderProducts();
else if (tab === "performance") renderPerformance();
else if (tab === "finance") renderFinance();
else if (tab === "plan") renderPlan();
if (window.lucide) window.lucide.createIcons();
}
@@ -200,8 +206,31 @@ window.setFinView = (view) => {
};
window.switchFinFilter = (filter) => {
state.finFilter = filter;
state.finSort = null;
renderFinance();
};
window.setFinSort = (key) => {
if (!state.finSort) state.finSort = { key: null, asc: true };
if (state.finSort.key === key) { state.finSort.asc = !state.finSort.asc; }
else { state.finSort.key = key; state.finSort.asc = true; }
renderFinance();
};
window.setPlanView = (view) => {
state.planView = view;
renderPlan();
};
window.switchPlanFilter = (filter) => {
state.planFilter = filter;
state.planSort = null;
renderPlan();
};
window.setPlanSort = (key) => {
if (!state.planSort) state.planSort = { key: null, asc: true };
if (state.planSort.key === key) { state.planSort.asc = !state.planSort.asc; }
else { state.planSort.key = key; state.planSort.asc = true; }
renderPlan();
};
window.switchTenant = (tenant) => {
state.tenant = tenant;
state.selectedProject = null;