左侧菜单改版:工作台下拉 + 5个图标导航
- 工作台改为下拉菜单(layout-grid + chevron-down 图标) - 顶部 tabs 移到左侧 sidebar,5 个图标导航(首页/财务/台账/方案/产品) - 头像与工作台间、工作台与导航间各加分隔线 - 经营管理 tab 短名改为'财务' - 移除 .tabs 样式,新增 .sidebar-tab 样式
This commit is contained in:
@@ -1,26 +1,13 @@
|
||||
// app.js — 入口文件(加载模块 + 初始化)
|
||||
// 所有业务逻辑已拆分到 modules/ 目录:
|
||||
// utils.js — 共享状态、工具函数、API 封装
|
||||
// home.js — 首页 + 财务趋势图
|
||||
// projects.js — 重点工作与台账(项目+任务+拖拽)
|
||||
// proposals.js — 业务方案 + 文件管理
|
||||
// products.js — 产品迭代
|
||||
// finance.js — 经营管理(财务)
|
||||
// drawer.js — 详情抽屉 + 评论 + 转移
|
||||
|
||||
// Tab 点击委托
|
||||
document.querySelector("#tabs").addEventListener("click", (event) => {
|
||||
const button = event.target.closest("button[data-tab]");
|
||||
if (button) switchTab(button.dataset.tab);
|
||||
});
|
||||
|
||||
// 恢复上次的工作台和标签页
|
||||
const savedTenant = localStorage.getItem("opc-active-tenant");
|
||||
if (savedTenant) {
|
||||
state.tenant = savedTenant;
|
||||
document.querySelectorAll(".workspace-nav-item").forEach(el => el.classList.toggle("active", el.dataset.tenant === savedTenant));
|
||||
const label = savedTenant.replace("·无界", "");
|
||||
document.querySelector("#workspaceTitle").textContent = label + " OPC 工作台";
|
||||
const tLabel = document.querySelector("#currentTenantLabel");
|
||||
if (tLabel) { tLabel.textContent = label || "工作台"; tLabel.title = savedTenant; }
|
||||
}
|
||||
const savedTab = localStorage.getItem("opc-active-tab");
|
||||
|
||||
|
||||
@@ -13,13 +13,57 @@ function applyUserTenants() {
|
||||
e.stopPropagation();
|
||||
toggleUserMenu(user);
|
||||
});
|
||||
const allowedTenants = data.tenants || [];
|
||||
document.querySelectorAll(".workspace-nav-item").forEach(el => {
|
||||
el.style.display = allowedTenants.includes(el.dataset.tenant) ? "" : "none";
|
||||
});
|
||||
// 缓存可用工作台列表,供下拉菜单使用
|
||||
state.allowedTenants = data.tenants || [];
|
||||
updateTenantLabel();
|
||||
});
|
||||
}
|
||||
|
||||
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 || [];
|
||||
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]";
|
||||
menu.style.left = Math.min(rect.left - 8, window.innerWidth - 180) + "px";
|
||||
menu.style.top = rect.bottom + 6 + "px";
|
||||
menu.innerHTML = `
|
||||
<div class="px-4 py-2 border-b border-slate-100">
|
||||
<p class="text-xs font-semibold text-slate-500 uppercase tracking-wider">切换工作台</p>
|
||||
</div>
|
||||
${tenants.map(t => `
|
||||
<button class="w-full text-left px-4 py-2 text-sm hover:bg-slate-50 transition-colors flex items-center justify-between gap-2 ${t === state.tenant ? 'text-blue-600 font-medium' : 'text-slate-700'}" onclick="switchTenantFromMenu('${t.replace(/'/g, "\\'")}')">
|
||||
<span>${esc(t)}</span>
|
||||
${t === state.tenant ? '<i data-lucide="check" style="width:14px;height:14px"></i>' : ''}
|
||||
</button>
|
||||
`).join('')}`;
|
||||
document.body.appendChild(menu);
|
||||
if (window.lucide) lucide.createIcons();
|
||||
setTimeout(() => {
|
||||
document.addEventListener("click", function closeMenu() {
|
||||
menu.remove();
|
||||
document.removeEventListener("click", closeMenu);
|
||||
}, { once: true });
|
||||
}, 10);
|
||||
};
|
||||
|
||||
window.switchTenantFromMenu = (tenant) => {
|
||||
document.getElementById("tenantMenu")?.remove();
|
||||
switchTenant(tenant);
|
||||
};
|
||||
|
||||
function updateTenantLabel() {
|
||||
const label = document.querySelector("#currentTenantLabel");
|
||||
if (label) {
|
||||
label.textContent = state.tenant.replace("·无界", "") || "工作台";
|
||||
label.title = state.tenant;
|
||||
}
|
||||
}
|
||||
|
||||
window.toggleUserMenu = (user) => {
|
||||
let menu = document.getElementById("userMenu");
|
||||
if (menu) { menu.remove(); return; }
|
||||
|
||||
@@ -123,7 +123,7 @@ async function load() {
|
||||
function switchTab(tab) {
|
||||
state.active = tab;
|
||||
localStorage.setItem("opc-active-tab", tab);
|
||||
document.querySelectorAll("#tabs button").forEach((btn) => btn.classList.toggle("active", btn.dataset.tab === tab));
|
||||
document.querySelectorAll(".sidebar-tab").forEach((btn) => btn.classList.toggle("active", btn.dataset.tab === tab));
|
||||
document.querySelectorAll(".panel").forEach((panel) => panel.classList.toggle("active", panel.id === tab));
|
||||
render();
|
||||
}
|
||||
@@ -179,7 +179,8 @@ window.switchTenant = (tenant) => {
|
||||
state.selectedProject = null;
|
||||
localStorage.setItem("opc-active-tenant", tenant);
|
||||
document.querySelector("#workspaceTitle").textContent = tenant.replace("·无界", "") + " OPC 工作台";
|
||||
document.querySelectorAll(".workspace-nav-item").forEach((el) => el.classList.toggle("active", el.dataset.tenant === tenant));
|
||||
const label = document.querySelector("#currentTenantLabel");
|
||||
if (label) { label.textContent = tenant.replace("·无界", "") || "工作台"; label.title = tenant; }
|
||||
load();
|
||||
};
|
||||
window.doLogout = async () => {
|
||||
|
||||
@@ -32,25 +32,30 @@ body {
|
||||
background: rgba(96,165,250,0.15);
|
||||
}
|
||||
|
||||
.tabs {
|
||||
.sidebar-tab {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.tabs button {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
border-bottom: 2px solid transparent;
|
||||
color: #64748b;
|
||||
display: inline-flex;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
gap: 8px;
|
||||
padding: 14px 16px;
|
||||
padding: 8px 4px;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
color: #94a3b8;
|
||||
transition: all 0.15s ease;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.tabs button.active {
|
||||
border-bottom-color: #1d4ed8;
|
||||
color: #1d4ed8;
|
||||
.sidebar-tab:hover {
|
||||
background: #1e293b;
|
||||
color: #cbd5e1;
|
||||
}
|
||||
|
||||
.sidebar-tab.active {
|
||||
background: #1e293b;
|
||||
color: #60a5fa;
|
||||
}
|
||||
|
||||
.sidebar-tab.active i {
|
||||
color: #60a5fa;
|
||||
}
|
||||
|
||||
.panel {
|
||||
|
||||
@@ -29,29 +29,42 @@
|
||||
<div class="flex min-h-screen">
|
||||
<!-- 左侧工作台切换栏 -->
|
||||
<aside class="w-[72px] bg-slate-900 flex flex-col items-center py-6 gap-1 shrink-0 sticky top-0 h-screen overflow-y-auto" id="workspaceSidebar">
|
||||
<div class="flex flex-col items-center mb-6 cursor-pointer" onclick="document.getElementById('userAvatar').click()">
|
||||
<div class="flex flex-col items-center mb-2 cursor-pointer" onclick="document.getElementById('userAvatar').click()">
|
||||
<div class="w-10 h-10 rounded-full bg-slate-700 flex items-center justify-center text-white font-bold text-sm hover:ring-2 hover:ring-blue-400 transition-all" id="userAvatar" title=""></div>
|
||||
<span class="text-[10px] text-slate-400 mt-1.5 max-w-[64px] truncate text-center" id="userDisplayName" title=""></span>
|
||||
</div>
|
||||
<div class="workspace-nav-item active" data-tenant="科普·无界" onclick="switchTenant('科普·无界')" title="科普·无界">
|
||||
<svg class="w-6 h-6" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M8 12h8"/><path d="M12 8v8"/></svg>
|
||||
<span class="text-[10px] mt-1">科普</span>
|
||||
<!-- 分隔线 -->
|
||||
<div class="w-8 h-px bg-slate-700 my-2"></div>
|
||||
<!-- 工作台切换按钮 -->
|
||||
<div class="flex flex-col items-center cursor-pointer hover:bg-slate-800 rounded-lg py-2 px-1 w-14 transition-colors" onclick="toggleTenantMenu(event)">
|
||||
<i data-lucide="layout-grid" style="width:18px;height:18px;color:#94a3b8"></i>
|
||||
<span class="text-[10px] text-slate-400 mt-1 max-w-[56px] truncate text-center" id="currentTenantLabel">工作台</span>
|
||||
<i data-lucide="chevron-down" style="width:12px;height:12px;color:#64748b;margin-top:2px"></i>
|
||||
</div>
|
||||
<div class="workspace-nav-item" data-tenant="科研·无界" onclick="switchTenant('科研·无界')" title="科研·无界">
|
||||
<svg class="w-6 h-6" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"/></svg>
|
||||
<span class="text-[10px] mt-1">科研</span>
|
||||
</div>
|
||||
<div class="workspace-nav-item" data-tenant="医患·无界" onclick="switchTenant('医患·无界')" title="医患·无界">
|
||||
<svg class="w-6 h-6" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 12h-4l-3 9L9 3l-3 9H2"/></svg>
|
||||
<span class="text-[10px] mt-1">医患</span>
|
||||
</div>
|
||||
<div class="workspace-nav-item" data-tenant="MCN·无界" onclick="switchTenant('MCN·无界')" title="MCN·无界">
|
||||
<svg class="w-6 h-6" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="7" width="20" height="15" rx="2"/><path d="M17 2l-5 5-5-5"/></svg>
|
||||
<span class="text-[10px] mt-1">MCN</span>
|
||||
</div>
|
||||
<div class="workspace-nav-item" data-tenant="无界·无界" onclick="switchTenant('无界·无界')" title="无界·无界">
|
||||
<svg class="w-6 h-6" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M2 12h20"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></svg>
|
||||
<span class="text-[10px] mt-1">无界</span>
|
||||
<!-- 分隔线 -->
|
||||
<div class="w-8 h-px bg-slate-700 my-2"></div>
|
||||
<!-- 导航 Tab 图标 -->
|
||||
<div class="flex flex-col items-center gap-1 w-14" id="sidebarTabs">
|
||||
<div class="sidebar-tab active" data-tab="home" onclick="switchTab('home')" title="首页">
|
||||
<i data-lucide="home" style="width:20px;height:20px"></i>
|
||||
<span class="text-[10px] mt-1">首页</span>
|
||||
</div>
|
||||
<div class="sidebar-tab" data-tab="finance" onclick="switchTab('finance')" title="经营管理">
|
||||
<i data-lucide="briefcase-business" style="width:20px;height:20px"></i>
|
||||
<span class="text-[10px] mt-1">财务</span>
|
||||
</div>
|
||||
<div class="sidebar-tab" data-tab="projects" onclick="switchTab('projects')" title="重点工作与台账">
|
||||
<i data-lucide="file-text" style="width:20px;height:20px"></i>
|
||||
<span class="text-[10px] mt-1">台账</span>
|
||||
</div>
|
||||
<div class="sidebar-tab" data-tab="proposals" onclick="switchTab('proposals')" title="业务方案">
|
||||
<i data-lucide="package" style="width:20px;height:20px"></i>
|
||||
<span class="text-[10px] mt-1">方案</span>
|
||||
</div>
|
||||
<div class="sidebar-tab" data-tab="products" onclick="switchTab('products')" title="产品迭代">
|
||||
<i data-lucide="wallet-cards" style="width:20px;height:20px"></i>
|
||||
<span class="text-[10px] mt-1">产品</span>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
<!-- 主内容区 -->
|
||||
@@ -67,14 +80,6 @@
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<nav class="tabs border-b border-slate-200 bg-white px-8" id="tabs">
|
||||
<button class="active" data-tab="home"><i data-lucide="home"></i>首页</button>
|
||||
<button data-tab="finance"><i data-lucide="briefcase-business"></i>经营管理</button>
|
||||
<button data-tab="projects"><i data-lucide="file-text"></i>重点工作与台账</button>
|
||||
<button data-tab="proposals"><i data-lucide="package"></i>业务方案</button>
|
||||
<button data-tab="products"><i data-lucide="wallet-cards"></i>产品迭代</button>
|
||||
</nav>
|
||||
|
||||
<main class="px-8 py-6">
|
||||
<section id="home" class="panel active"></section>
|
||||
<section id="projects" class="panel"></section>
|
||||
|
||||
Reference in New Issue
Block a user