From 68af363ca9b7b84c3678af3cb237866498f6cf01 Mon Sep 17 00:00:00 2001 From: andyesfly <35127884@qq.com> Date: Fri, 11 Sep 2026 17:01:24 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=E8=87=AA=E5=AE=9A?= =?UTF-8?q?=E4=B9=89=E5=9B=BE=E6=A0=87=E5=BA=93=E5=B9=B6=E8=B0=83=E6=95=B4?= =?UTF-8?q?=E8=8F=9C=E5=8D=95=E5=9B=BE=E6=A0=87=E4=B8=8E=E8=8F=9C=E5=8D=95?= =?UTF-8?q?=E5=BD=92=E5=B1=9E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 新增 iconfont 自定义图标库及相关依赖文件 2. 调整菜单图标为自定义字体图标,替换原有 Ant Design 图标 3. 将客户拜访菜单从客户管理移动到项目管理作为首个子菜单 4. 优化侧边菜单展开逻辑与图标样式适配 --- .gitignore | 1 + client/src/assets/fonts/iconfont/iconfont.css | 41 ++++++++++++ client/src/assets/fonts/iconfont/iconfont.js | 1 + .../src/assets/fonts/iconfont/iconfont.json | 51 +++++++++++++++ client/src/assets/fonts/iconfont/iconfont.ttf | Bin 0 -> 3204 bytes .../src/assets/fonts/iconfont/iconfont.woff | Bin 0 -> 2364 bytes .../src/assets/fonts/iconfont/iconfont.woff2 | Bin 0 -> 1772 bytes client/src/layouts/index.vue | 59 +++++++++++++++--- client/src/main.ts | 1 + database/db_seed.sql | 25 ++++---- .../migrations/20260911_menu_iconfont.sql | 31 +++++++++ ...0260911_move_customer_visit_to_project.sql | 16 +++++ 12 files changed, 207 insertions(+), 19 deletions(-) create mode 100644 .gitignore create mode 100644 client/src/assets/fonts/iconfont/iconfont.css create mode 100644 client/src/assets/fonts/iconfont/iconfont.js create mode 100644 client/src/assets/fonts/iconfont/iconfont.json create mode 100644 client/src/assets/fonts/iconfont/iconfont.ttf create mode 100644 client/src/assets/fonts/iconfont/iconfont.woff create mode 100644 client/src/assets/fonts/iconfont/iconfont.woff2 create mode 100644 database/migrations/20260911_menu_iconfont.sql create mode 100644 database/migrations/20260911_move_customer_visit_to_project.sql diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c4c4ffc --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*.zip diff --git a/client/src/assets/fonts/iconfont/iconfont.css b/client/src/assets/fonts/iconfont/iconfont.css new file mode 100644 index 0000000..b6e0b35 --- /dev/null +++ b/client/src/assets/fonts/iconfont/iconfont.css @@ -0,0 +1,41 @@ +@font-face { + font-family: "iconfont"; /* Project id 5232815 */ + /* Color fonts */ + src: + url('data:application/x-font-woff2;charset=utf-8;base64,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') format('woff2'), + url('iconfont.woff?t=1789098374123') format('woff'), + url('iconfont.ttf?t=1789098374123') format('truetype'); +} + +.iconfont { + font-family: "iconfont" !important; + font-size: 16px; + font-style: normal; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +.xt-jihuabaifang:before { + content: "\e6c4"; +} + +.xt-xiaochengxu:before { + content: "\e61e"; +} + +.xt-baifangshenyue:before { + content: "\e6c6"; +} + +.xt-topology:before { + content: "\e67c"; +} + +.xt-xiangmukancha:before { + content: "\e633"; +} + +.xt-xiangmufangan:before { + content: "\e738"; +} + diff --git a/client/src/assets/fonts/iconfont/iconfont.js b/client/src/assets/fonts/iconfont/iconfont.js new file mode 100644 index 0000000..164d377 --- /dev/null +++ b/client/src/assets/fonts/iconfont/iconfont.js @@ -0,0 +1 @@ +window._iconfont_svg_string_5232815='',(e=>{var t=(c=(c=document.getElementsByTagName("script"))[c.length-1]).getAttribute("data-injectcss"),c=c.getAttribute("data-disable-injectsvg");if(!c){var l,h,a,n,i,o=function(t,c){c.parentNode.insertBefore(t,c)};if(t&&!e.__iconfont__svg__cssinject__){e.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(t){console&&console.log(t)}}l=function(){var t,c=document.createElement("div");c.innerHTML=e._iconfont_svg_string_5232815,(c=c.getElementsByTagName("svg")[0])&&(c.setAttribute("aria-hidden","true"),c.style.position="absolute",c.style.width=0,c.style.height=0,c.style.overflow="hidden",c=c,(t=document.body).firstChild?o(c,t.firstChild):t.appendChild(c))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(l,0):(h=function(){document.removeEventListener("DOMContentLoaded",h,!1),l()},document.addEventListener("DOMContentLoaded",h,!1)):document.attachEvent&&(a=l,n=e.document,i=!1,s(),n.onreadystatechange=function(){"complete"==n.readyState&&(n.onreadystatechange=null,v())})}function v(){i||(i=!0,a())}function s(){try{n.documentElement.doScroll("left")}catch(t){return void setTimeout(s,50)}v()}})(window); \ No newline at end of file diff --git a/client/src/assets/fonts/iconfont/iconfont.json b/client/src/assets/fonts/iconfont/iconfont.json new file mode 100644 index 0000000..82f3ab7 --- /dev/null +++ b/client/src/assets/fonts/iconfont/iconfont.json @@ -0,0 +1,51 @@ +{ + "id": "5232815", + "name": "鑫潼智电后台", + "font_family": "iconfont", + "css_prefix_text": "xt-", + "description": "", + "glyphs": [ + { + "icon_id": "905033", + "name": "客户拜访", + "font_class": "jihuabaifang", + "unicode": "e6c4", + "unicode_decimal": 59076 + }, + { + "icon_id": "4424124", + "name": "小程序", + "font_class": "xiaochengxu", + "unicode": "e61e", + "unicode_decimal": 58910 + }, + { + "icon_id": "4520265", + "name": "拜访审阅", + "font_class": "baifangshenyue", + "unicode": "e6c6", + "unicode_decimal": 59078 + }, + { + "icon_id": "4922685", + "name": "拓扑", + "font_class": "topology", + "unicode": "e67c", + "unicode_decimal": 59004 + }, + { + "icon_id": "11622405", + "name": "项目勘察", + "font_class": "xiangmukancha", + "unicode": "e633", + "unicode_decimal": 58931 + }, + { + "icon_id": "46034577", + "name": "项目方案", + "font_class": "xiangmufangan", + "unicode": "e738", + "unicode_decimal": 59192 + } + ] +} diff --git a/client/src/assets/fonts/iconfont/iconfont.ttf b/client/src/assets/fonts/iconfont/iconfont.ttf new file mode 100644 index 0000000000000000000000000000000000000000..24d16ad6c71a833da7cb3018ca5d3e6fd6124b6a GIT binary patch literal 3204 zcmZt}ZERE5_1t@HKl|B!_KV+#^MT{fXJeCK``OQ7I01^nM=1%=610{UG9V2V0YXVt zvQ?y;RCN=AO4HhI6GeQ-3a6dJ9X#8OQ&6k}>`yF;` z5)s0)>_^D&KsY%$b@FNPd2v6&Zz9}(PZYHIxH;}C(LA{})?^yv81 zA$fi!1rR?%c=7nLCr>us0Tb=L!Rf$#!TxUaufzKS)&BtF+W;*5Cd+MT&Y8xu>^eJ* zpbJow-B5$c@px>ht2ybuBWCBcr z7XDu&!6o3x=*Teq9HJi&G;LsWFo$IB3mQnT;uPg;+d4qnG>o8?eQ*)xh=;gn2Byg# z7$JA)44r9FI6L=_VOMbE+7xte2hKtKaeQ@C~?06tapuLN3q|dYZmO-)3$Hb*x3{IUZ%)7;Pbh z&DGF|8i%GEe__+iXyK<3Z!BIyXr%F5HqXi}{QTl8ti1RlLhTc5>bbtSlbv4V=hFva z_5odJ@qe)RFkPq>5xUs;JKN17E&T4<7>g`k#^|)|2|*goC`PENNHh`er{&TR4HbuQ z?nhWce1uXMJ#)BPJ$z=gJrAGSYnppc4Yy_nONpGRwv=qMamPLIOZs2B8P*}7X@W$B zMBGT1tcqz_70XB)f-68>f-4bE8%AY_R?MQND=s17LZqf^6>A+uM*4oFOY~$4I`amI zB(g-p<@5&>Rdv%BI#vdQ-TD%`-^-HmrSu#v$oIt}T>_Ovl&7K`jJdsnps;^s{7yg8 zR&?KkmX;)N5*LiK0qViy6Tk}r=tfUevut7)THR@&BUVL2dtJm;TJfz9OQjO|_NqZb zhWc_tI8qN{O0U>|C&*B)Zv!ENtNJzop_#@FHVqRX0;Etw3}d5JG!{>0N&Vey(#Yp0 zw5_?^RxLt!3fTFx6Z0nmP$?)V;1XhSqb!#!Ggj18*+|PJBN2_v*shBuT5Q9<1V3>S}l`-8~AO`9lOvXJ$ElZDCmAk@i-73bLuT#Zx zDG^r{b~dQ0Qk<$%{nrUeQKZR$q6FfqL>2~K@O5c*y zoV!~Ilm_IE4jJP}?(kcIXn4lGcjL&jh{ueSTlu3Z8m5%Rj%No4v-R7#YBfg&6o37; z6bea1_bWuJ=B{w!m0Y#{$18qC@$&&y(wW95cpVLZAF~<*#*YcrqjvgHKve^q`7odc zxZ?qp^TR&24PO5uKTC7gvwV1+^Ku!?gbZl(D&A{3Sc}_fdcwf8<26HYDZH8#u$u4+ z!v}+JY+-(IJ5q|KnMDgTwJAX}`!-D(U(Vlu`jL>65hip~3?6YhDDC;?qeI&_&1~5` zvuS(%Tyd;qjuo3Dc{#E*+%xpV#*i$AJY7T!bR;^Qp2yCth|X@?{M7KKna%glUM}r6 z&D|w?#Cx<|uZwUW?1d-b9Gr&>@ME-ILA!bF&;(3m+bUe}kl7Kl-Qb?^O5JorSYkgO zl7{~CFVRl*R+usSQBbd78NgFShxwthd_0b&s*S^9VV_Hz#lj+D7_7yN;Y3j#6WzeG zvw1>IH7$tt!RB;8|%H6BVy4d!y@nBbKJ6d|~P+jOL5G z^7&nF$JfLYYvS=W30%}aLwmdx=5zPRUSBZ6u;w_Q5#3z3TO|7lApyTHxH?|{PAtzk z&=YbrHJGd~w3)7Tw3r9l82*51-^Bq4K@V0_AiOTi&F_jwo2jvKNUrFzDaT|}O$%k_ ztKEv^auFKlW9`<~+1U?kw>~QrSR~t98|%$xd+CMVY;7=~r?d4NmPL}aYczjJRWCJP zDlU94;S0#&!MlP#yqaqYlKl?icm@{U9!00e-;kI0XPOn@pj_l zN!SI4;BlCNBQOpppaA{&4&uMHxaTPLVO%Yj>02hXoE}|9AHzLPz>}zl?-^aP%W}!( zn)xdaTVtNzS@&!%#7fwUSZDZqk)pR0&&h%J9l_VEV6VKm7Vy6WV!wVp$R{G77rPI; sAG?IK07?e2%h)@xhma3TpWX0rjGCHzp+ScaL8JcOw#J@4d+^=)KkgP1KmY&$ literal 0 HcmV?d00001 diff --git a/client/src/assets/fonts/iconfont/iconfont.woff b/client/src/assets/fonts/iconfont/iconfont.woff new file mode 100644 index 0000000000000000000000000000000000000000..7c855e7862d4d3bdb15d3b662aa66a96da7e9414 GIT binary patch literal 2364 zcmY*bdpy%^AHFwc8*?aA!sDDA%4tfPgjpyhhf(y@#?Ub2d^X40Qi>Gju$)RwYEwNW zS$MHTB+-c)PYzK^Z&KL%Q~mRPKcDM&-`90t_xJvN?tiX3&UN>000U5=tpR3r#AN;E ztMmUaf~$)=05CO3B|-=Udtq9H19mQuMuoWgDr#0*@?1Sk%polc;?5ArM>w8*e>x=s z()u9o0D;QSZJY@TO$Y=4js*Y#>8RM^haURJ0w_=~2eTXZr`oy;ZF23}yZnPZ36ou%&8GJ3a-^*3BEI-kH7O9)e zKv^_$9(h@_rZQxi4UFTHv#q2VwNqeIQ0l7#M55*p=x zF)=Lwh*z5TioLzP#Q?;Y`jaUXa;ZS@NYH9=%HVW*`os11xLPx1oC=Nu-wo3}Q_)gk z)CQBvo>=!-R;;S5T%@Y3VjFB5>4I?yNncaG8&g6L zkVL~F&dRw3rIvCZeBP4Iu1DM9^O~OqH%Ep}h;|%5miOtCl|{d=flu9dNK(zcrY|6W zGUXE#CQcvZ@1Sj>)%k&gw#SF|bnVU(&Mj?81hYhQd!;$D9X)rL6MHuY ze{}FsSk=v;VV{QZtt@4eZ%>RTE41y8dmtt38bd9gWpv%K&ZLErWDa-o5V7~Dq6-fA zAf#a-?8{TzjlPD(v%7kp2J*A!ui1W69EyI-Vvd^GoseA8{c(QD-o_D54c3;qds4$+ z@xdA&KM%{YzM+_x(e|5idxjN=m!><8w|6Ua_UL1-l7?;)`*la3?7b>>WODN=_G2>!5_AM9+Z%+T5#qJIGjJlBgO+X8+#5H(<*V^0 zN~YQ05o&rU(y7H$o3T1se&0(EzB8A}P6S;k92&OR3u~fkk|I6bv!G;xQDaYijlWmR!(6FUM8)n|Eu6XlP+499zSdLRl?N^{A;P`D%+WE>%Iuwg0{#@Gxr zrY2tvA$i2`ZU?ov4h~)Y5fP_2UQOy2k=%_BzOQtVTVI{{WC~F@Sd&qX-hFE&Rd)KM z_#F9TRpi||4rr|?@QpqU2727@J~9e?`yKmtrT?qDHD4jW zD7hXU8$&O8cf2|>A*hYg5V`r4{Hn#=;rn{96Nn0B*nv7}~^YnWp(_`riZbV~OL?Pu@v;$rqJeu2Huhbw#Q@`>PE zT9IK_o~wsgbh%7;Vc$jmRG}UAn_2rqUf>?tv6=}~G8u?wr5!P2i}VNPrhHR|fM4Ll zjqn*u)0vNXpQw59bUIpB8mOI>-ucHL_I*?!5VQ(74>`i*GMIh_ya;$2N^JWTVXI*V z7~nYA5A?wna1AhEXqXgS9~_5ygZ;2C@ECZ^pOs&J=!1b0SQkolYv2Sj!8i;H+YIxC zU4`$5r@=468zfMYa7p~1vlRk{g@T6wo{KpKfYJf*;&-n3&-s%B$<%jLGF5?0X7GxY zzcZ{+?Ba*Hc5;sbDF?`?bl#y2)yjJ~q1!m|8U#IpIl+dYPEaY@OhNfM1vmxi2Xs-K zsQP}U)INVxKnUTKt=P^;Q!jn!Ck|vAH!$=xq{fxZuo8pm<4R{kwaDf`FGIWccdiD4 SOfh>y?uHxC+oBFO1^x%x5a@0I literal 0 HcmV?d00001 diff --git a/client/src/assets/fonts/iconfont/iconfont.woff2 b/client/src/assets/fonts/iconfont/iconfont.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..9562545d5f74f4b0d8a66cdb1a32d810a74433a3 GIT binary patch literal 1772 zcmVR_SluuHF>?5U{xuD{kPycQD z=k6|HvzJ{+N?1r7iB_;k#i2=!q_3nV3h%44+j`;5DC^VH8hpD+6S%IDX0rJ;`|LcBz=FB% z0Ol&d4yawm?R`20n7gGhL4e88avcwj(ITU<1NU(qP1DBmf<|1#8w`@v)Zj5BN)Abs z3|y>c;jDcY&ZF!^^(ln#L?|fFge3etHdysyB%F;2N@Wb1OJi=s)8v{HSL}jPWeQiz z^!V|l>W!wI_U&e4aF4e_P%;~zrPZIlo<-HlgiwW1IC8{Xu_0kc)~+PE@BYH$6F~`e zxxxDlKlUhf#fNU)u*#@=Dmor%MCsg5Lw^;wp8nfwlmXg_3?garkmh3Z4( zfuK_!shY=hG&v0DMnD4<)yUM2?nxQ3a$&A*=F@WF)GItOSYxgT8EXWER5i-=o06*K z=g<4zdandkUl2z;Q{C;jPUQet^?%iT?u`a3WO>T%v|*7 zDq%oaQLY^`m0<_-WUJ{^2 zhO_j$So&@?n~_sidf|&!UAoMSU-iAb{8vNLq-Ngo1=jaAvo1?_(~7;&@Lt^`#jAK< z)kS`fci`IT2kr{GwjC~PenW$O;7%WtpHm`q&BH_T2mkmZHg|#?9oA-UcK<|Q;yo8e zn0@u@@9)b|IsPYbravnFPWFyjv>E2nKYG6+9F-x&#R%w)%92eC_q9o~=)nhtdY@i`uQ)&{mcl)4 zXK-cRZz8LDNQ%E?a^HSQ&TdQE4j3QbsZ)AvlkD+H3_f&7q?5~NP8z&S9hCe*kNMq; z{j+EJN{VOs{j-X@<%dtZ>~#gp-CmDN!sXudk0HtBY!N7RbJHyS)>f9Z>{`*~gvm1~ z+mVnpx{yB5i67GR`}^+x=Jy*T@|GrL&gKE9+tn-BBHvTq!rkliKKsQItF7pw!*Q_0 zW<`;@QI|O+3@xx~wWs;yi0((K)6+;Qn_0+>TEB>|<+XMEE^VWhZ_p+Ug}tDx19bn3 z?c6{&+t=J`*3w4=?9O6xOrv zp31{{In|unkk**i5bl$5$!T_$%(u_arf8Lrvma5@ z=QndTVc+a%n0?MJ{TB!`BU}b`&m54A9QL`vi!h(s`$CwG0J6~x5rjxAiD9xao%BV3 z{LIWIcWr=}$EuUycQh*>8G`^=5H6Ng7=W~lfn=g7bSw&xs^gH3piYBC%)*$uqgG-( zMq(JOX3g4WDINRd+Gb!T#$$Q< O3JaPw!9qKV9)<@`wRKnk literal 0 HcmV?d00001 diff --git a/client/src/layouts/index.vue b/client/src/layouts/index.vue index 69299cd..7dd6c7e 100644 --- a/client/src/layouts/index.vue +++ b/client/src/layouts/index.vue @@ -140,7 +140,14 @@ const iconMap: Record = { DashboardOutlined, } +// iconfont 图标类名前缀(对应 assets/fonts/iconfont/iconfont.css 的 css_prefix_text) +const ICONFONT_PREFIX = 'xt-' + function resolveIcon(icon?: string | null) { + // icon 值形如 xt-xxx 时渲染 iconfont 图标,否则回退到 ant-design 图标 + if (icon && icon.startsWith(ICONFONT_PREFIX)) { + return () => h('i', { class: `iconfont ${icon}` }) + } const iconComponent = icon ? iconMap[icon] : undefined return () => h(iconComponent || MenuOutlined) } @@ -194,6 +201,20 @@ const menuItems = computed(() => [ ...buildMenuItems(permissionStore.menus), ]) +// 子菜单路径 → 所属父级目录的菜单 key,用于按当前路由展开对应的父菜单 +// (不能按路径首段推导:如“客户拜访”归属项目管理,但路径仍为 /customer/visit) +const parentKeyMap = computed>(() => { + const map: Record = {} + for (const node of permissionStore.menus) { + if (node.menuType !== 1) continue + const dirKey = node.menuPath || `dir-${node.id}` + for (const child of node.children || []) { + if (child.menuPath) map[child.menuPath] = dirKey + } + } + return map +}) + // 计算属性 const userInfo = computed(() => permissionStore.userInfo) const displayName = computed(() => userInfo.value?.realName || userInfo.value?.username || '管理员') @@ -219,10 +240,10 @@ watch( (path) => { selectedKeys.value = [path] - // 自动展开父级菜单 - const pathParts = path.split('/').filter(Boolean) - if (pathParts.length > 1) { - openKeys.value = [`/${pathParts[0]}`] + // 自动展开当前菜单所属的父级目录 + const parentKey = parentKeyMap.value[path] + if (parentKey) { + openKeys.value = [parentKey] } }, { immediate: true } @@ -234,9 +255,9 @@ watch(collapsed, (val) => { openKeys.value = [] } else { // 展开时根据当前路由自动打开对应的父菜单 - const pathParts = route.path.split('/').filter(Boolean) - if (pathParts.length > 1) { - openKeys.value = [`/${pathParts[0]}`] + const parentKey = parentKeyMap.value[route.path] + if (parentKey) { + openKeys.value = [parentKey] } } }) @@ -306,6 +327,30 @@ function handleUserMenuClick({ key }: { key: string }) { border-right: none; } + // iconfont 图标统一:该图标集为彩色字体,颜色写死在字形里,无法用 color 覆盖, + // 这里用 filter 统一压成单色,并按菜单状态在灰 / 白之间切换,与 ant-design 图标观感保持一致 + // 尺寸统一:ant-design 菜单图标为 14px,而 iconfont.css 自带 font-size: 16px, + // 故此处显式对齐为 14px,并重置 line-height,避免字体图标在 antd 的 line-height: 0 下出现基线偏移 + :deep(.ant-menu) .iconfont { + display: inline-block; + font-size: 14px; + line-height: 1; + vertical-align: -0.125em; + filter: brightness(0) invert(0.65); + } + + // 折叠态与 ant-design 图标同步放大到 16px + :deep(.ant-menu-inline-collapsed) .iconfont { + font-size: 16px; + } + + :deep(.ant-menu-item:hover) .iconfont, + :deep(.ant-menu-item-selected) .iconfont, + :deep(.ant-menu-submenu-title:hover) .iconfont, + :deep(.ant-menu-submenu-selected) .iconfont { + filter: brightness(0) invert(1); + } + .collapse-btn { position: absolute; bottom: 0; diff --git a/client/src/main.ts b/client/src/main.ts index 71bbb09..ae19d96 100644 --- a/client/src/main.ts +++ b/client/src/main.ts @@ -2,6 +2,7 @@ import { createApp } from "vue"; import Antd from "ant-design-vue"; import "ant-design-vue/dist/reset.css"; import "@/assets/styles/global.less"; +import "@/assets/fonts/iconfont/iconfont.css"; import App from "./App.vue"; import router from "./router"; import pinia from "./stores"; diff --git a/database/db_seed.sql b/database/db_seed.sql index 70aff4b..c1a19fa 100644 --- a/database/db_seed.sql +++ b/database/db_seed.sql @@ -85,7 +85,7 @@ ON CONFLICT (id) DO UPDATE SET INSERT INTO sys_menu (id, parent_id, menu_name, menu_path, component, icon, sort_order, menu_type, permission, visible) VALUES (12, 1, '客户档案管理', '/customer/info', 'customer/info/index', 'UserOutlined', 1, 2, 'customer:list', 1), (13, 1, '客户部门', '/customer/department', 'customer/department/index', 'TeamOutlined', 2, 2, 'customer:department', 1), -(14, 1, '客户小程序授权管理', '/customer/account', 'customer/account/index', 'SafetyOutlined', 3, 2, 'customer:account', 1), +(14, 1, '客户小程序授权管理', '/customer/account', 'customer/account/index', 'xt-xiaochengxu', 3, 2, 'customer:account', 1), (15, 1, '新增客户', NULL, NULL, NULL, 1, 3, 'customer:create', 1), (16, 1, '编辑客户', NULL, NULL, NULL, 2, 3, 'customer:update', 1), (17, 1, '删除客户', NULL, NULL, NULL, 3, 3, 'customer:delete', 1), @@ -106,18 +106,19 @@ ON CONFLICT (id) DO UPDATE SET permission = EXCLUDED.permission, visible = EXCLUDED.visible; --- 初始菜单(客户扩展子菜单 + 项目域子菜单,id:24-31 + 空间拓扑 32) --- 客户域:客户拜访(customer:visit) / 空间拓扑与用电架构(customer:topology) --- 项目域:项目勘察(project:survey) / 项目方案(project:plan) / 合同管理(project:contract) --- 项目验收(project:acceptance) / 回访记录(project:visit) +-- 初始菜单(项目域子菜单 + 客户扩展子菜单,id:24-31 + 空间拓扑 32) +-- 项目域:客户拜访(customer:visit) / 项目勘察(project:survey) / 项目方案(project:plan) +-- 合同管理(project:contract) / 项目验收(project:acceptance) / 回访记录(project:visit) +-- 客户域:空间拓扑与用电架构(customer:topology) +-- 注:客户拜访归属“项目管理”,但仍沿用 /customer/visit 路径与 customer:visit 权限码 INSERT INTO sys_menu (id, parent_id, menu_name, menu_path, component, icon, sort_order, menu_type, permission, visible) VALUES -(24, 1, '客户拜访', '/customer/visit', 'customer/visit/index', 'AccountBookOutlined', 4, 2, 'customer:visit', 1), -(27, 2, '项目勘察', '/project/survey', 'project/survey/index', 'CarryOutOutlined', 1, 2, 'project:survey', 1), -(28, 2, '项目方案', '/project/plan', 'project/plan/index', 'FileSearchOutlined', 2, 2, 'project:plan', 1), -(29, 2, '合同管理', '/project/contract', 'project/contract/index', 'FileDoneOutlined', 3, 2, 'project:contract', 1), -(30, 2, '项目验收', '/project/acceptance', 'project/acceptance/index', 'ThunderboltOutlined', 4, 2, 'project:acceptance',1), -(31, 2, '回访记录', '/project/visit', 'project/visit/index', 'AuditOutlined', 5, 2, 'project:visit', 1), -(32, 1, '空间拓扑与用电架构', '/customer/topology', 'customer/topology/index', 'ApartmentOutlined', 5, 2, 'customer:topology',1) +(24, 2, '客户拜访', '/customer/visit', 'customer/visit/index', 'xt-jihuabaifang', 1, 2, 'customer:visit', 1), +(27, 2, '项目勘察', '/project/survey', 'project/survey/index', 'xt-xiangmukancha', 2, 2, 'project:survey', 1), +(28, 2, '项目方案', '/project/plan', 'project/plan/index', 'xt-xiangmufangan', 3, 2, 'project:plan', 1), +(29, 2, '合同管理', '/project/contract', 'project/contract/index', 'FileDoneOutlined', 4, 2, 'project:contract', 1), +(30, 2, '项目验收', '/project/acceptance', 'project/acceptance/index', 'ThunderboltOutlined', 5, 2, 'project:acceptance',1), +(31, 2, '回访记录', '/project/visit', 'project/visit/index', 'AuditOutlined', 6, 2, 'project:visit', 1), +(32, 1, '空间拓扑与用电架构', '/customer/topology', 'customer/topology/index', 'xt-topology', 5, 2, 'customer:topology',1) ON CONFLICT (id) DO UPDATE SET parent_id = EXCLUDED.parent_id, menu_name = EXCLUDED.menu_name, diff --git a/database/migrations/20260911_menu_iconfont.sql b/database/migrations/20260911_menu_iconfont.sql new file mode 100644 index 0000000..cc2dcdf --- /dev/null +++ b/database/migrations/20260911_menu_iconfont.sql @@ -0,0 +1,31 @@ +-- ============================================================ +-- 增量脚本:导航菜单图标切换为 iconfont 自定义图标 +-- 日期:2026-09-11 +-- 数据库:PostgreSQL 15+ (xintong) +-- 说明:将 5 个业务菜单的 sys_menu.icon 由 ant-design 图标名 +-- 改为前端 iconfont 类名(xt- 前缀),前端据此渲染字体图标。 +-- 图标源自 client/src/assets/fonts/iconfont(css_prefix_text = xt-)。 +-- 幂等脚本,可重复执行。 +-- +-- 用法: +-- psql -U postgres -d xintong -f 20260911_menu_iconfont.sql +-- ============================================================ + +BEGIN; + +UPDATE sys_menu SET icon = 'xt-xiaochengxu' WHERE menu_path = '/customer/account'; -- 客户小程序授权管理 +UPDATE sys_menu SET icon = 'xt-jihuabaifang' WHERE menu_path = '/customer/visit'; -- 客户拜访 +UPDATE sys_menu SET icon = 'xt-topology' WHERE menu_path = '/customer/topology'; -- 空间拓扑与用电架构 +UPDATE sys_menu SET icon = 'xt-xiangmukancha' WHERE menu_path = '/project/survey'; -- 项目勘察 +UPDATE sys_menu SET icon = 'xt-xiangmufangan' WHERE menu_path = '/project/plan'; -- 项目方案 + +COMMIT; + +-- ============================================================ +-- 验证查询(可选) +-- ============================================================ +SELECT id, menu_name, menu_path, icon +FROM sys_menu +WHERE menu_path IN ('/customer/account', '/customer/visit', '/customer/topology', + '/project/survey', '/project/plan') +ORDER BY id; diff --git a/database/migrations/20260911_move_customer_visit_to_project.sql b/database/migrations/20260911_move_customer_visit_to_project.sql new file mode 100644 index 0000000..78001bb --- /dev/null +++ b/database/migrations/20260911_move_customer_visit_to_project.sql @@ -0,0 +1,16 @@ +-- 把“客户拜访”从“客户管理”移动到“项目管理”,并调整为项目管理下的第 1 个菜单 +-- 说明:仅调整归属(parent_id)与排序(sort_order), +-- menu_path(/customer/visit)、component(customer/visit/index)、permission(customer:visit) 保持不变 +BEGIN; + +-- 客户拜访改挂到项目管理(parent_id=2),并排在项目勘察之前 +UPDATE sys_menu SET parent_id = 2, sort_order = 1 WHERE menu_path = '/customer/visit'; + +-- 项目管理下原有子菜单顺序后移,保持连续 +UPDATE sys_menu SET sort_order = 2 WHERE menu_path = '/project/survey'; -- 项目勘察 +UPDATE sys_menu SET sort_order = 3 WHERE menu_path = '/project/plan'; -- 项目方案 +UPDATE sys_menu SET sort_order = 4 WHERE menu_path = '/project/contract'; -- 合同管理 +UPDATE sys_menu SET sort_order = 5 WHERE menu_path = '/project/acceptance'; -- 项目验收 +UPDATE sys_menu SET sort_order = 6 WHERE menu_path = '/project/visit'; -- 回访记录 + +COMMIT;