Commit 748348f010daebf3f581773b6bf98b6277d60fbb

Authored by zichun
1 parent c0e2fcae

feat(frontend): match prototype mega-nav and trim sidebar tree

- Sidebar 系统管理 now contains only 角色管理 / 菜单配置 / 操作日志
  (用户列表 and 系统模块配置 are accessed via mega-nav, not duplicated
  in the tree).
- MegaNav rebuilt as full-viewport dark layout per prototype: dark
  topbar with highlighted 全部导航 chip, 150px left rail of categories
  (active = accent + white left border), right grid of category columns
  with bordered titles and muted/featured items.
- MEGA_COLUMNS expanded to 8 columns matching prototype 系统设置
  (期初设置 / 用户管理 / 系统参数 / 计算方案 / 日志 / 开发平台 /
  API对接管理 / 系统模块).
frontend/src/components/MegaNav.tsx
1 import { useState } from "react"; 1 import { useState } from "react";
2 -import { CloseOutlined, FolderOutlined, SettingOutlined } from "@ant-design/icons"; 2 +import {
  3 + AppstoreOutlined,
  4 + HomeOutlined,
  5 + CloseOutlined,
  6 + FolderOutlined,
  7 + SettingOutlined,
  8 +} from "@ant-design/icons";
3 import { MEGA_NAV, MEGA_COLUMNS } from "@/utils/data"; 9 import { MEGA_NAV, MEGA_COLUMNS } from "@/utils/data";
4 10
5 interface Props { 11 interface Props {
@@ -11,38 +17,93 @@ export default function MegaNav({ onClose, onOpen }: Props) { @@ -11,38 +17,93 @@ export default function MegaNav({ onClose, onOpen }: Props) {
11 const [activeId, setActiveId] = useState<string>( 17 const [activeId, setActiveId] = useState<string>(
12 MEGA_NAV.find((s) => s.active)?.id ?? "sys" 18 MEGA_NAV.find((s) => s.active)?.id ?? "sys"
13 ); 19 );
14 - const columns = MEGA_COLUMNS[activeId] ?? []; 20 + const cols = MEGA_COLUMNS[activeId];
15 21
16 return ( 22 return (
17 <div 23 <div
18 style={{ 24 style={{
19 position: "fixed", 25 position: "fixed",
20 inset: 0, 26 inset: 0,
21 - background: "rgba(0,0,0,0.4)",  
22 zIndex: 100, 27 zIndex: 100,
  28 + background: "#1a2030",
  29 + color: "var(--text-on-dark)",
23 display: "flex", 30 display: "flex",
  31 + flexDirection: "column",
24 }} 32 }}
25 - onClick={onClose}  
26 > 33 >
27 <div 34 <div
28 - onClick={(e) => e.stopPropagation()}  
29 style={{ 35 style={{
  36 + height: 36,
  37 + flex: "none",
30 display: "flex", 38 display: "flex",
31 - flex: 1,  
32 - background: "#fff",  
33 - margin: "60px auto",  
34 - maxWidth: 1100,  
35 - height: "calc(100% - 120px)",  
36 - border: "1px solid var(--border)",  
37 - boxShadow: "0 6px 32px rgba(0,0,0,0.3)", 39 + alignItems: "center",
  40 + padding: "0 10px",
  41 + background: "#262d3a",
  42 + borderBottom: "1px solid #1a1f2a",
38 }} 43 }}
39 > 44 >
  45 + <button
  46 + onClick={onClose}
  47 + style={{
  48 + display: "inline-flex",
  49 + alignItems: "center",
  50 + gap: 5,
  51 + height: 28,
  52 + padding: "0 10px",
  53 + background: "rgba(58,142,224,0.18)",
  54 + color: "#fff",
  55 + border: "1px solid rgba(58,142,224,0.4)",
  56 + cursor: "pointer",
  57 + fontSize: 12,
  58 + fontFamily: "inherit",
  59 + }}
  60 + >
  61 + <AppstoreOutlined /> 全部导航
  62 + </button>
  63 + <button
  64 + onClick={onClose}
  65 + style={{
  66 + display: "inline-flex",
  67 + alignItems: "center",
  68 + gap: 5,
  69 + height: 28,
  70 + padding: "0 10px",
  71 + background: "transparent",
  72 + color: "var(--text-on-dark)",
  73 + border: "none",
  74 + cursor: "pointer",
  75 + fontSize: 12,
  76 + marginLeft: 4,
  77 + fontFamily: "inherit",
  78 + }}
  79 + >
  80 + <HomeOutlined /> 主页
  81 + </button>
  82 + <div style={{ flex: 1 }} />
  83 + <button
  84 + onClick={onClose}
  85 + title="关闭"
  86 + style={{
  87 + width: 28,
  88 + height: 28,
  89 + background: "transparent",
  90 + color: "var(--text-on-dark)",
  91 + border: "none",
  92 + cursor: "pointer",
  93 + }}
  94 + >
  95 + <CloseOutlined />
  96 + </button>
  97 + </div>
  98 +
  99 + <div style={{ flex: 1, display: "flex", overflow: "hidden", minHeight: 0 }}>
40 <div 100 <div
41 style={{ 101 style={{
42 - width: 200,  
43 - borderRight: "1px solid var(--border)",  
44 - background: "var(--bg-app)", 102 + width: 150,
  103 + flex: "none",
  104 + borderRight: "1px solid rgba(255,255,255,0.08)",
45 overflow: "auto", 105 overflow: "auto",
  106 + padding: "8px 0",
46 }} 107 }}
47 > 108 >
48 {MEGA_NAV.map((s) => { 109 {MEGA_NAV.map((s) => {
@@ -55,117 +116,99 @@ export default function MegaNav({ onClose, onOpen }: Props) { @@ -55,117 +116,99 @@ export default function MegaNav({ onClose, onOpen }: Props) {
55 display: "flex", 116 display: "flex",
56 alignItems: "center", 117 alignItems: "center",
57 gap: 8, 118 gap: 8,
58 - padding: "8px 12px",  
59 - fontSize: 12, 119 + padding: "8px 14px",
60 cursor: "pointer", 120 cursor: "pointer",
61 - color: active ? "var(--accent-strong)" : "var(--text)",  
62 - background: active ? "var(--accent-soft)" : "transparent",  
63 - borderLeft: active ? "3px solid var(--accent)" : "3px solid transparent",  
64 - fontWeight: active ? 500 : 400, 121 + fontSize: 12,
  122 + background: active ? "var(--accent)" : "transparent",
  123 + color: active ? "#fff" : "var(--text-on-dark)",
  124 + borderLeft: active ? "3px solid #fff" : "3px solid transparent",
  125 + }}
  126 + onMouseEnter={(e) => {
  127 + if (!active) e.currentTarget.style.background = "rgba(255,255,255,0.06)";
  128 + }}
  129 + onMouseLeave={(e) => {
  130 + if (!active) e.currentTarget.style.background = "transparent";
65 }} 131 }}
66 > 132 >
67 {s.id === "sys" ? <SettingOutlined /> : <FolderOutlined />} 133 {s.id === "sys" ? <SettingOutlined /> : <FolderOutlined />}
68 - {s.label} 134 + <span>{s.label}</span>
69 </div> 135 </div>
70 ); 136 );
71 })} 137 })}
72 </div> 138 </div>
73 - <div  
74 - style={{  
75 - flex: 1,  
76 - display: "flex",  
77 - flexDirection: "column",  
78 - overflow: "hidden",  
79 - }}  
80 - >  
81 - <div  
82 - style={{  
83 - display: "flex",  
84 - alignItems: "center",  
85 - padding: "10px 16px",  
86 - borderBottom: "1px solid var(--border)",  
87 - }}  
88 - >  
89 - <div style={{ flex: 1, fontSize: 14, fontWeight: 500 }}>  
90 - {MEGA_NAV.find((s) => s.id === activeId)?.label}  
91 - </div>  
92 - <button  
93 - onClick={onClose} 139 +
  140 + <div style={{ flex: 1, overflow: "auto", padding: "16px 24px" }}>
  141 + {cols ? (
  142 + <div
94 style={{ 143 style={{
95 - background: "transparent",  
96 - border: "none",  
97 - cursor: "pointer",  
98 - fontSize: 14,  
99 - color: "var(--text-muted)", 144 + display: "grid",
  145 + gridTemplateColumns: "repeat(auto-fit, minmax(180px, 1fr))",
  146 + gap: "0 24px",
100 }} 147 }}
101 > 148 >
102 - <CloseOutlined />  
103 - </button>  
104 - </div>  
105 - <div  
106 - style={{  
107 - flex: 1,  
108 - overflow: "auto",  
109 - padding: 16,  
110 - display: "grid",  
111 - gridTemplateColumns: "repeat(auto-fill, minmax(200px, 1fr))",  
112 - gap: 16,  
113 - alignContent: "start",  
114 - }}  
115 - >  
116 - {columns.length === 0 && (  
117 - <div style={{ color: "var(--text-faint)", fontSize: 12 }}>  
118 - 此分类暂无配置内容  
119 - </div>  
120 - )}  
121 - {columns.map((col) => (  
122 - <div key={col.title}>  
123 - <div  
124 - style={{  
125 - fontSize: 12,  
126 - fontWeight: 500,  
127 - color: "var(--text-muted)",  
128 - paddingBottom: 6,  
129 - borderBottom: "1px solid var(--border)",  
130 - marginBottom: 6,  
131 - }}  
132 - >  
133 - {col.title}  
134 - </div>  
135 - {col.items.map((it) => ( 149 + {cols.map((col) => (
  150 + <div key={col.title} style={{ marginBottom: 24 }}>
136 <div 151 <div
137 - key={it.label}  
138 - onClick={() => {  
139 - if (it.screen) {  
140 - onOpen(it.screen, it.label);  
141 - onClose();  
142 - }  
143 - }}  
144 style={{ 152 style={{
145 - padding: "5px 6px",  
146 - fontSize: 12,  
147 - cursor: it.screen ? "pointer" : "default",  
148 - color: it.screen  
149 - ? it.featured  
150 - ? "var(--accent-strong)"  
151 - : "var(--text)"  
152 - : "var(--text-faint)",  
153 - fontWeight: it.featured ? 500 : 400,  
154 - borderRadius: 2,  
155 - }}  
156 - onMouseEnter={(e) => {  
157 - if (it.screen) e.currentTarget.style.background = "var(--bg-row-hover)";  
158 - }}  
159 - onMouseLeave={(e) => {  
160 - e.currentTarget.style.background = "transparent"; 153 + fontSize: 13,
  154 + fontWeight: 500,
  155 + color: "#fff",
  156 + marginBottom: 10,
  157 + paddingBottom: 6,
  158 + borderBottom: "1px solid rgba(255,255,255,0.1)",
161 }} 159 }}
162 > 160 >
163 - {it.label} 161 + {col.title}
164 </div> 162 </div>
165 - ))}  
166 - </div>  
167 - ))}  
168 - </div> 163 + <div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
  164 + {col.items.map((it, i) => {
  165 + const clickable = !!it.screen;
  166 + return (
  167 + <div
  168 + key={i}
  169 + onClick={
  170 + clickable
  171 + ? () => {
  172 + onOpen(it.screen!, it.label);
  173 + onClose();
  174 + }
  175 + : undefined
  176 + }
  177 + style={{
  178 + fontSize: 12,
  179 + color: it.featured ? "var(--accent)" : "var(--text-on-dark-muted)",
  180 + cursor: clickable ? "pointer" : "default",
  181 + display: "inline-flex",
  182 + alignItems: "center",
  183 + gap: 4,
  184 + padding: "1px 0",
  185 + }}
  186 + onMouseEnter={(e) => {
  187 + if (clickable) e.currentTarget.style.color = "#fff";
  188 + }}
  189 + onMouseLeave={(e) => {
  190 + if (clickable)
  191 + e.currentTarget.style.color = it.featured
  192 + ? "var(--accent)"
  193 + : "var(--text-on-dark-muted)";
  194 + }}
  195 + >
  196 + {it.label}
  197 + {it.featured ? (
  198 + <span style={{ color: "var(--warning)", fontSize: 10 }}>★</span>
  199 + ) : null}
  200 + </div>
  201 + );
  202 + })}
  203 + </div>
  204 + </div>
  205 + ))}
  206 + </div>
  207 + ) : (
  208 + <div style={{ color: "var(--text-on-dark-muted)", fontSize: 13, padding: 40 }}>
  209 + {MEGA_NAV.find((s) => s.id === activeId)?.label} · 模块开发中
  210 + </div>
  211 + )}
169 </div> 212 </div>
170 </div> 213 </div>
171 </div> 214 </div>
frontend/src/utils/data.ts
@@ -74,8 +74,6 @@ export const NAV_TREE: NavNode[] = [ @@ -74,8 +74,6 @@ export const NAV_TREE: NavNode[] = [
74 label: "系统管理", 74 label: "系统管理",
75 icon: "settings", 75 icon: "settings",
76 children: [ 76 children: [
77 - { id: "userlist", label: "用户列表", leaf: true, screen: "userlist" },  
78 - { id: "module", label: "系统模块配置", leaf: true, screen: "module" },  
79 { id: "roles", label: "角色管理", leaf: true }, 77 { id: "roles", label: "角色管理", leaf: true },
80 { id: "menucfg", label: "菜单配置", leaf: true }, 78 { id: "menucfg", label: "菜单配置", leaf: true },
81 { id: "log", label: "操作日志", leaf: true }, 79 { id: "log", label: "操作日志", leaf: true },
@@ -97,6 +95,8 @@ export const MEGA_NAV = [ @@ -97,6 +95,8 @@ export const MEGA_NAV = [
97 { id: "logistics", label: "物流管理" }, 95 { id: "logistics", label: "物流管理" },
98 { id: "qc", label: "质量管理" }, 96 { id: "qc", label: "质量管理" },
99 { id: "fin", label: "财务管理" }, 97 { id: "fin", label: "财务管理" },
  98 + { id: "cost-pro", label: "成本管理(专)" },
  99 + { id: "cost", label: "成本管理" },
100 { id: "equip", label: "设备管理" }, 100 { id: "equip", label: "设备管理" },
101 { id: "hr", label: "人事行政" }, 101 { id: "hr", label: "人事行政" },
102 { id: "oa", label: "OA 系统" }, 102 { id: "oa", label: "OA 系统" },
@@ -110,28 +110,73 @@ export const MEGA_COLUMNS: Record&lt; @@ -110,28 +110,73 @@ export const MEGA_COLUMNS: Record&lt;
110 > = { 110 > = {
111 sys: [ 111 sys: [
112 { 112 {
  113 + title: "期初设置",
  114 + items: [
  115 + { label: "客户期初" },
  116 + { label: "供应商期初" },
  117 + { label: "材料期初" },
  118 + { label: "产品期初" },
  119 + { label: "数据导入" },
  120 + { label: "离线导出下载" },
  121 + ],
  122 + },
  123 + {
113 title: "用户管理", 124 title: "用户管理",
114 items: [ 125 items: [
115 { label: "用户列表", screen: "userlist", featured: true }, 126 { label: "用户列表", screen: "userlist", featured: true },
116 { label: "系统权限" }, 127 { label: "系统权限" },
  128 + { label: "系统权限稽查表" },
117 { label: "权限组" }, 129 { label: "权限组" },
118 ], 130 ],
119 }, 131 },
120 { 132 {
121 - title: "系统模块", 133 + title: "系统参数",
122 items: [ 134 items: [
123 - { label: "系统模块配置", screen: "module", featured: true },  
124 - { label: "菜单配置" },  
125 - { label: "模块字段配置" }, 135 + { label: "系统参数" },
  136 + { label: "财务结账" },
  137 + { label: "系统常量配置" },
126 ], 138 ],
127 }, 139 },
128 { 140 {
129 - title: "系统参数",  
130 - items: [{ label: "系统参数" }, { label: "财务结准" }, { label: "系统常量配置" }], 141 + title: "计算方案",
  142 + items: [{ label: "方案列表" }, { label: "计算参数" }],
131 }, 143 },
132 { 144 {
133 title: "日志", 145 title: "日志",
134 - items: [{ label: "操作日志" }, { label: "MYSQL 监听器" }], 146 + items: [
  147 + { label: "个性化模块" },
  148 + { label: "操作日志" },
  149 + { label: "异常清除KPI任务表" },
  150 + { label: "MYSQL监听器" },
  151 + ],
  152 + },
  153 + {
  154 + title: "开发平台",
  155 + items: [
  156 + { label: "自定义开发范例" },
  157 + { label: "系统功能模块设置" },
  158 + { label: "EBC流程清单" },
  159 + { label: "功能模块界面设置" },
  160 + { label: "增删改存业务处理" },
  161 + ],
  162 + },
  163 + {
  164 + title: "API对接管理",
  165 + items: [
  166 + { label: "调用第三方接口(TOKEN配置)" },
  167 + { label: "调用第三方接口(接口定义)" },
  168 + { label: "被第三方调用(生成token)" },
  169 + { label: "数据同步" },
  170 + { label: "被第三方调用(API定义)" },
  171 + ],
  172 + },
  173 + {
  174 + title: "系统模块",
  175 + items: [
  176 + { label: "系统模块配置", screen: "module", featured: true },
  177 + { label: "菜单配置" },
  178 + { label: "模块字段配置" },
  179 + ],
135 }, 180 },
136 ], 181 ],
137 }; 182 };