diff --git a/static/modules/projects.js b/static/modules/projects.js
index e3ad0b4..36a401c 100644
--- a/static/modules/projects.js
+++ b/static/modules/projects.js
@@ -67,19 +67,58 @@ function updateTenantLabel() {
};
const container = document.querySelector("#tenantList");
if (container) {
- container.innerHTML = tenants.map(t => {
- const shortName = t.replace("·无界", "");
- const isActive = t === state.tenant;
- var icon = tenantIcons[t] || 'circle';
- return '
' +
- '' +
- '' + esc(shortName) + '' +
- '
';
- }).join('');
+ var currentShort = (state.tenant || '').replace("·无界", "");
+ var currentIcon = tenantIcons[state.tenant] || 'circle';
+ container.innerHTML = '' +
+ '' +
+ '' + esc(currentShort) + '' +
+ '' +
+ '
';
if (window.lucide) window.lucide.createIcons();
}
}
+window.toggleTenantMenu = (event) => {
+ event.stopPropagation();
+ let menu = document.getElementById("tenantMenu");
+ 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; });
+ var tenantIcons = {
+ '学术·无界': 'graduation-cap',
+ 'MCN·无界': 'video',
+ '科普·无界': 'flask-conical',
+ '医患·无界': 'stethoscope',
+ '科研·无界': 'microscope',
+ '总览': 'layout-dashboard'
+ };
+ menu = document.createElement("div");
+ menu.id = "tenantMenu";
+ menu.className = "fixed bg-white rounded-lg shadow-xl border border-slate-200 py-1 min-w-[180px] z-[9999]";
+ menu.style.left = Math.min(rect.left - 8, window.innerWidth - 200) + "px";
+ menu.style.top = rect.bottom + 6 + "px";
+ menu.innerHTML = '' +
+ tenants.map(t => {
+ const shortName = t.replace("·无界", "");
+ const isActive = t === state.tenant;
+ var icon = tenantIcons[t] || 'circle';
+ return '';
+ }).join('');
+ document.body.appendChild(menu);
+ if (window.lucide) window.lucide.createIcons();
+ setTimeout(() => {
+ document.addEventListener("click", function closeMenu() {
+ menu.remove();
+ document.removeEventListener("click", closeMenu);
+ }, { once: true });
+ }, 10);
+};
+
window.toggleUserMenu = (user) => {
let menu = document.getElementById("userMenu");
if (menu) { menu.remove(); return; }
diff --git a/static/modules/utils.js b/static/modules/utils.js
index b5c8267..6f462d2 100644
--- a/static/modules/utils.js
+++ b/static/modules/utils.js
@@ -115,18 +115,81 @@ function renderTable(headers, rows, rowClicks) {
`);
}
+var _loadingSteps = [
+ { fn: 'renderHome', label: '正在加载首页仪表盘(指标卡片+图表)' },
+ { fn: 'renderProjects', label: '正在加载业务机会列表' },
+ { fn: 'renderProposals', label: '正在加载业务方案列表' },
+ { fn: 'renderProducts', label: '正在加载产品迭代列表' },
+ { fn: 'renderPerformance', label: '正在加载绩效管理' },
+ { fn: 'renderFinance', label: '正在加载财务模块(表格+总览+图表)' },
+ { fn: 'renderPlan', label: '正在加载计划预算' },
+];
+
+function showLoadingOverlay(steps) {
+ var existing = document.getElementById('_loadingOverlay');
+ if (existing) existing.remove();
+ var stepsHtml = steps.map(function(s, i) {
+ return '' +
+ '
' + (i === 0 ? '' : '') + '' +
+ '
' + s.label + '' +
+ '
';
+ }).join('');
+ var overlay = document.createElement('div');
+ overlay.id = '_loadingOverlay';
+ overlay.className = 'fixed inset-0 z-[99999] flex items-center justify-center bg-black/50';
+ overlay.innerHTML = '' +
+ '
正在加载工作台
' +
+ '
共 ' + steps.length + ' 个模块,请稍候…
' +
+ '
' + stepsHtml + '
' +
+ '
' +
+ '
';
+ document.body.appendChild(overlay);
+}
+
+function updateLoadingStep(idx, total) {
+ var stepEl = document.getElementById('_loadingStep_' + idx);
+ if (stepEl) {
+ stepEl.className = 'flex items-center gap-3 py-1.5 text-sm text-green-600 font-medium';
+ stepEl.querySelector('span:first-child').innerHTML = '';
+ }
+ var nextEl = document.getElementById('_loadingStep_' + (idx + 1));
+ if (nextEl) {
+ nextEl.className = 'flex items-center gap-3 py-1.5 text-sm text-blue-600 font-medium';
+ nextEl.querySelector('span:first-child').innerHTML = '';
+ }
+ var progress = document.getElementById('_loadingProgress');
+ if (progress) progress.style.width = Math.round((idx + 1) / total * 100) + '%';
+}
+
+function hideLoadingOverlay() {
+ var overlay = document.getElementById('_loadingOverlay');
+ if (overlay) overlay.remove();
+}
+
async function load() {
+ showLoadingOverlay(_loadingSteps);
state.data = await api(`/api/bootstrap?tenant=${encodeURIComponent(state.tenant)}`);
// 首次加载时确保标准资料库 7 项已初始化
if (typeof ensureStandardProposals === "function") {
const existing = (state.data.proposals || []).filter(p => p.proposal_type === "标准资料");
const missingCount = 7 - existing.length;
if (missingCount > 0) {
+ hideLoadingOverlay();
await ensureStandardProposals();
return; // ensureStandardProposals 内部会再次 render
}
}
- render();
+ // 逐步渲染
+ for (var i = 0; i < _loadingSteps.length; i++) {
+ var step = _loadingSteps[i];
+ if (typeof window[step.fn] === 'function') {
+ window[step.fn]();
+ }
+ updateLoadingStep(i, _loadingSteps.length);
+ await new Promise(function(r) { setTimeout(r, 50); });
+ }
+ if (window.lucide) window.lucide.createIcons();
+ setTimeout(hideLoadingOverlay, 300);
}
function switchTab(tab) {
diff --git a/templates/index.html b/templates/index.html
index bf4de69..7495998 100644
--- a/templates/index.html
+++ b/templates/index.html
@@ -76,7 +76,7 @@
-
OPC Manager v1.0.0.38
+
OPC Manager v1.0.0.39