完善页面
This commit is contained in:
@@ -5,10 +5,7 @@
|
||||
>
|
||||
<view class="aside-header">
|
||||
<view class="logo">
|
||||
<text class="logo-text">{{ collapsed ? '商' : '商城后台' }}</text>
|
||||
</view>
|
||||
<view class="collapse-btn" @click="$emit('toggle')">
|
||||
<text class="collapse-text">{{ collapsed ? '›' : '‹' }}</text>
|
||||
<text class="logo-text"> 商城后台</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@@ -38,7 +35,6 @@ defineProps<{
|
||||
}>()
|
||||
|
||||
defineEmits<{
|
||||
(e:'toggle'): void
|
||||
(e:'menu-click', menuId: string): void
|
||||
}>()
|
||||
</script>
|
||||
@@ -58,12 +54,21 @@ defineEmits<{
|
||||
.aside-header{
|
||||
height: 56px;
|
||||
display:flex;
|
||||
flex-direction:row;
|
||||
align-items:center;
|
||||
justify-content: space-between;
|
||||
padding: 0 12px;
|
||||
border-bottom: 1px solid rgba(255,255,255,0.08);
|
||||
}
|
||||
.logo-text{ color:#fff; font-size:14px; font-weight:600; }
|
||||
.logo-text{
|
||||
color:#fff;
|
||||
font-size:20px;
|
||||
font-weight:600;
|
||||
display:flex;
|
||||
justify-content:center;
|
||||
align-items:center;
|
||||
text-align:center;
|
||||
}
|
||||
.collapse-btn{ width:28px; height:28px; display:flex; align-items:center; justify-content:center; }
|
||||
.collapse-text{ color:rgba(255,255,255,0.7); }
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<template>
|
||||
<view
|
||||
class="sub-sider"
|
||||
v-if="groups && groups.length > 0"
|
||||
:style="{ left: props.asideWidth + 'px', width: props.siderWidth + 'px' }"
|
||||
>
|
||||
<view class="sub-header">
|
||||
@@ -14,14 +13,22 @@
|
||||
:key="groupKey(g)"
|
||||
class="group"
|
||||
>
|
||||
<view class="group-title" @click="toggleGroup(groupKey(g))">
|
||||
<!-- ✅ 改:点击标题时,如果该组没有 children 但有 path,则当成“叶子菜单”直接跳 -->
|
||||
<view class="group-title" @click="handleGroupTitleClick(g)">
|
||||
<text class="group-title-text">{{ g.title }}</text>
|
||||
<text class="group-arrow" :class="{ open: isGroupOpen(groupKey(g)) }">v</text>
|
||||
|
||||
<!-- ✅ 改:只有有 children 才显示箭头 -->
|
||||
<text
|
||||
v-if="groupChildren(g).length > 0"
|
||||
class="group-arrow"
|
||||
:class="{ open: isGroupOpen(groupKey(g)) }"
|
||||
>v</text>
|
||||
</view>
|
||||
|
||||
<!-- ✅ 改:v-show 的条件不变,但 children 的遍历必须兜底 -->
|
||||
<view v-show="isGroupOpen(groupKey(g))" class="group-children">
|
||||
<view
|
||||
v-for="c in g.children"
|
||||
v-for="c in groupChildren(g)"
|
||||
:key="c.id"
|
||||
class="sub-item"
|
||||
:class="{ active: resolvedActiveId === c.id }"
|
||||
@@ -38,16 +45,19 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="uts">
|
||||
import { ref, computed, watch } from 'vue'
|
||||
import { ref, computed, watch, withDefaults } from 'vue'
|
||||
import type { MenuGroup, MenuChild } from '../types.uts'
|
||||
|
||||
const props = defineProps<{
|
||||
const props = withDefaults(defineProps<{
|
||||
activeMenuTitle: string
|
||||
groups: MenuGroup[]
|
||||
activeSubId: string
|
||||
activeMenuId?: string | null
|
||||
asideWidth: number
|
||||
siderWidth: number
|
||||
}>()
|
||||
}>(), {
|
||||
activeMenuId: 'home',
|
||||
})
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'sub-click', child: MenuChild): void
|
||||
@@ -58,33 +68,60 @@ const openGroupKey = ref<string>('')
|
||||
|
||||
/** 给 group 一个稳定 key:优先用 id(如果你 types 里没有 id,就用 title) */
|
||||
const groupKey = (g: MenuGroup): string => {
|
||||
// @ts-ignore - 允许 g.id 可选
|
||||
// @ts-ignore
|
||||
const anyG: any = g as any
|
||||
return (anyG.id && (anyG.id as string)) ? (anyG.id as string) : g.title
|
||||
}
|
||||
|
||||
/** ✅ 核心:group.children 兜底成 [](因为 children 现在可选/可空) */
|
||||
const groupChildren = (g: MenuGroup): MenuChild[] => {
|
||||
// @ts-ignore
|
||||
const anyG: any = g as any
|
||||
// @ts-ignore
|
||||
const list = anyG.children as (MenuChild[] | null | undefined)
|
||||
return list && list.length > 0 ? list : []
|
||||
}
|
||||
|
||||
/** ✅ 兼容四级:child.children 兜底成 [] */
|
||||
const childChildren = (c: MenuChild): MenuChild[] => {
|
||||
// @ts-ignore
|
||||
const anyC: any = c as any
|
||||
// @ts-ignore
|
||||
const list = anyC.children as (MenuChild[] | null | undefined)
|
||||
return list && list.length > 0 ? list : []
|
||||
}
|
||||
|
||||
/** ✅ 叶子 group(无 children)如果有 path,则构造一个“伪 MenuChild”用于 emit 跳转 */
|
||||
const groupAsChild = (g: MenuGroup): MenuChild | null => {
|
||||
// @ts-ignore
|
||||
const anyG: any = g as any
|
||||
// @ts-ignore
|
||||
const p = anyG.path as (string | null | undefined)
|
||||
if (!p) return null
|
||||
return { id: groupKey(g), title: g.title, path: p } as MenuChild
|
||||
}
|
||||
|
||||
const normalizePath = (p: string): string => {
|
||||
if (!p) return ''
|
||||
// 统一去掉开头 /
|
||||
const s = p.startsWith('/') ? p.slice(1) : p
|
||||
// 去掉 query
|
||||
const q = s.indexOf('?')
|
||||
return q >= 0 ? s.slice(0, q) : s
|
||||
}
|
||||
|
||||
/** 扁平查找:id -> child */
|
||||
/** 扁平查找:id -> child(✅ 改:遍历时用 groupChildren/childChildren) */
|
||||
const findChildById = (id: string): MenuChild | null => {
|
||||
if (!id) return null
|
||||
|
||||
for (const g of props.groups) {
|
||||
for (const c of g.children) {
|
||||
// ✅ 叶子 group 也能命中
|
||||
const gLeaf = groupAsChild(g)
|
||||
if (gLeaf && gLeaf.id === id) return gLeaf
|
||||
|
||||
for (const c of groupChildren(g)) {
|
||||
if (c.id === id) return c
|
||||
// 兼容未来有更深层 children(递归)
|
||||
// @ts-ignore
|
||||
const anyC: any = c as any
|
||||
// @ts-ignore
|
||||
if (anyC.children && (anyC.children as MenuChild[]).length > 0) {
|
||||
// @ts-ignore
|
||||
const hit = findFirstMatchInChildren(id, anyC.children as MenuChild[])
|
||||
const deep = childChildren(c)
|
||||
if (deep.length > 0) {
|
||||
const hit = findFirstMatchInChildren(id, deep)
|
||||
if (hit) return hit
|
||||
}
|
||||
}
|
||||
@@ -92,16 +129,12 @@ const findChildById = (id: string): MenuChild | null => {
|
||||
return null
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
const findFirstMatchInChildren = (id: string, list: MenuChild[]): MenuChild | null => {
|
||||
for (const n of list) {
|
||||
if (n.id === id) return n
|
||||
// @ts-ignore
|
||||
const anyN: any = n as any
|
||||
// @ts-ignore
|
||||
if (anyN.children && (anyN.children as MenuChild[]).length > 0) {
|
||||
// @ts-ignore
|
||||
const hit = findFirstMatchInChildren(id, anyN.children as MenuChild[])
|
||||
const deep = childChildren(n)
|
||||
if (deep.length > 0) {
|
||||
const hit = findFirstMatchInChildren(id, deep)
|
||||
if (hit) return hit
|
||||
}
|
||||
}
|
||||
@@ -111,16 +144,17 @@ const findFirstMatchInChildren = (id: string, list: MenuChild[]): MenuChild | nu
|
||||
const findChildByPath = (path: string): MenuChild | null => {
|
||||
const target = normalizePath(path)
|
||||
if (!target) return null
|
||||
|
||||
for (const g of props.groups) {
|
||||
for (const c of g.children) {
|
||||
// ✅ 叶子 group 也能按 path 命中
|
||||
const gLeaf = groupAsChild(g)
|
||||
if (gLeaf && normalizePath(gLeaf.path) === target) return gLeaf
|
||||
|
||||
for (const c of groupChildren(g)) {
|
||||
if (normalizePath(c.path) === target) return c
|
||||
// 兼容更深层 children(递归)
|
||||
// @ts-ignore
|
||||
const anyC: any = c as any
|
||||
// @ts-ignore
|
||||
if (anyC.children && (anyC.children as MenuChild[]).length > 0) {
|
||||
// @ts-ignore
|
||||
const hit = findFirstMatchByPath(target, anyC.children as MenuChild[])
|
||||
const deep = childChildren(c)
|
||||
if (deep.length > 0) {
|
||||
const hit = findFirstMatchByPath(target, deep)
|
||||
if (hit) return hit
|
||||
}
|
||||
}
|
||||
@@ -128,72 +162,62 @@ const findChildByPath = (path: string): MenuChild | null => {
|
||||
return null
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
const findFirstMatchByPath = (targetNorm: string, list: MenuChild[]): MenuChild | null => {
|
||||
for (const n of list) {
|
||||
if (normalizePath(n.path) === targetNorm) return n
|
||||
// @ts-ignore
|
||||
const anyN: any = n as any
|
||||
// @ts-ignore
|
||||
if (anyN.children && (anyN.children as MenuChild[]).length > 0) {
|
||||
// @ts-ignore
|
||||
const hit = findFirstMatchByPath(targetNorm, anyN.children as MenuChild[])
|
||||
const deep = childChildren(n)
|
||||
if (deep.length > 0) {
|
||||
const hit = findFirstMatchByPath(targetNorm, deep)
|
||||
if (hit) return hit
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
/** 找到某个 child 属于哪个 group,用于自动展开该组 */
|
||||
/** 找到某个 child 属于哪个 group,用于自动展开该组(✅ 改:遍历兜底) */
|
||||
const findGroupKeyByChildId = (id: string): string => {
|
||||
for (const g of props.groups) {
|
||||
for (const c of g.children) {
|
||||
const gLeaf = groupAsChild(g)
|
||||
if (gLeaf && gLeaf.id === id) return groupKey(g)
|
||||
|
||||
for (const c of groupChildren(g)) {
|
||||
if (c.id === id) return groupKey(g)
|
||||
// @ts-ignore
|
||||
const anyC: any = c as any
|
||||
// @ts-ignore
|
||||
if (anyC.children && (anyC.children as MenuChild[]).length > 0) {
|
||||
// 深层命中也算这个 group
|
||||
// @ts-ignore
|
||||
const hit = findFirstMatchInChildren(id, anyC.children as MenuChild[])
|
||||
const deep = childChildren(c)
|
||||
if (deep.length > 0) {
|
||||
const hit = findFirstMatchInChildren(id, deep)
|
||||
if (hit) return groupKey(g)
|
||||
}
|
||||
}
|
||||
}
|
||||
// fallback:第一个 group
|
||||
return props.groups.length > 0 ? groupKey(props.groups[0]) : ''
|
||||
}
|
||||
|
||||
/** 递归取第一个 leaf 菜单:你要求的“默认打开第一个页面(递归)” */
|
||||
/** 递归取第一个 leaf 菜单(✅ 改:children 兜底) */
|
||||
const firstLeaf = (): MenuChild | null => {
|
||||
if (!props.groups || props.groups.length === 0) return null
|
||||
const g0 = props.groups[0]
|
||||
if (!g0.children || g0.children.length === 0) return null
|
||||
const g0Children = groupChildren(g0)
|
||||
|
||||
const c0: any = g0.children[0] as any
|
||||
if (c0.children && (c0.children as MenuChild[]).length > 0) {
|
||||
// @ts-ignore
|
||||
return findFirstLeafInChildren(c0.children as MenuChild[])
|
||||
// ✅ 如果第一个 group 是叶子(无 children 但有 path),也能返回
|
||||
if (g0Children.length === 0) {
|
||||
return groupAsChild(g0)
|
||||
}
|
||||
return g0.children[0]
|
||||
|
||||
const c0 = g0Children[0]
|
||||
const deep = childChildren(c0)
|
||||
if (deep.length > 0) return findFirstLeafInChildren(deep)
|
||||
return c0
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
const findFirstLeafInChildren = (list: MenuChild[]): MenuChild | null => {
|
||||
if (!list || list.length === 0) return null
|
||||
const n: any = list[0] as any
|
||||
if (n.children && (n.children as MenuChild[]).length > 0) {
|
||||
// @ts-ignore
|
||||
return findFirstLeafInChildren(n.children as MenuChild[])
|
||||
}
|
||||
return list[0]
|
||||
const n = list[0]
|
||||
const deep = childChildren(n)
|
||||
if (deep.length > 0) return findFirstLeafInChildren(deep)
|
||||
return n
|
||||
}
|
||||
|
||||
/**
|
||||
* 高亮兜底:如果 activeSubId 没同步(你现在遇到的“点了不高亮”),
|
||||
* 就按当前页面 route/path 再匹配一次。
|
||||
* uni-app-x 的 getCurrentPages() 返回页面对象,page.route 可取当前路由。:contentReference[oaicite:3]{index=3}
|
||||
*/
|
||||
/** 高亮兜底:按 activeSubId -> route/path 再匹配一次 */
|
||||
const resolvedActiveId = computed((): string => {
|
||||
const byId = findChildById(props.activeSubId)
|
||||
if (byId) return byId.id
|
||||
@@ -215,7 +239,6 @@ const resolvedActiveId = computed((): string => {
|
||||
|
||||
const isGroupOpen = (key: string): boolean => {
|
||||
if (!openGroupKey.value) {
|
||||
// 初始默认展开第一个组
|
||||
return props.groups && props.groups.length > 0 ? groupKey(props.groups[0]) === key : false
|
||||
}
|
||||
return openGroupKey.value === key
|
||||
@@ -225,24 +248,37 @@ const toggleGroup = (key: string) => {
|
||||
openGroupKey.value = (openGroupKey.value === key) ? '' : key
|
||||
}
|
||||
|
||||
/** ✅ 新增:点 group 标题时的行为 */
|
||||
const handleGroupTitleClick = (g: MenuGroup) => {
|
||||
const list = groupChildren(g)
|
||||
// 有 children:正常展开/收起
|
||||
if (list.length > 0) {
|
||||
toggleGroup(groupKey(g))
|
||||
return
|
||||
}
|
||||
// 无 children:如果有 path,当成叶子菜单直接跳
|
||||
const leaf = groupAsChild(g)
|
||||
if (leaf) {
|
||||
openGroupKey.value = groupKey(g)
|
||||
emit('sub-click', leaf)
|
||||
}
|
||||
}
|
||||
|
||||
const handleClick = (c: MenuChild) => {
|
||||
// 点击就让该组展开(用户体验更 CRMEB)
|
||||
openGroupKey.value = findGroupKeyByChildId(c.id)
|
||||
emit('sub-click', c)
|
||||
}
|
||||
|
||||
/** 自动:当 groups 变了/activeSubId 无效时,默认跳第一个 leaf(递归)并展开对应 group */
|
||||
/** 自动:groups 变更/activeSubId 无效时,默认跳第一个 leaf 并展开对应 group */
|
||||
const ensureDefault = () => {
|
||||
if (!props.groups || props.groups.length === 0) return
|
||||
|
||||
// activeSubId 有效:展开它所在组
|
||||
const hit = findChildById(props.activeSubId)
|
||||
if (hit) {
|
||||
openGroupKey.value = findGroupKeyByChildId(hit.id)
|
||||
return
|
||||
}
|
||||
|
||||
// activeSubId 无效/空:默认递归选第一个 leaf
|
||||
const first = firstLeaf()
|
||||
if (first) {
|
||||
openGroupKey.value = findGroupKeyByChildId(first.id)
|
||||
@@ -264,6 +300,7 @@ watch(
|
||||
</script>
|
||||
|
||||
<style>
|
||||
/* 原样保留你的样式 */
|
||||
.sub-sider{
|
||||
background:#ffffff;
|
||||
border-right: 1px solid #e5e7eb;
|
||||
@@ -278,20 +315,21 @@ watch(
|
||||
height: 56px;
|
||||
display:flex;
|
||||
align-items:center;
|
||||
padding: 0 16px;
|
||||
border-bottom: 1px solid #eef2f7;
|
||||
}
|
||||
.sub-title{
|
||||
font-size:16px;
|
||||
font-weight:700;
|
||||
color:#111827;
|
||||
display:flex;
|
||||
justify-content:center;
|
||||
align-items:center;
|
||||
}
|
||||
|
||||
.sub-body{
|
||||
height: calc(100vh - 56px);
|
||||
}
|
||||
|
||||
/* CRMEB 风格:分组“块”更明显 */
|
||||
.group{ padding: 8px 0; }
|
||||
|
||||
.group-title{
|
||||
@@ -324,11 +362,10 @@ watch(
|
||||
padding: 0 12px 6px 12px;
|
||||
}
|
||||
|
||||
/* 子菜单:缩进 + 更小高度 */
|
||||
.sub-item{
|
||||
height: 34px;
|
||||
margin: 4px 0;
|
||||
padding: 0 14px 0 28px; /* 体现层级 */
|
||||
padding: 0 14px 0 28px;
|
||||
display:flex;
|
||||
align-items:center;
|
||||
border-radius: 8px;
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
:activeMenuTitle="activeMenuTitle"
|
||||
:groups="activeGroups"
|
||||
:activeSubId="activeSubId"
|
||||
:activeMenuId="activeMenuId || 'home'"
|
||||
:asideWidth="ASIDE_W"
|
||||
:siderWidth="SUB_W"
|
||||
@sub-click="onSubClick"
|
||||
@@ -63,7 +64,7 @@ import AdminFooter from './components/AdminFooter.uvue'
|
||||
import { menuList as menuConst } from './utils/menu.uts'
|
||||
import { findActiveByCurrentPage, getCurrentRoutePath } from './utils/nav.uts'
|
||||
import { makeTabFromPath, upsertTab, removeTab } from './utils/tabs.uts'
|
||||
import type { MenuItem, TabItem } from './types.uts'
|
||||
import type { MenuItem, TabItem , MenuChild } from './types.uts'
|
||||
|
||||
// 你页面传进来的 currentPage:可能是顶级 id,也可能是子页面 id(user-list)
|
||||
const props = defineProps<{ currentPage: string }>()
|
||||
@@ -115,19 +116,24 @@ syncTabsByRoute()
|
||||
// computed
|
||||
const activeMenu = computed(() => menuList.value.find(m => m.id === activeMenuId.value))
|
||||
const activeMenuTitle = computed(() => activeMenu.value?.title || '商城后台')
|
||||
const activeGroups = computed(() => activeMenu.value?.groups || [])
|
||||
const activeGroups = computed(() => {
|
||||
const m = menuList.value.find(it => it.id === activeMenuId.value)
|
||||
return m?.groups ?? [] // ✅ 永远是数组
|
||||
})
|
||||
|
||||
|
||||
const breadcrumb = computed(() => {
|
||||
// “一级 / 二级”
|
||||
let subTitle = ''
|
||||
const groups = activeGroups.value
|
||||
for (const g of groups) {
|
||||
const hit = g.children.find(c => c.id === activeSubId.value)
|
||||
const cs = g.children ?? []
|
||||
const hit = cs.find(c => c.id === activeSubId.value)
|
||||
if (hit) { subTitle = hit.title; break }
|
||||
}
|
||||
return subTitle ? `${activeMenuTitle.value} / ${subTitle}` : `${activeMenuTitle.value}`
|
||||
})
|
||||
|
||||
|
||||
// handlers
|
||||
const toggleCollapse = () => {
|
||||
isCollapsed.value = !isCollapsed.value
|
||||
@@ -153,27 +159,54 @@ const firstLeafOfMenu = (m: MenuItem): MenuChild | null => {
|
||||
return g0.children[0]
|
||||
}
|
||||
|
||||
const go = (url: string) => {
|
||||
// 你明确要用 navigateTo:页面栈会增长(这是正常行为):contentReference[oaicite:4]{index=4}
|
||||
uni.navigateTo({ url })
|
||||
let navigating = false
|
||||
|
||||
const go = async (url?: string | null) => {
|
||||
if (!url || url.length === 0) return
|
||||
if (navigating) return
|
||||
navigating = true
|
||||
try {
|
||||
await uni.navigateTo({ url })
|
||||
} catch (e) {
|
||||
// 可选:忽略取消类报错,避免控制台刷屏
|
||||
} finally {
|
||||
setTimeout(() => { navigating = false }, 80)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const onMenuClick = (menuId: string) => {
|
||||
const m = menuList.value.find(x => x.id === menuId)
|
||||
if (!m) return
|
||||
|
||||
activeMenuId.value = m.id
|
||||
// 默认激活该菜单第一个子项
|
||||
const g0 = (m.groups && m.groups.length > 0) ? m.groups[0] : null
|
||||
const c0 = g0 && g0.children.length > 0 ? g0.children[0] : null
|
||||
activeSubId.value = c0 ? c0.id : ''
|
||||
go(m.path)
|
||||
|
||||
const leaf = firstLeafOfMenu(m)
|
||||
activeSubId.value = leaf ? leaf.id : ''
|
||||
|
||||
// ✅ 优先 leaf.path,其次才考虑 m.path
|
||||
go(leaf?.path ?? m.path ?? '')
|
||||
}
|
||||
|
||||
const firstLeafInChildren = (list?: MenuChild[] | null): MenuChild | null => {
|
||||
const arr = list ?? []
|
||||
if (arr.length === 0) return null
|
||||
const n = arr[0]
|
||||
const deep = n.children ?? []
|
||||
return deep.length > 0 ? firstLeafInChildren(deep) : n
|
||||
}
|
||||
|
||||
const onSubClick = (c: MenuChild) => {
|
||||
activeSubId.value = c.id
|
||||
go(c.path)
|
||||
if (c.path && c.path.length > 0) {
|
||||
go(c.path)
|
||||
} else {
|
||||
const leaf = firstLeafInChildren(c.children)
|
||||
go(leaf?.path ?? '')
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const onTabClick = (tab: TabItem) => {
|
||||
activeTabId.value = tab.id
|
||||
go(tab.path)
|
||||
@@ -216,6 +249,6 @@ const onNotify = () => uni.showToast({ title: '通知', icon: 'none' })
|
||||
height: calc(100vh - 56px - 44px);
|
||||
}
|
||||
.content-inner{
|
||||
padding: 16px;
|
||||
padding:5px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -14,15 +14,26 @@ export type TagItem = {
|
||||
export type MenuChild = {
|
||||
id: string
|
||||
title: string
|
||||
path: string
|
||||
|
||||
// ✅ 目录节点可以没有 path
|
||||
path?: string | null
|
||||
|
||||
// ✅ 允许四级
|
||||
children?: MenuChild[] | null
|
||||
}
|
||||
|
||||
export type MenuGroup = {
|
||||
id?:string
|
||||
id?: string | null
|
||||
title: string
|
||||
children: MenuChild[]
|
||||
|
||||
// ✅ 允许 group 自己也能当叶子直接跳转(可选)
|
||||
path?: string | null
|
||||
|
||||
// ✅ children 允许缺省/为空
|
||||
children?: MenuChild[] | null
|
||||
}
|
||||
|
||||
|
||||
export type MenuItem = {
|
||||
id: string
|
||||
title: string
|
||||
|
||||
@@ -6,7 +6,7 @@ export const menuList: MenuItem[] = [
|
||||
title: '首页',
|
||||
icon: '/static/homepage.svg',
|
||||
path: '/pages/mall/admin/homePage/index',
|
||||
groups: [],
|
||||
groups: []
|
||||
},
|
||||
{
|
||||
id: 'user',
|
||||
@@ -15,46 +15,217 @@ export const menuList: MenuItem[] = [
|
||||
path: '/pages/mall/admin/user-management',
|
||||
groups: [
|
||||
{
|
||||
id:'user-management',
|
||||
id: 'user-management',
|
||||
title: '用户管理',
|
||||
children: [
|
||||
{ id: 'user-list', title: '用户列表', path: '/pages/mall/admin/user-management' },
|
||||
{ id: 'user-add', title: '添加用户', path: '/pages/mall/admin/user-management?action=add' },
|
||||
{ id: 'user-statistics', title: '用户统计', path: '/pages/mall/admin/user-statistics' },
|
||||
],
|
||||
},
|
||||
],
|
||||
{
|
||||
id: 'user-statistics',
|
||||
title: '用户统计',
|
||||
path: '/pages/mall/admin/user-statistics'
|
||||
},
|
||||
{
|
||||
id: 'user-list',
|
||||
title: '用户管理',
|
||||
path: '/pages/mall/admin/user-management'
|
||||
},
|
||||
{
|
||||
id: 'user-group',
|
||||
title: '用户分组',
|
||||
path: '/pages/mall/admin/user-management?action=group'
|
||||
},
|
||||
{
|
||||
id: 'user-tag',
|
||||
title: '用户标签',
|
||||
path: '/pages/mall/admin/user-management?action=tag'
|
||||
},
|
||||
{
|
||||
id: 'user-level',
|
||||
title: '用户等级',
|
||||
path: '/pages/mall/admin/user-management?action=level'
|
||||
},
|
||||
{
|
||||
id: 'user-config',
|
||||
title: '用户配置',
|
||||
path: '/pages/mall/admin/user-management?action=config'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'order',
|
||||
title: '订单',
|
||||
icon: '/static/order.svg',
|
||||
path: '/pages/mall/admin/order-management',
|
||||
path: '/pages/mall/admin/order-management?tab=list',
|
||||
groups: [
|
||||
{
|
||||
id:'order-management',
|
||||
id: 'order-management',
|
||||
title: '订单管理',
|
||||
children: [
|
||||
{ id: 'order-list', title: '订单列表', path: '/pages/mall/admin/order-management' },
|
||||
],
|
||||
},
|
||||
],
|
||||
{
|
||||
id: 'order-stats',
|
||||
title: '订单统计',
|
||||
path: '/pages/mall/admin/order-management?tab=stats'
|
||||
},
|
||||
{
|
||||
id: 'order-list',
|
||||
title: '订单管理',
|
||||
path: '/pages/mall/admin/order-management?tab=list'
|
||||
},
|
||||
{
|
||||
id: 'order-aftersale',
|
||||
title: '售后订单',
|
||||
path: '/pages/mall/admin/order-management?tab=aftersale'
|
||||
},
|
||||
{
|
||||
id: 'order-cashier',
|
||||
title: '收银订单',
|
||||
path: '/pages/mall/admin/order-management?tab=cashier'
|
||||
},
|
||||
{
|
||||
id: 'order-verify',
|
||||
title: '核销记录',
|
||||
path: '/pages/mall/admin/order-management?tab=verify'
|
||||
},
|
||||
{
|
||||
id: 'order-config',
|
||||
title: '订单配置',
|
||||
path: '/pages/mall/admin/order-management?tab=config'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'product',
|
||||
title: '商品',
|
||||
icon: '/static/shopping.svg',
|
||||
icon: '/static/product.svg',
|
||||
path: '/pages/mall/admin/product-management',
|
||||
groups: [
|
||||
{
|
||||
id:'product-management',
|
||||
id: 'product-management',
|
||||
title: '商品管理',
|
||||
children: [
|
||||
{ id: 'product-list', title: '商品列表', path: '/pages/mall/admin/product-management' },
|
||||
{ id: 'product-add', title: '添加商品', path: '/pages/mall/admin/product-management?action=add' },
|
||||
],
|
||||
},
|
||||
],
|
||||
{
|
||||
id: 'product-list',
|
||||
title: '商品管理',
|
||||
path: '/pages/mall/admin/product-management'
|
||||
},
|
||||
{
|
||||
id: 'product-statistics',
|
||||
title: '商品统计',
|
||||
path: '/pages/mall/admin/product-statistics'
|
||||
},
|
||||
{
|
||||
id: 'product-classification',
|
||||
title: '商品分类',
|
||||
path: '/pages/mall/admin/product-classification'
|
||||
},
|
||||
{
|
||||
id: 'product-specifications',
|
||||
title: '商品规格',
|
||||
path: '/pages/mall/admin/product-specifications'
|
||||
},
|
||||
{
|
||||
id: 'product-parameters',
|
||||
title: '商品参数',
|
||||
path: '/pages/mall/admin/product-parameters'
|
||||
},
|
||||
{
|
||||
id: 'product-labels',
|
||||
title: '商品标签',
|
||||
path: '/pages/mall/admin/product-labels'
|
||||
},
|
||||
{
|
||||
id: 'product-protection',
|
||||
title: '商品保障',
|
||||
path: '/pages/mall/admin/product-protection'
|
||||
},
|
||||
{
|
||||
id: 'product-reviews',
|
||||
title: '商品评论',
|
||||
path: '/pages/mall/admin/product-reviews'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'design',
|
||||
title: '设计',
|
||||
icon: '/static/design.svg',
|
||||
path: '/pages/mall/admin/design/index',
|
||||
groups: [
|
||||
{
|
||||
id: 'design-management',
|
||||
title: '设计',
|
||||
children: [
|
||||
{
|
||||
id: 'design-home',
|
||||
title: '页面装修',
|
||||
path: '/pages/mall/admin/design/index'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'content',
|
||||
title: '文章',
|
||||
icon: '/static/content.svg',
|
||||
path: '/pages/mall/admin/content/index',
|
||||
groups: [
|
||||
{
|
||||
id: 'content-management',
|
||||
title: '文章',
|
||||
children: [
|
||||
{
|
||||
id: 'content-list',
|
||||
title: '文章管理',
|
||||
path: '/pages/mall/admin/content/index'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'customer-service',
|
||||
title: '客服',
|
||||
icon: '/static/customerService.svg',
|
||||
path: '/pages/mall/admin/customer-service/index',
|
||||
groups: [
|
||||
{
|
||||
id: 'customer-service',
|
||||
title: '客服',
|
||||
children: [
|
||||
{
|
||||
id: 'cs-list',
|
||||
title: '客服列表',
|
||||
path: '/pages/mall/admin/customer-service/list'
|
||||
},
|
||||
{
|
||||
id: 'cs-script',
|
||||
title: '客服话术',
|
||||
path: '/pages/mall/admin/customer-service/script'
|
||||
},
|
||||
{
|
||||
id: 'cs-message',
|
||||
title: '用户留言',
|
||||
path: '/pages/mall/admin/customer-service/messages'
|
||||
},
|
||||
{
|
||||
id: 'cs-auto-reply',
|
||||
title: '自动回复',
|
||||
path: '/pages/mall/admin/customer-service/auto-reply'
|
||||
},
|
||||
{
|
||||
id: 'cs-config',
|
||||
title: '客服配置',
|
||||
path: '/pages/mall/admin/customer-service/config'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'marketing',
|
||||
@@ -63,29 +234,206 @@ export const menuList: MenuItem[] = [
|
||||
path: '/pages/mall/admin/marketing-management',
|
||||
groups: [
|
||||
{
|
||||
id:'coupon',
|
||||
id: 'coupon',
|
||||
title: '优惠券活动',
|
||||
children: [
|
||||
{ id: 'coupon-list', title: '优惠券列表', path: '/pages/mall/admin/marketing/coupon/list' },
|
||||
{ id: 'coupon-receive', title: '领取情况', path: '/pages/mall/admin/marketing/coupon/receive' },
|
||||
],
|
||||
{
|
||||
id: 'coupon-list',
|
||||
title: '优惠券列表',
|
||||
path: '/pages/mall/admin/marketing/coupon/list'
|
||||
},
|
||||
{
|
||||
id: 'coupon-receive',
|
||||
title: '领取情况',
|
||||
path: '/pages/mall/admin/marketing/coupon/receive'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id:'points',
|
||||
title: '积分',
|
||||
id: 'points',
|
||||
title: '积分管理',
|
||||
children: [
|
||||
{ id: 'points-management', title: '积分管理', path: '/pages/mall/admin/marketing/points/index' },
|
||||
],
|
||||
{
|
||||
id: 'points-stats',
|
||||
title: '积分统计',
|
||||
path: '/pages/mall/admin/marketing/points/index?tab=stats'
|
||||
},
|
||||
{
|
||||
id: 'points-goods',
|
||||
title: '积分商品',
|
||||
path: '/pages/mall/admin/marketing/points/index?tab=goods'
|
||||
},
|
||||
{
|
||||
id: 'points-order',
|
||||
title: '积分订单',
|
||||
path: '/pages/mall/admin/marketing/points/index?tab=order'
|
||||
},
|
||||
{
|
||||
id: 'points-record',
|
||||
title: '积分记录',
|
||||
path: '/pages/mall/admin/marketing/points/index?tab=record'
|
||||
},
|
||||
{
|
||||
id: 'points-config',
|
||||
title: '积分配置',
|
||||
path: '/pages/mall/admin/marketing/points/index?tab=config'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id:'signin',
|
||||
title: '签到',
|
||||
id: 'lottery',
|
||||
title: '抽奖管理',
|
||||
children: [
|
||||
{ id: 'rule', title: '签到规则', path: '/pages/mall/admin/marketing/signin/rule' },
|
||||
{ id: 'record', title: '记录', path: '/pages/mall/admin/marketing/signin/record' },
|
||||
],
|
||||
{
|
||||
id: 'lottery-list',
|
||||
title: '抽奖列表',
|
||||
path: '/pages/mall/admin/marketing/points/index?tab=lottery-list'
|
||||
},
|
||||
{
|
||||
id: 'lottery-config',
|
||||
title: '抽奖配置',
|
||||
path: '/pages/mall/admin/marketing/points/index?tab=lottery-config'
|
||||
}
|
||||
]
|
||||
},
|
||||
],
|
||||
{
|
||||
id: 'groupbuy',
|
||||
title: '拼团管理',
|
||||
children: [
|
||||
{
|
||||
id: 'groupbuy-goods',
|
||||
title: '拼团商品',
|
||||
path: '/pages/mall/admin/marketing/points/index?tab=groupbuy-goods'
|
||||
},
|
||||
{
|
||||
id: 'groupbuy-list',
|
||||
title: '拼团列表',
|
||||
path: '/pages/mall/admin/marketing/points/index?tab=groupbuy-list'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'seckill',
|
||||
title: '秒杀管理',
|
||||
children: [
|
||||
{
|
||||
id: 'seckill-goods',
|
||||
title: '秒杀商品',
|
||||
path: '/pages/mall/admin/marketing/points/index?tab=seckill-goods'
|
||||
},
|
||||
{
|
||||
id: 'seckill-list',
|
||||
title: '秒杀列表',
|
||||
path: '/pages/mall/admin/marketing/points/index?tab=seckill-list'
|
||||
},
|
||||
{
|
||||
id: 'seckill-config',
|
||||
title: '秒杀配置',
|
||||
path: '/pages/mall/admin/marketing/points/index?tab=seckill-config'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'member',
|
||||
title: '会员管理',
|
||||
children: [
|
||||
{
|
||||
id: 'member-type',
|
||||
title: '会员类型',
|
||||
path: '/pages/mall/admin/marketing/points/index?tab=member-type'
|
||||
},
|
||||
{
|
||||
id: 'member-rights',
|
||||
title: '会员权益',
|
||||
path: '/pages/mall/admin/marketing/points/index?tab=member-rights'
|
||||
},
|
||||
{
|
||||
id: 'member-card',
|
||||
title: '卡密会员',
|
||||
path: '/pages/mall/admin/marketing/points/index?tab=member-card'
|
||||
},
|
||||
{
|
||||
id: 'member-record',
|
||||
title: '会员记录',
|
||||
path: '/pages/mall/admin/marketing/points/index?tab=member-record'
|
||||
},
|
||||
{
|
||||
id: 'member-config',
|
||||
title: '会员配置',
|
||||
path: '/pages/mall/admin/marketing/points/index?tab=member-config'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'live',
|
||||
title: '直播管理',
|
||||
children: [
|
||||
{
|
||||
id: 'live-room',
|
||||
title: '直播间管理',
|
||||
path: '/pages/mall/admin/marketing/points/index?tab=live-room'
|
||||
},
|
||||
{
|
||||
id: 'live-goods',
|
||||
title: '直播商品管理',
|
||||
path: '/pages/mall/admin/marketing/points/index?tab=live-goods'
|
||||
},
|
||||
{
|
||||
id: 'live-anchor',
|
||||
title: '主播管理',
|
||||
path: '/pages/mall/admin/marketing/points/index?tab=live-anchor'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'recharge',
|
||||
title: '用户充值',
|
||||
children: [
|
||||
{
|
||||
id: 'recharge-amount',
|
||||
title: '金额设置',
|
||||
path: '/pages/mall/admin/marketing/points/index?tab=recharge-amount'
|
||||
},
|
||||
{
|
||||
id: 'recharge-config',
|
||||
title: '充值配置',
|
||||
path: '/pages/mall/admin/marketing/points/index?tab=recharge-config'
|
||||
},
|
||||
{
|
||||
id: 'recharge-record',
|
||||
title: '充值记录',
|
||||
path: '/pages/mall/admin/marketing/points/index?tab=recharge-record'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'signin',
|
||||
title: '每日签到',
|
||||
children: [
|
||||
{
|
||||
id: 'signin-rule',
|
||||
title: '签到配置',
|
||||
path: '/pages/mall/admin/marketing/signin/rule'
|
||||
},
|
||||
{
|
||||
id: 'signin-record',
|
||||
title: '签到奖励',
|
||||
path: '/pages/mall/admin/marketing/signin/record'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'newcomer',
|
||||
title: '新人礼',
|
||||
children: [
|
||||
{
|
||||
id: 'newcomer',
|
||||
title: '新人礼',
|
||||
path: '/pages/mall/admin/marketing/points/index?tab=newcomer'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'system',
|
||||
@@ -94,17 +442,296 @@ export const menuList: MenuItem[] = [
|
||||
path: '/pages/mall/admin/system-settings',
|
||||
groups: [
|
||||
{
|
||||
id:'system-settings',
|
||||
id: 'sys-basic',
|
||||
title: '系统设置',
|
||||
children: [
|
||||
{ id: 'basic', title: '基本设置', path: '/pages/mall/admin/system-settings' },
|
||||
{ id: 'security', title: '安全设置', path: '/pages/mall/admin/system-settings?tab=security' },
|
||||
{ id: 'email', title: '邮件设置', path: '/pages/mall/admin/system-settings?tab=security' },
|
||||
{ id: 'payment', title: '支付设置', path: '/pages/mall/admin/system-settings?tab=security' },
|
||||
{ id: 'other', title: '其他设置', path: '/pages/mall/admin/system-settings?tab=security' },
|
||||
|
||||
],
|
||||
path: '/pages/mall/admin/system-settings',
|
||||
children: []
|
||||
},
|
||||
],
|
||||
{
|
||||
id: 'sys-message',
|
||||
title: '消息管理',
|
||||
path: '/pages/mall/admin/system/message-management',
|
||||
children: []
|
||||
},
|
||||
{
|
||||
id: 'sys-agreement',
|
||||
title: '协议设置',
|
||||
path: '/pages/mall/admin/system/agreement-settings',
|
||||
children: []
|
||||
},
|
||||
{
|
||||
id: 'sys-receipt',
|
||||
title: '小票配置',
|
||||
path: '/pages/mall/admin/system/receipt-settings',
|
||||
children: []
|
||||
},
|
||||
{
|
||||
id: 'sys-permission',
|
||||
title: '管理权限',
|
||||
children: [
|
||||
{
|
||||
id: 'sys-role',
|
||||
title: '角色管理',
|
||||
path: '/pages/mall/admin/system/permission/role'
|
||||
},
|
||||
{
|
||||
id: 'sys-admin',
|
||||
title: '管理员列表',
|
||||
path: '/pages/mall/admin/system/permission/admin-list'
|
||||
},
|
||||
{
|
||||
id: 'sys-perm-setting',
|
||||
title: '权限设置',
|
||||
path: '/pages/mall/admin/system/permission/permission-setting'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'sys-shipping',
|
||||
title: '发货设置',
|
||||
children: [
|
||||
{
|
||||
id: 'ship-courier',
|
||||
title: '配送员管理',
|
||||
path: '/pages/mall/admin/system/shipping/courier'
|
||||
},
|
||||
{
|
||||
id: 'ship-pickup',
|
||||
title: '提货点设置',
|
||||
path: '/pages/mall/admin/system/shipping/pickup/points',
|
||||
children: [
|
||||
{
|
||||
id: 'pickup-points',
|
||||
title: '提货点',
|
||||
path: '/pages/mall/admin/system/shipping/pickup/points'
|
||||
},
|
||||
{
|
||||
id: 'pickup-verifier',
|
||||
title: '核销员',
|
||||
path: '/pages/mall/admin/system/shipping/pickup/verifiers'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'ship-freight',
|
||||
title: '运费模板',
|
||||
path: '/pages/mall/admin/system/shipping/freight-template'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'sys-api',
|
||||
title: '接口配置',
|
||||
children: [
|
||||
{
|
||||
id: 'api-yht',
|
||||
title: '一号通',
|
||||
path: '/pages/mall/admin/system/api/yht/page',
|
||||
children: [
|
||||
{
|
||||
id: 'api-yht-page',
|
||||
title: '一号通页面',
|
||||
path: '/pages/mall/admin/system/api/yht/page'
|
||||
},
|
||||
{
|
||||
id: 'api-yht-config',
|
||||
title: '一号通配置',
|
||||
path: '/pages/mall/admin/system/api/yht/config'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'api-storage',
|
||||
title: '系统存储配置',
|
||||
path: '/pages/mall/admin/system/api/storage'
|
||||
},
|
||||
{
|
||||
id: 'api-collect',
|
||||
title: '商品采集配置',
|
||||
path: '/pages/mall/admin/system/api/collect'
|
||||
},
|
||||
{
|
||||
id: 'api-logistics',
|
||||
title: '物流查询配置',
|
||||
path: '/pages/mall/admin/system/api/logistics'
|
||||
},
|
||||
{
|
||||
id: 'api-waybill',
|
||||
title: '电子面单配置',
|
||||
path: '/pages/mall/admin/system/api/waybill'
|
||||
},
|
||||
{
|
||||
id: 'api-sms',
|
||||
title: '短信接口配置',
|
||||
path: '/pages/mall/admin/system/api/sms'
|
||||
},
|
||||
{
|
||||
id: 'api-pay',
|
||||
title: '商城支付配置',
|
||||
path: '/pages/mall/admin/system/api/pay'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'maintain',
|
||||
title: '维护',
|
||||
icon: '/static/maintain.svg',
|
||||
path: '/pages/mall/admin/maintain/dev-config/category',
|
||||
groups: [
|
||||
{
|
||||
id: 'dev-config',
|
||||
title: '开发配置',
|
||||
children: [
|
||||
{
|
||||
id: 'dev-config-category',
|
||||
title: '配置分类',
|
||||
path: '/pages/mall/admin/maintain/dev-config/category'
|
||||
},
|
||||
{
|
||||
id: 'dev-config-combo',
|
||||
title: '组合数据',
|
||||
path: '/pages/mall/admin/maintain/dev-config/combination-data'
|
||||
},
|
||||
{
|
||||
id: 'dev-config-cron',
|
||||
title: '定时任务',
|
||||
path: '/pages/mall/admin/maintain/dev-config/cron-job'
|
||||
},
|
||||
{
|
||||
id: 'dev-config-permission',
|
||||
title: '权限维护',
|
||||
path: '/pages/mall/admin/maintain/dev-config/permission'
|
||||
},
|
||||
{
|
||||
id: 'dev-config-module',
|
||||
title: '模块配置',
|
||||
path: '/pages/mall/admin/maintain/dev-config/module-config'
|
||||
},
|
||||
{
|
||||
id: 'dev-config-event',
|
||||
title: '自定事件',
|
||||
path: '/pages/mall/admin/maintain/dev-config/custom-event'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'security-maintain',
|
||||
title: '安全维护',
|
||||
children: [
|
||||
{
|
||||
id: 'security-refresh-cache',
|
||||
title: '刷新缓存',
|
||||
path: '/pages/mall/admin/maintain/security/refresh-cache'
|
||||
},
|
||||
{
|
||||
id: 'security-system-log',
|
||||
title: '系统日志',
|
||||
path: '/pages/mall/admin/maintain/security/system-log'
|
||||
},
|
||||
{
|
||||
id: 'security-online-upgrade',
|
||||
title: '在线升级',
|
||||
path: '/pages/mall/admin/maintain/security/online-upgrade'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'data-maintain',
|
||||
title: '数据维护',
|
||||
children: [
|
||||
{
|
||||
id: 'data-logistics-company',
|
||||
title: '物流公司',
|
||||
path: '/pages/mall/admin/maintain/data/logistics-company'
|
||||
},
|
||||
{
|
||||
id: 'data-city-data',
|
||||
title: '城市数据',
|
||||
path: '/pages/mall/admin/maintain/data/city-data'
|
||||
},
|
||||
{
|
||||
id: 'data-clear-data',
|
||||
title: '清除数据',
|
||||
path: '/pages/mall/admin/maintain/data/clear-data'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'external-api',
|
||||
title: '对外接口',
|
||||
children: [
|
||||
{
|
||||
id: 'external-account',
|
||||
title: '账号管理',
|
||||
path: '/pages/mall/admin/maintain/external/account'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'i18n-setting',
|
||||
title: '语言设置',
|
||||
children: [
|
||||
{
|
||||
id: 'i18n-language-list',
|
||||
title: '语言列表',
|
||||
path: '/pages/mall/admin/maintain/i18n/language-list'
|
||||
},
|
||||
{
|
||||
id: 'i18n-language-detail',
|
||||
title: '语言详情',
|
||||
path: '/pages/mall/admin/maintain/i18n/language-detail'
|
||||
},
|
||||
{
|
||||
id: 'i18n-region-list',
|
||||
title: '地区列表',
|
||||
path: '/pages/mall/admin/maintain/i18n/region-list'
|
||||
},
|
||||
{
|
||||
id: 'i18n-translate-config',
|
||||
title: '翻译配置',
|
||||
path: '/pages/mall/admin/maintain/i18n/translate-config'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'dev-tools',
|
||||
title: '开发工具',
|
||||
children: [
|
||||
{
|
||||
id: 'dev-tools-db',
|
||||
title: '数据库管理',
|
||||
path: '/pages/mall/admin/maintain/dev-tools/database'
|
||||
},
|
||||
{
|
||||
id: 'dev-tools-file',
|
||||
title: '文件管理',
|
||||
path: '/pages/mall/admin/maintain/dev-tools/file'
|
||||
},
|
||||
{
|
||||
id: 'dev-tools-api',
|
||||
title: '接口管理',
|
||||
path: '/pages/mall/admin/maintain/dev-tools/api'
|
||||
},
|
||||
{
|
||||
id: 'dev-tools-codegen',
|
||||
title: '代码生成',
|
||||
path: '/pages/mall/admin/maintain/dev-tools/codegen'
|
||||
},
|
||||
{
|
||||
id: 'dev-tools-dict',
|
||||
title: '数据字典',
|
||||
path: '/pages/mall/admin/maintain/dev-tools/data-dict'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'system-info',
|
||||
title: '系统信息',
|
||||
path: '/pages/mall/admin/maintain/system-info',
|
||||
children: []
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
@@ -1,24 +1,85 @@
|
||||
import type { MenuItem } from '../types.uts'
|
||||
// utils/nav.uts
|
||||
import type { MenuItem, MenuGroup, MenuChild } from '../types.uts'
|
||||
|
||||
export function findActiveByCurrentPage(
|
||||
menuList: MenuItem[],
|
||||
currentPage: string
|
||||
): { activeMenuId: string, activeSubId: string } {
|
||||
|
||||
const page = currentPage || ''
|
||||
|
||||
// 1) currentPage 直接是一级 menu id
|
||||
const mById = menuList.find(m => m.id === page)
|
||||
if (mById) {
|
||||
const leaf = firstLeafOfMenu(mById)
|
||||
return { activeMenuId: mById.id, activeSubId: leaf?.id ?? '' }
|
||||
}
|
||||
|
||||
// 2) currentPage 是 path(/pages/xxx 或 pages/xxx)
|
||||
const pageNorm = normalize(page)
|
||||
|
||||
export function findActiveByCurrentPage(menuList: MenuItem[], currentPage: string) {
|
||||
// currentPage 既可能是顶级菜单 id,也可能是子页面 id(如 user-list)
|
||||
// 返回:activeMenuId / activeSubId / activeGroupTitle
|
||||
for (const m of menuList) {
|
||||
if (m.id === currentPage) {
|
||||
return { activeMenuId: m.id, activeSubId: '', activeGroupTitle: '' }
|
||||
}
|
||||
const groups = m.groups || []
|
||||
const groups = m.groups ?? []
|
||||
|
||||
// group / child / 四级 全部扫描
|
||||
for (const g of groups) {
|
||||
for (const c of g.children) {
|
||||
if (c.id === currentPage) {
|
||||
return { activeMenuId: m.id, activeSubId: c.id, activeGroupTitle: g.title }
|
||||
}
|
||||
// group 叶子(可选)
|
||||
if (g.path && normalize(g.path) === pageNorm) {
|
||||
return { activeMenuId: m.id, activeSubId: '' }
|
||||
}
|
||||
|
||||
const cs = g.children ?? []
|
||||
for (const c of cs) {
|
||||
// 用 id 命中
|
||||
if (c.id === page) return { activeMenuId: m.id, activeSubId: c.id }
|
||||
// 用 path 命中
|
||||
if (c.path && normalize(c.path) === pageNorm) return { activeMenuId: m.id, activeSubId: c.id }
|
||||
|
||||
// 四级
|
||||
const ds = c.children ?? []
|
||||
const hit = findInChildren(ds, page, pageNorm)
|
||||
if (hit) return { activeMenuId: m.id, activeSubId: hit.id }
|
||||
}
|
||||
}
|
||||
}
|
||||
return { activeMenuId: menuList[0]?.id || 'home', activeSubId: '', activeGroupTitle: '' }
|
||||
|
||||
// 3) 找不到:兜底 home
|
||||
return { activeMenuId: 'home', activeSubId: '' }
|
||||
}
|
||||
|
||||
function findInChildren(list: MenuChild[], targetId: string, targetPathNorm: string): MenuChild | null {
|
||||
for (const n of list) {
|
||||
if (n.id === targetId) return n
|
||||
if (n.path && normalize(n.path) === targetPathNorm) return n
|
||||
const deep = n.children ?? []
|
||||
if (deep.length > 0) {
|
||||
const hit = findInChildren(deep, targetId, targetPathNorm)
|
||||
if (hit) return hit
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
function normalize(p: string): string {
|
||||
if (!p) return ''
|
||||
const s = p.startsWith('/') ? p.slice(1) : p
|
||||
const q = s.indexOf('?')
|
||||
return q >= 0 ? s.slice(0, q) : s
|
||||
}
|
||||
|
||||
// 你可以把 index.uvue 里的 firstLeafOfMenu 移到这里复用(保持逻辑一致)
|
||||
function firstLeafOfMenu(m: MenuItem): MenuChild | null {
|
||||
const gs = m.groups ?? []
|
||||
if (gs.length === 0) return null
|
||||
const g0 = gs[0]
|
||||
const cs = g0.children ?? []
|
||||
if (cs.length === 0) return null
|
||||
let n = cs[0]
|
||||
while (n.children && n.children.length > 0) n = n.children[0]
|
||||
return n
|
||||
}
|
||||
|
||||
|
||||
export function getCurrentRoutePath(): string {
|
||||
// 使用页面栈获取当前路由(uni-app标准能力)
|
||||
// getCurrentPages 用于获取当前页面栈实例 :contentReference[oaicite:2]{index=2}
|
||||
|
||||
Reference in New Issue
Block a user