From a97d9223b6a8247c593f8be72b19bb644a13f5a4 Mon Sep 17 00:00:00 2001 From: hwj Date: Fri, 15 May 2026 15:04:55 +0800 Subject: [PATCH] =?UTF-8?q?feat=EF=BC=9A=E6=8A=A5=E8=A1=A8/=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E8=8F=9C=E5=8D=95=E5=8A=A8=E6=80=81=E6=B8=B2=E6=9F=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.vue | 11 +- src/api/login.js | 5 +- src/components/common/PermissionMenuPage.vue | 443 +++++++++++++ src/locales/index.js | 20 +- src/main.js | 7 + src/pages.json | 30 +- src/pages/report.vue | 359 +---------- src/pages/work.vue | 623 +------------------ src/store/modules/user.ts | 6 +- src/utils/navigationBar.js | 48 ++ src/utils/permissionMenu.js | 183 ++++++ 11 files changed, 744 insertions(+), 991 deletions(-) create mode 100644 src/components/common/PermissionMenuPage.vue create mode 100644 src/utils/navigationBar.js create mode 100644 src/utils/permissionMenu.js diff --git a/src/App.vue b/src/App.vue index bad79e8..793ed2d 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1,7 +1,8 @@ + + \ No newline at end of file diff --git a/src/locales/index.js b/src/locales/index.js index 67f0f05..9f37713 100644 --- a/src/locales/index.js +++ b/src/locales/index.js @@ -1,6 +1,8 @@ import { createI18n } from 'vue-i18n' import zhCN from './zh-CN' import enUS from './en-US' +import useUserStore from '@/store/modules/user' +import { syncTabBarMenus } from '@/utils/permissionMenu' const LOCALE_STORAGE_KEY = 'app_locale' const LOCALE_CHANGE_EVENT = 'app-locale-changed' @@ -83,7 +85,7 @@ const literalMap = { '点检记录详情': 'moldWorkOrder.detailTitle' } -function applyTabBarLanguage() { +export function applyTabBarLanguage() { try { const pages = getCurrentPages() if (!pages || pages.length === 0) return @@ -93,17 +95,11 @@ function applyTabBarLanguage() { const tabBarPages = ['pages/index', 'pages/report', 'pages/work', 'pages/mine'] if (!tabBarPages.includes(route)) return - const labels = [ - i18n.global.t('tab.home'), - i18n.global.t('tab.report'), - i18n.global.t('tab.work'), - i18n.global.t('tab.mine') - ] - labels.forEach((text, index) => { - uni.setTabBarItem({ - index, - text - }) + syncTabBarMenus(useUserStore().menus, { + homeText: i18n.global.t('tab.home'), + reportFallback: i18n.global.t('tab.report'), + workFallback: i18n.global.t('tab.work'), + mineText: i18n.global.t('tab.mine') }) } catch (e) { } diff --git a/src/main.js b/src/main.js index 4555b84..7cf7f96 100644 --- a/src/main.js +++ b/src/main.js @@ -11,6 +11,7 @@ import { translateLiteral } from '@/locales' import { useDict } from '@/utils/dict' import { parseTime, resetForm, addDateRange, handleTree, selectDictLabel, selectDictLabels } from '@/utils/ruoyi' +import { applyNavigationBarTheme } from '@/utils/navigationBar' @@ -32,6 +33,12 @@ export function createApp() { app.config.globalProperties.selectDictLabels = selectDictLabels app.config.globalProperties.$tl = translateLiteral + app.mixin({ + onShow() { + applyNavigationBarTheme() + } + }) + return { app } diff --git a/src/pages.json b/src/pages.json index e7529bd..05b6bd4 100644 --- a/src/pages.json +++ b/src/pages.json @@ -16,46 +16,60 @@ "path": "pages/index", "style": { "navigationBarTitleText": "首页", - "enablePullDownRefresh": false + "enablePullDownRefresh": false, + "navigationBarBackgroundColor": "#22486e", + "navigationBarTextStyle": "white" } }, { "path": "pages/login", "style": { - "navigationBarTitleText": "登录" + "navigationBarTitleText": "登录", + "navigationBarBackgroundColor": "#ffffff", + "navigationBarTextStyle": "black" } }, { "path": "pages/work", "style": { "navigationBarTitleText": "管理", - "enablePullDownRefresh": false + "enablePullDownRefresh": false, + "navigationBarBackgroundColor": "#22486e", + "navigationBarTextStyle": "white" } }, { "path": "pages/mine", "style": { - "navigationBarTitleText": "个人中心" + "navigationBarTitleText": "个人中心", + "navigationBarBackgroundColor": "#22486e", + "navigationBarTextStyle": "white" } }, { "path": "pages/common/webview/index", "style": { - "navigationBarTitleText": "浏览网页" + "navigationBarTitleText": "浏览网页", + "navigationBarBackgroundColor": "#22486e", + "navigationBarTextStyle": "white" } }, { "path": "pages/common/textview/index", "style": { - "navigationBarTitleText": "浏览文本" + "navigationBarTitleText": "浏览文本", + "navigationBarBackgroundColor": "#22486e", + "navigationBarTextStyle": "white" } }, { "path": "pages/report", "style": { "navigationBarTitleText": "报表", - "enablePullDownRefresh": false + "enablePullDownRefresh": false, + "navigationBarBackgroundColor": "#22486e", + "navigationBarTextStyle": "white" } } ], @@ -639,6 +653,6 @@ "globalStyle": { "navigationBarTextStyle": "white", "navigationBarTitleText": "BESURE", - "navigationBarBackgroundColor": "#1a3a5c" + "navigationBarBackgroundColor": "#22486e" } } diff --git a/src/pages/report.vue b/src/pages/report.vue index 5b461da..f429bc6 100644 --- a/src/pages/report.vue +++ b/src/pages/report.vue @@ -1,357 +1,12 @@ - - diff --git a/src/pages/work.vue b/src/pages/work.vue index 1e70d74..1682fb9 100644 --- a/src/pages/work.vue +++ b/src/pages/work.vue @@ -1,619 +1,14 @@ - - diff --git a/src/store/modules/user.ts b/src/store/modules/user.ts index b7f17bc..e79594f 100644 --- a/src/store/modules/user.ts +++ b/src/store/modules/user.ts @@ -3,6 +3,7 @@ import { getToken, setToken, removeToken } from "@/utils/auth"; import defAva from "@/static/images/profile.jpg"; import { defineStore } from "pinia"; import { initAllDict } from "@/utils/dict"; +import { syncTabBarMenus } from "@/utils/permissionMenu"; export interface LoginForm { username: string; @@ -56,13 +57,15 @@ const useUserStore = defineStore("user", { // const avatar = // user.avatar == "" || user.avatar == null ? defAva : import.meta.env.VITE_APP_BASE_API + user.avatar; const avatar = user.avatar == "" || user.avatar == null ? defAva : user.avatar; - if (res.roles && res.roles.length > 0) { + if (res.data.roles && res.data.roles.length > 0) { // 验证返回的roles是否是一个非空数组 this.roles = res.data.roles; } else { this.roles = ["ROLE_DEFAULT"]; } this.permissions = res.data.permissions; + this.menus = Array.isArray(res.data.menus) ? res.data.menus : []; + syncTabBarMenus(this.menus); this.name = user.nickname; this.userId = user.id; this.avatar = avatar; @@ -82,6 +85,7 @@ const useUserStore = defineStore("user", { this.token = ""; this.roles = []; this.permissions = []; + this.menus = []; this.name = ""; this.avatar = ""; removeToken(); diff --git a/src/utils/navigationBar.js b/src/utils/navigationBar.js new file mode 100644 index 0000000..fcd2eb9 --- /dev/null +++ b/src/utils/navigationBar.js @@ -0,0 +1,48 @@ +const DEFAULT_NAV_BACKGROUND = '#22486e' +const DEFAULT_NAV_FRONT = '#ffffff' +const LOGIN_NAV_BACKGROUND = '#ffffff' +const LOGIN_NAV_FRONT = '#000000' + +function getCurrentRoute() { + try { + const pages = getCurrentPages() + if (!pages || pages.length === 0) return '' + return pages[pages.length - 1]?.route || '' + } catch (error) { + return '' + } +} + +function getNavigationTheme(route = '') { + const currentRoute = route || getCurrentRoute() + if (currentRoute === 'pages/login') { + return { + frontColor: LOGIN_NAV_FRONT, + backgroundColor: LOGIN_NAV_BACKGROUND + } + } + + return { + frontColor: DEFAULT_NAV_FRONT, + backgroundColor: DEFAULT_NAV_BACKGROUND + } +} + +export function applyNavigationBarTheme(route = '') { + const theme = getNavigationTheme(route) + try { + uni.setNavigationBarColor({ + frontColor: theme.frontColor, + backgroundColor: theme.backgroundColor, + animation: { + duration: 0, + timingFunc: 'linear' + } + }) + } catch (error) { + } +} + +export function getDefaultNavigationBackground() { + return DEFAULT_NAV_BACKGROUND +} \ No newline at end of file diff --git a/src/utils/permissionMenu.js b/src/utils/permissionMenu.js new file mode 100644 index 0000000..94ef3de --- /dev/null +++ b/src/utils/permissionMenu.js @@ -0,0 +1,183 @@ +const DIRECT_ROUTE_PREFIXES = ['pages/', 'page_', 'pages_'] + +const MENU_ROUTE_MAP = { + '生产任务': '/pages_function/pages/taskList/index', + tasklist: '/pages_function/pages/taskList/index', + '生产计划': '/pages_function/pages/planList/index', + planlist: '/pages_function/pages/planList/index', + '生产投料': '/page_record/feedingRecordForm', + feedingrecordform: '/page_record/feedingRecordForm', + '生产报工': '/page_report/reportForm', + reportform: '/page_report/reportForm', + '代报工': '/page_report/replaceForm', + replaceform: '/page_report/replaceForm', + '计划进度': '/page_report/planProgress', + planprogress: '/page_report/planProgress', + '仓库信息': '/pages_function/pages/warehouse/index', + warehouse: '/pages_function/pages/warehouse/index', + '检验类型': '/pages_function/pages/inspection/index', + inspection: '/pages_function/pages/inspection/index', + '检验项库': '/pages_function/pages/inspectionItem/index', + inspectionitem: '/pages_function/pages/inspectionItem/index', + moldinspectionitems: '/pages_function/pages/moldInspectionItems/index', + '检验模板': '/pages_function/pages/inspectionTemplate/index', + inspectiontemplate: '/pages_function/pages/inspectionTemplate/index', + moldinspectionplan: '/pages_function/pages/moldInspectionPlan/index', + '点检模板': '/pages_function/pages/moldInspectionPlan/index', + '点检任务': '/pages_function/pages/moldTaskConfiguration/index', + moldtaskconfiguration: '/pages_function/pages/moldTaskConfiguration/index', + moldtaskconfig: '/pages_function/pages/moldTaskConfiguration/index', + '点检记录': '/pages_function/pages/moldWorkOrderInquiry/index', + moldworkorderinquiry: '/pages_function/pages/moldWorkOrderInquiry/index', + '产品物料分类': '/pages_function/pages/materialCategory/index', + materialcategory: '/pages_function/pages/materialCategory/index', + '产品物料信息': '/pages_function/pages/materialInfo/index', + materialinfo: '/pages_function/pages/materialInfo/index', + '产品BOM': '/pages_function/pages/productBom/index', + productbom: '/pages_function/pages/productBom/index', + '设备分类': '/pages_function/pages/equipmentCategory/index', + equipmentcategory: '/pages_function/pages/equipmentCategory/index', + '设备台账': '/pages_function/pages/equipmentLedger/index', + equipmentledger: '/pages_function/pages/equipmentLedger/index', + '设备关键件': '/pages_function/pages/criticalComponent/index', + criticalcomponent: '/pages_function/pages/criticalComponent/index', + equipmentkeypart: '/pages_function/pages/equipmentKeypart/index', + '模具类型': '/pages_function/pages/moldType/index', + moldtype: '/pages_function/pages/moldType/index', + '模具台账': '/pages_function/pages/moldLedger/index', + moldledger: '/pages_function/pages/moldLedger/index', + moldget: '/pages_function/pages/moldget/index', + moldreturn: '/pages_function/pages/moldreturn/index', + moldoperate: '/pages_function/pages/moldoperate/index', + mold: '/pages_function/pages/mold/index', + equipment: '/pages_function/pages/equipment/index', + spare: '/pages_function/pages/spare/index', + keypart: '/pages_function/pages/keypart/index', + product: '/pages_function/pages/product/index' +} + +const HOME_KEYWORDS = ['首页', 'home', 'index', 'dashboard'] +const MODULE_COLORS = ['#1a3a5c', '#2d5a87', '#3d7ab5', '#ff8c00', '#18bc37', '#5aa0d2'] +const MENU_SYMBOLS = ['●', '■', '▲', '◆', '★', '▣'] + +function toArray(value) { + return Array.isArray(value) ? value : [] +} + +function normalizeMenuKey(value) { + return String(value || '') + .trim() + .toLowerCase() + .replace(/\/index$/i, '') + .replace(/[^a-z0-9\u4e00-\u9fa5]/g, '') +} + +function getDirectRoute(value) { + const route = String(value || '').trim().replace(/^\/+/, '') + if (!route) return '' + return DIRECT_ROUTE_PREFIXES.some((prefix) => route.startsWith(prefix)) ? `/${route}` : '' +} + +function looksLikeHomeMenu(menu) { + const fields = [menu.name, menu.enName, menu.path, menu.component] + .map((item) => String(item || '').toLowerCase()) + .join('|') + return HOME_KEYWORDS.some((keyword) => fields.includes(keyword)) +} + +function wrapGroupEntries(group) { + const directChildren = toArray(group.children) + const hasNestedEntries = directChildren.some((child) => toArray(child.children).length > 0) + if (hasNestedEntries) { + return directChildren + .map((child) => ({ + ...child, + children: toArray(child.children) + })) + .filter((child) => child.children.length > 0) + } + if (directChildren.length === 0) { + return [] + } + return [ + { + id: `${group.id || group.name || 'group'}-entries`, + name: group.name, + children: directChildren + } + ] +} + +export function getTopLevelMenus(menus) { + return toArray(menus).filter((menu) => menu && typeof menu === 'object') +} + +export function getDynamicTabMenus(menus) { + return getTopLevelMenus(menus).filter((menu) => !looksLikeHomeMenu(menu)) +} + +export function findTabMenuByPage(menus, pagePath) { + const dynamicMenus = getDynamicTabMenus(menus) + if (pagePath === 'pages/report') { + return dynamicMenus[0] || null + } + if (pagePath === 'pages/work') { + return dynamicMenus[1] || dynamicMenus[0] || null + } + return null +} + +export function buildPageModules(tabMenu) { + return toArray(tabMenu?.children) + .map((module) => ({ + ...module, + groups: wrapGroupEntries(module) + })) + .filter((module) => module.groups.length > 0) +} + +export function resolveMenuUrl(menu) { + const directRoute = getDirectRoute(menu?.path) || getDirectRoute(menu?.component) + if (directRoute) { + return directRoute + } + + const keys = [menu?.component, menu?.path, menu?.enName, menu?.name] + for (const key of keys) { + const normalizedKey = normalizeMenuKey(key) + if (normalizedKey && MENU_ROUTE_MAP[normalizedKey]) { + return MENU_ROUTE_MAP[normalizedKey] + } + } + + return '' +} + +export function getModuleColor(index) { + return MODULE_COLORS[index % MODULE_COLORS.length] +} + +export function getMenuSymbol(name, index) { + const trimmed = String(name || '').trim() + if (trimmed) { + return trimmed.slice(0, 1) + } + return MENU_SYMBOLS[index % MENU_SYMBOLS.length] +} + +export function syncTabBarMenus(menus, options = {}) { + const dynamicMenus = getDynamicTabMenus(menus) + const labels = [ + options.homeText || '首页', + dynamicMenus[0]?.name || options.reportFallback || '报表', + dynamicMenus[1]?.name || dynamicMenus[0]?.name || options.workFallback || '管理', + options.mineText || '我的' + ] + + labels.forEach((text, index) => { + uni.setTabBarItem({ + index, + text + }) + }) +} \ No newline at end of file