| | |
| | | </el-avatar> |
| | | <div class="welcome-text"> |
| | | <div class="welcome-title"> |
| | | 晚上好,{{ userStore.nickName || userStore.name || "超级管理员" }} 👋 |
| | | {{ greetingText }},{{ userStore.nickName || userStore.name || "超级管理员" }} 👋 |
| | | </div> |
| | | <div class="welcome-subtitle">专注工业数字化,助力智造升级</div> |
| | | </div> |
| | | </div> |
| | | <div class="welcome-meta"> |
| | | <div class="meta-time digital-number">{{ nowTime }}</div> |
| | | <div class="meta-extra"> |
| | | <span class="meta-date">{{ nowDate }}</span> |
| | | <span class="meta-weather">{{ weatherText }}</span> |
| | | </div> |
| | | <div class="meta-tip">MES / MOM 生产运营驾驶舱</div> |
| | | </div> |
| | | </section> |
| | |
| | | UserFilled, |
| | | } from "@element-plus/icons-vue"; |
| | | import Echarts from "@/components/Echarts/echarts.vue"; |
| | | import usePermissionStore from "@/store/modules/permission"; |
| | | import useUserStore from "@/store/modules/user.js"; |
| | | import { |
| | | analysisCustomerContractAmounts, |
| | |
| | | |
| | | const router = useRouter(); |
| | | const userStore = useUserStore(); |
| | | const permissionStore = usePermissionStore(); |
| | | const defaultWelcomeAvatar = new URL("../assets/images/profile.jpg", import.meta.url).href; |
| | | |
| | | const nowTime = ref(""); |
| | |
| | | const weatherText = "多云 28°C"; |
| | | |
| | | const nowDate = computed(() => (nowTime.value ? nowTime.value.slice(0, 10) : dayjs().format("YYYY-MM-DD"))); |
| | | |
| | | const greetingText = computed(() => { |
| | | const hour = dayjs().hour(); |
| | | if (hour < 6) return "凌晨好"; |
| | | if (hour < 9) return "早上好"; |
| | | if (hour < 12) return "上午好"; |
| | | if (hour < 14) return "中午好"; |
| | | if (hour < 18) return "下午好"; |
| | | return "晚上好"; |
| | | }); |
| | | |
| | | const welcomeAvatar = computed(() => |
| | | welcomeAvatarLoadFailed.value || !userStore.avatar ? defaultWelcomeAvatar : userStore.avatar |
| | |
| | | }, |
| | | ]); |
| | | |
| | | const quickEntries = [ |
| | | { label: "生产排产", icon: Calendar, path: "/productionManagement/productionDispatching" }, |
| | | { label: "工序报工", icon: EditPen, path: "/productionManagement/productionReporting" }, |
| | | { label: "生产订单", icon: Tickets, path: "/productionManagement/productionOrder" }, |
| | | { label: "物料齐套", icon: Box, path: "/productionManagement/workOrderManagement" }, |
| | | { label: "质量检验", icon: Checked, path: "/qualityManagement/processInspection" }, |
| | | { label: "设备管理", icon: Tools, path: "/equipmentManagement/deviceInfo" }, |
| | | { label: "库存查询", icon: Search, path: "/inventoryManagement/stockManage" }, |
| | | { label: "数据报表", icon: DataAnalysis, path: "/reportAnalysis/reportManagement" }, |
| | | const quickEntryConfigs = [ |
| | | { label: "主生产计划", icon: Calendar, titles: ["主生产计划"], fallbackPath: "/productionPlan/productionPlan" }, |
| | | { label: "生产订单", icon: Tickets, titles: ["生产订单"], fallbackPath: "/productionManagement/productionOrder" }, |
| | | { label: "生产报工", icon: EditPen, titles: ["生产报工"], fallbackPath: "/productionManagement/productionReporting" }, |
| | | { label: "设备台账", icon: Tools, titles: ["设备台账"], fallbackPath: "/equipmentManagement/ledger" }, |
| | | { label: "销售台账", icon: DataLine, titles: ["销售台账"], fallbackPath: "/salesManagement/salesLedger" }, |
| | | { label: "采购台账", icon: ShoppingCartFull, titles: ["采购台账"], fallbackPath: "/procurementManagement/procurementLedger" }, |
| | | { label: "员工台账", icon: UserFilled, titles: ["员工台账", "在职员工台账"], fallbackPath: "/personnelManagement/employeeRecord" }, |
| | | { label: "库存管理", icon: Box, titles: ["库存管理"], fallbackPath: "/inventoryManagement/stockManage" }, |
| | | ]; |
| | | |
| | | const normalizeMenuTitle = (title) => String(title || "").replace(/\s+/g, "").trim(); |
| | | |
| | | const resolveRoutePath = (route, parentPath = "") => { |
| | | const currentPath = String(route?.path || "").trim(); |
| | | if (!currentPath) return parentPath || ""; |
| | | if (/^(https?:)?\/\//.test(currentPath)) return currentPath; |
| | | if (currentPath.startsWith("/")) return currentPath; |
| | | const basePath = parentPath && parentPath !== "/" ? parentPath.replace(/\/$/, "") : ""; |
| | | return `${basePath}/${currentPath}`.replace(/\/+/g, "/"); |
| | | }; |
| | | |
| | | const collectAccessibleRoutes = (routes = [], parentPath = "") => { |
| | | const items = []; |
| | | (routes || []).forEach((route) => { |
| | | if (!route) return; |
| | | const fullPath = resolveRoutePath(route, parentPath); |
| | | const title = route.meta?.title ?? route.title ?? ""; |
| | | if (title && fullPath && !String(route.redirect || "").includes("noRedirect")) { |
| | | items.push({ |
| | | title: normalizeMenuTitle(title), |
| | | path: fullPath, |
| | | }); |
| | | } |
| | | if (Array.isArray(route.children) && route.children.length > 0) { |
| | | items.push(...collectAccessibleRoutes(route.children, fullPath)); |
| | | } |
| | | }); |
| | | return items; |
| | | }; |
| | | |
| | | const accessibleMenuRoutes = computed(() => { |
| | | const routePool = |
| | | permissionStore.defaultRoutes?.length > 0 |
| | | ? permissionStore.defaultRoutes |
| | | : permissionStore.sidebarRouters?.length > 0 |
| | | ? permissionStore.sidebarRouters |
| | | : permissionStore.routes; |
| | | return collectAccessibleRoutes(routePool || []); |
| | | }); |
| | | |
| | | const quickEntries = computed(() => |
| | | quickEntryConfigs |
| | | .map((item) => { |
| | | const targetRoute = accessibleMenuRoutes.value.find((route) => |
| | | item.titles.some((title) => route.title === normalizeMenuTitle(title)) |
| | | ); |
| | | const resolvedPath = targetRoute?.path || ""; |
| | | return resolvedPath |
| | | ? { |
| | | label: item.label, |
| | | icon: item.icon, |
| | | path: resolvedPath, |
| | | } |
| | | : null; |
| | | }) |
| | | .filter(Boolean) |
| | | ); |
| | | |
| | | const updateNowTime = () => { |
| | | nowTime.value = dayjs().format("YYYY-MM-DD HH:mm:ss"); |
| | |
| | | }; |
| | | |
| | | const goToQuick = (path) => { |
| | | if (!path) return; |
| | | router.push(path).catch(() => {}); |
| | | }; |
| | | |
| | |
| | | flex-direction: column; |
| | | gap: 16px; |
| | | overflow-x: clip; |
| | | margin-top: 10px; |
| | | } |
| | | |
| | | .digital-number { |