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 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 9 import { MEGA_NAV, MEGA_COLUMNS } from "@/utils/data";
4 10  
5 11 interface Props {
... ... @@ -11,38 +17,93 @@ export default function MegaNav({ onClose, onOpen }: Props) {
11 17 const [activeId, setActiveId] = useState<string>(
12 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 22 return (
17 23 <div
18 24 style={{
19 25 position: "fixed",
20 26 inset: 0,
21   - background: "rgba(0,0,0,0.4)",
22 27 zIndex: 100,
  28 + background: "#1a2030",
  29 + color: "var(--text-on-dark)",
23 30 display: "flex",
  31 + flexDirection: "column",
24 32 }}
25   - onClick={onClose}
26 33 >
27 34 <div
28   - onClick={(e) => e.stopPropagation()}
29 35 style={{
  36 + height: 36,
  37 + flex: "none",
30 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 100 <div
41 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 105 overflow: "auto",
  106 + padding: "8px 0",
46 107 }}
47 108 >
48 109 {MEGA_NAV.map((s) => {
... ... @@ -55,117 +116,99 @@ export default function MegaNav({ onClose, onOpen }: Props) {
55 116 display: "flex",
56 117 alignItems: "center",
57 118 gap: 8,
58   - padding: "8px 12px",
59   - fontSize: 12,
  119 + padding: "8px 14px",
60 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 133 {s.id === "sys" ? <SettingOutlined /> : <FolderOutlined />}
68   - {s.label}
  134 + <span>{s.label}</span>
69 135 </div>
70 136 );
71 137 })}
72 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 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 151 <div
137   - key={it.label}
138   - onClick={() => {
139   - if (it.screen) {
140   - onOpen(it.screen, it.label);
141   - onClose();
142   - }
143   - }}
144 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 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 212 </div>
170 213 </div>
171 214 </div>
... ...
frontend/src/utils/data.ts
... ... @@ -74,8 +74,6 @@ export const NAV_TREE: NavNode[] = [
74 74 label: "系统管理",
75 75 icon: "settings",
76 76 children: [
77   - { id: "userlist", label: "用户列表", leaf: true, screen: "userlist" },
78   - { id: "module", label: "系统模块配置", leaf: true, screen: "module" },
79 77 { id: "roles", label: "角色管理", leaf: true },
80 78 { id: "menucfg", label: "菜单配置", leaf: true },
81 79 { id: "log", label: "操作日志", leaf: true },
... ... @@ -97,6 +95,8 @@ export const MEGA_NAV = [
97 95 { id: "logistics", label: "物流管理" },
98 96 { id: "qc", label: "质量管理" },
99 97 { id: "fin", label: "财务管理" },
  98 + { id: "cost-pro", label: "成本管理(专)" },
  99 + { id: "cost", label: "成本管理" },
100 100 { id: "equip", label: "设备管理" },
101 101 { id: "hr", label: "人事行政" },
102 102 { id: "oa", label: "OA 系统" },
... ... @@ -110,28 +110,73 @@ export const MEGA_COLUMNS: Record&lt;
110 110 > = {
111 111 sys: [
112 112 {
  113 + title: "期初设置",
  114 + items: [
  115 + { label: "客户期初" },
  116 + { label: "供应商期初" },
  117 + { label: "材料期初" },
  118 + { label: "产品期初" },
  119 + { label: "数据导入" },
  120 + { label: "离线导出下载" },
  121 + ],
  122 + },
  123 + {
113 124 title: "用户管理",
114 125 items: [
115 126 { label: "用户列表", screen: "userlist", featured: true },
116 127 { label: "系统权限" },
  128 + { label: "系统权限稽查表" },
117 129 { label: "权限组" },
118 130 ],
119 131 },
120 132 {
121   - title: "系统模块",
  133 + title: "系统参数",
122 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 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 };
... ...