kn-cli 1.0.92 → 1.0.93

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kn-cli",
3
- "version": "1.0.92",
3
+ "version": "1.0.93",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -21,8 +21,11 @@
21
21
  var docEl = document.documentElement;
22
22
  function setRemUnit(type) {
23
23
  let w = Math.max(window.innerWidth, docEl.clientWidth, docEl.offsetWidth);
24
- let defaultWidth = 1200;//填入设计稿上的宽度
25
- // w = w > defaultWidth ? defaultWidth : w;
24
+ let defaultWidth = 1440;//填入设计稿上的宽度
25
+ let minWidth=1200;
26
+ let maxWidth=1440;
27
+ w = w < minWidth ? minWidth :w;
28
+ w = w > maxWidth ? maxWidth :w;
26
29
  var rem = w / defaultWidth * 100;
27
30
  docEl.style.fontSize = rem + 'px'
28
31
  }
@@ -1,4 +1,10 @@
1
1
  @import '~antd/dist/antd.less';
2
2
 
3
3
  // 这里可以重写antd的主题样式变量,参考 https://4x.ant.design/docs/react/customize-theme-cn
4
- @primary-color:#EB437C;
4
+ @primary-color:#EB437C;
5
+
6
+ :global{
7
+ .ant-menu-submenu{
8
+ color:white;
9
+ }
10
+ }
@@ -1,11 +1,10 @@
1
1
  // @ts-ignore
2
2
  import React, { useEffect, useRef, useState } from 'react';
3
3
  // @ts-ignore
4
- import { Menu } from 'antd';
4
+ import { Menu,Badge } from 'antd';
5
5
  // @ts-ignore
6
- import {matchPath,useLocation,useNavigate,} from 'react-router-dom';
7
- // @ts-ignore
8
- import { AppstoreOutlined, MailOutlined, SettingOutlined } from '@ant-design/icons';
6
+ import {useNavigate,} from 'react-router-dom';
7
+
9
8
 
10
9
  import {useLoading} from '@/hooks/index';
11
10
  import ProviderMenu from '@/provider/menu';
@@ -13,11 +12,6 @@ import ProviderMenu from '@/provider/menu';
13
12
  // @ts-ignore
14
13
  import styles from './index.less';
15
14
 
16
- const ICON_NAME={
17
- 'AppstoreOutlined':<AppstoreOutlined />,
18
- 'MailOutlined':<MailOutlined />,
19
- 'SettingOutlined':<SettingOutlined />,
20
- }
21
15
 
22
16
 
23
17
 
@@ -28,113 +22,66 @@ const ICON_NAME={
28
22
  */
29
23
  const LeftMenu=()=>{
30
24
 
25
+
31
26
  const providerMenu = ProviderMenu.useContainer();
32
27
  const loader = useLoading();
33
28
  const [menus,setMenus] = useState([]);
34
- const [openKeys,setOpenKeys] = useState([]);
35
- const [selectedKeys,setSelectKeys] = useState([]);
36
- const curRoute = useLocation();
29
+ const [subMenuOpen,setSubMenuOpen]= useState([]);
37
30
  const nav = useNavigate();
38
-
39
- const MENU_KEY_COUNTER=useRef(1);
40
- const MENU_KEY=useRef({});
41
- const translateMenu=(data={},parent)=>{
42
- const {label,icon,children,path}= data;
43
- let myKey = `${MENU_KEY_COUNTER.current}`
44
- let item ={
45
- key:myKey,
46
- label:`${label}`,
47
- source:data,
48
- path,
49
- routepath:parent?`${parent.key}-${myKey}`:myKey
50
- }
51
- MENU_KEY.current[myKey] = item;
52
- MENU_KEY_COUNTER.current++;
53
- if(icon){
54
- item.icon= ICON_NAME[icon];
55
- }
56
- if(children){
57
- item.children = children.map(childData=>{
58
- return translateMenu(childData,item)
59
- })
60
- }
61
- return item;
62
- }
63
-
64
- // @ts-ignore
65
- window.matchPath=matchPath;
66
-
67
-
68
- const GET_ROUTE_MENU=(menuList)=>{
69
- let groupKeys=[];
70
- for(let key in menuList){
71
- const menuItem = menuList[key];
72
- const {path,routeTemplate,subRoute}= menuItem.source;
73
- if(path){
74
- let fn = matchPath;
75
- let match= fn(routeTemplate||path,curRoute.pathname);
76
- if(!match&&subRoute){
77
- match= subRoute.some(sub=>{
78
- let subMatch= fn(sub.routeTemplate||sub.path,curRoute.pathname);
79
- if(subMatch){
80
- return true;
81
- }
82
- return false;
83
- })
84
- }
85
- if(match){
86
- groupKeys = menuItem.routepath?menuItem.routepath.split('-'):'';
87
- }
31
+
32
+ const translateMenu=(list)=>{
33
+ if(!list||list.length<=0)return [];
34
+ let req=list.map(menu=>{
35
+ const {key,name:label,icon,url,msgCount}=menu;
36
+
37
+ let item={
38
+ key,label,icon,url
88
39
  }
89
- }
90
- return groupKeys;
91
- }
92
-
93
- const loadMenu= ()=>{
94
- loader.setLoading(true);
95
- if(providerMenu.menus){
96
- const menuList= providerMenu.menus.map(menu=>{
97
- return translateMenu(menu)
98
- })
99
- setMenus(menuList);
100
- }else{
101
- setMenus([]);
102
- }
103
- loader.setLoading(false);
40
+ if(msgCount&&msgCount>0){
41
+ item.label=<span>{label}<Badge size='small' count={msgCount} offset={[5,-5]} /></span>
42
+ }
43
+ if(menu.children){
44
+ item.children = translateMenu(menu.children)
45
+ }
46
+ return item;
47
+ })
48
+ return req;
104
49
  }
105
50
 
106
51
  useEffect(()=>{
107
- loadMenu();
108
- },[providerMenu])
52
+ let menuList = translateMenu(providerMenu.leftMenu);
53
+ setMenus(menuList);
54
+ },[providerMenu.leftMenu])
109
55
 
110
- useEffect(()=>{
111
- let groupKeys= GET_ROUTE_MENU(MENU_KEY.current);
112
- setOpenKeys(groupKeys);
113
- let selectKey =groupKeys[groupKeys.length-1];
114
- setSelectKeys([selectKey])
115
- },[curRoute,menus]);
116
56
 
117
57
  const onClickMenuItem=(e)=>{
118
58
  const { item, key, keyPath, domEvent } = e;
119
- const menuData = MENU_KEY.current[key];
120
- if(menuData.path){
121
- nav(menuData.path);
122
- }
59
+ let menu = providerMenu.findMenuData(key);
60
+ nav(menu.url);
123
61
  }
124
- const onOpenChange=(keys)=>{
125
- setOpenKeys(keys);
62
+
63
+ const onOpenSubMenu=(keys)=>{
64
+ setSubMenuOpen(keys)
126
65
  }
66
+ useEffect(()=>{
67
+ if(providerMenu.selectMenus.keys && providerMenu.selectMenus.keys.length>0){
68
+ setSubMenuOpen([...subMenuOpen,...providerMenu.selectMenus.keys]);
69
+ }
70
+ },[providerMenu.selectMenus.keys])
127
71
  return (
128
72
  <section className={styles.nav}>
129
- <Menu
130
- theme='dark'
131
- selectedKeys={selectedKeys}
132
- openKeys={openKeys}
133
- onOpenChange={onOpenChange}
134
- onClick={onClickMenuItem}
135
- mode="inline"
136
- items={loader.loading?[]:menus}
137
- />
73
+ {
74
+ providerMenu.selectMenus.keys&&
75
+ <Menu
76
+ theme='dark'
77
+ openKeys={subMenuOpen}
78
+ onOpenChange={onOpenSubMenu}
79
+ selectedKeys={providerMenu.selectMenus.keys}
80
+ onClick={onClickMenuItem}
81
+ mode="inline"
82
+ items={loader.loading?[]:menus}
83
+ />
84
+ }
138
85
  </section>
139
86
  )
140
87
  }
@@ -0,0 +1,119 @@
1
+ // @ts-ignore
2
+ import React, { useState,useEffect, useRef} from 'react';
3
+ // @ts-ignore
4
+ import { useLocation,useNavigate } from 'react-router-dom';
5
+ // @ts-ignore
6
+ import { LogoutOutlined, UserOutlined } from '@ant-design/icons';
7
+ // @ts-ignore
8
+ import { Menu,Dropdown,Badge } from 'antd';
9
+
10
+ import ProviderApp from '@/provider/app';
11
+ import ProviderMenu from '@/provider/menu';
12
+
13
+ // @ts-ignore
14
+ import imgAvatar from '@/assets/images/avatar.png';
15
+ // @ts-ignore
16
+ import styles from './index.less';
17
+
18
+ const TopMenu=()=>{
19
+ const app = ProviderApp.useContainer();
20
+ const providerMenu = ProviderMenu.useContainer();
21
+ const navigate = useNavigate();
22
+
23
+ const [menus,setMenus]=useState([]);
24
+
25
+
26
+ const translateMenu=(list)=>{
27
+ if(!list||list.length<=0)return [];
28
+ let req=list.map(menu=>{
29
+ const {key,name:label,icon,url,msgCount}=menu;
30
+
31
+ let item={
32
+ key,label,icon,url
33
+ }
34
+ if(msgCount&&msgCount>0){
35
+ item.label=<span>{label}<Badge size='small' count={msgCount} offset={[5,-5]} /></span>
36
+ }
37
+ if(menu.children){
38
+ item.children = translateMenu(menu.children)
39
+ }
40
+ return item;
41
+ })
42
+ return req;
43
+ }
44
+
45
+ const reload= async ()=>{
46
+ providerMenu.reload();
47
+ }
48
+ useEffect(()=>{
49
+ let menuList = translateMenu(providerMenu.topMenu);
50
+ menuList.forEach(menu=>{
51
+ menu.children=null;//顶级菜单这里设计为不需要展示子菜单
52
+ })
53
+ setMenus(menuList);
54
+ },[providerMenu.topMenu]);
55
+
56
+ useEffect(()=>{
57
+ reload();
58
+ },[])
59
+
60
+
61
+ // 推出登录
62
+ const onAvatarClick = (e)=>{
63
+ if (e?.key === 'logout') {
64
+ app.logout();
65
+ }
66
+ }
67
+ const onHome=()=>{
68
+ navigate('/');
69
+ }
70
+ const onClickMenuItem=(e)=>{
71
+ const { item, key, keyPath, domEvent } = e;
72
+ let menu = providerMenu.findMenuData(key);
73
+ navigate(menu.url);
74
+ }
75
+
76
+ return (
77
+ <section className={styles.topMenu}>
78
+ <div className={styles.left}>
79
+ <span className={styles.title} onClick={onHome}>后管系统</span>
80
+ </div>
81
+
82
+ <div className={styles.center}>
83
+ {
84
+ menus&&menus.length>1&&
85
+ <Menu
86
+ items={menus}
87
+ mode="horizontal"
88
+ onClick={onClickMenuItem}
89
+ selectedKeys={providerMenu.selectMenus.keys}
90
+ className={styles.menu}
91
+ />
92
+ }
93
+ </div>
94
+ <div className={styles.right}>
95
+
96
+ <span className={styles.username}>Hi,{app?.user?.username||''}!</span>
97
+ <Dropdown menu={(
98
+ <Menu onClick={onAvatarClick} className={styles.avatarMenu}>
99
+ <Menu.Item key="user">
100
+ <UserOutlined />
101
+ 用户名
102
+ </Menu.Item>
103
+
104
+ <Menu.Item key="logout">
105
+ <LogoutOutlined />
106
+ 退出登录
107
+ </Menu.Item>
108
+ </Menu>
109
+ )}>
110
+ <img className={styles.avatar} src={imgAvatar}/>
111
+ </Dropdown>
112
+
113
+ </div>
114
+
115
+ </section>
116
+ )
117
+ }
118
+
119
+ export default TopMenu;
@@ -3,10 +3,13 @@
3
3
  import React from 'react';
4
4
  // @ts-ignore
5
5
  import {useDictionary} from 'kn-hooks';
6
+ // @ts-ignore
7
+ import {Select,Radio} from 'antd';
6
8
 
7
9
  import {GET_USER_TYPE} from '@/services/user';
8
10
  import ShowToast from '@/components/toast';
9
11
 
12
+
10
13
  export const SelectOption=(props)=>{
11
14
  const {value}=props;
12
15
  const name = props['data-keyname'];
@@ -18,7 +21,7 @@ export const SelectOption=(props)=>{
18
21
  return <hgroup onClick={onClick} key={value} name={name} value={value}>{props.children}</hgroup>
19
22
  }
20
23
 
21
- useDictionary.SetConfig({SelectOption})
24
+ useDictionary.SetConfig({SelectOption:Select.Option, RadioOption: Radio })
22
25
 
23
26
 
24
27
  export const useUserType = useDictionary.createDictionary({
@@ -6,6 +6,8 @@ import usePreload from './usePreload';
6
6
  import useSearch from './useSearch';
7
7
  import useUpdate from './useUpdate';
8
8
  import useLoading from './useLoading';
9
+ import useRouteMenu from './useRouteMenu';
10
+
9
11
  // @ts-ignore
10
12
  import {usePagination,usePaginationWithForm} from 'kn-hooks';
11
13
 
@@ -17,5 +19,6 @@ export {
17
19
  usePreload,
18
20
  useSearch,
19
21
  useUpdate,
20
- useLoading
22
+ useLoading,
23
+ useRouteMenu
21
24
  }
@@ -0,0 +1,232 @@
1
+
2
+
3
+ // @ts-ignore
4
+ import React,{ useRef, useState} from 'react';
5
+ // @ts-ignore
6
+ import { matchPath } from 'react-router-dom';
7
+
8
+
9
+ /**
10
+ * 设置菜单属性
11
+ * @typedef MenuConfig
12
+ * @property {string} primaryId - 菜单的primaryId值
13
+ * @property {('msgCount'|'icon'|'name')} key - 需要设置的字段名称
14
+ * @property {any} value - 字段值
15
+ */
16
+
17
+ /**
18
+ * 菜单项
19
+ * @typedef RouteMenuItem
20
+ * @property {string} name - 菜单展示的名称
21
+ * @property {string|JSX.Element} [icon] - 菜单图标
22
+ * @property {string} [url] - 菜单的URL地址
23
+ * @property {string} [routeTemplate] - 菜单的路由模版,用于匹配动态路由
24
+ * @property {number} [msgCount] - 菜单的未读消息数量
25
+ * @property {boolean} [hideMenu=false] - 是否为隐藏的菜单,隐藏的菜单不会展示独立菜单,但是会匹配路由
26
+ * @property {string[]} [auth] - 允许访问该菜单的权限列表
27
+ * @property {RouteMenuItem[]} [children] - 子菜单
28
+ * @property {string} [primaryId] - 菜单的唯一ID,如果存在primaryId,则菜单的key同primaryId
29
+ * @property {string} [key] - 会自动生成的菜单唯一ID,如果菜单配置内存在了primaryId,则会取该id
30
+ * @property {string} [parentKey] - 父级菜单的key,自动生成的
31
+ *
32
+ */
33
+
34
+ // const MenuData=[
35
+ // {
36
+ // name:'用户中心',
37
+ // icon:'AppstoreOutlined',
38
+ // url:'/user',
39
+ // children:[
40
+ // {
41
+ // key:'myAccount',
42
+ // name:'账号管理',
43
+ // url:'/user/accouunt',
44
+ // msgCount:0,
45
+ // },
46
+ // ]
47
+ // },
48
+ // {
49
+ // name:'消息中心',
50
+ // icon:'AppstoreOutlined',
51
+ // url:'/message',
52
+ // children:[
53
+ // {
54
+ // name:'我的消息',
55
+ // url:'/message/my',
56
+ // msgCount:3,
57
+ // children:[
58
+ // {
59
+ // label: '创建消息',
60
+ // url: '/message/edit/create',
61
+ // routeTemplate:'/message/edit/:type',
62
+ // msgCount:0,
63
+ // },
64
+ // {
65
+ // label: '编辑消息',
66
+ // url: '/message/edit/edit',
67
+ // routeTemplate:'/message/edit/:type',
68
+ // msgCount:0,
69
+ // auth:['super'],
70
+ // },
71
+ // {
72
+ // url:'/message/detail',
73
+ // hideMenu:true,//是否是隐藏
74
+ // routeTemplate:'/message/detail/:id',
75
+ // },
76
+
77
+ // ]
78
+ // },
79
+
80
+ // ]
81
+ // }
82
+ // ]
83
+
84
+
85
+ const useRouteMenu=()=>{
86
+ /**
87
+ * @type [RouteMenuItem[], React.Dispatch<React.SetStateAction<RouteMenuItem[]>>]
88
+ */
89
+ const [source,setSource]= useState([]);
90
+
91
+ /**
92
+ * @type React.MutableRefObject<number>
93
+ */
94
+ const refKey = useRef(1);
95
+
96
+ /**
97
+ * @type React.MutableRefObject<Object<string,RouteMenuItem>>
98
+ */
99
+ const refKeyMap = useRef();
100
+
101
+ /**
102
+ * 遍历菜单,给菜单创建key,并创建map表以便快速查找菜单数据
103
+ * @param {RouteMenuItem[]} data - 菜单列表数据
104
+ * @param {RouteMenuItem} [parent] - 父级菜单数据,如果没有则代表是顶层菜单
105
+ */
106
+ const setSourceMap=(data,parent=null)=>{
107
+ if(parent==null){
108
+ // 重置
109
+ refKey.current=1;
110
+ refKeyMap.current={};
111
+ }
112
+ data.forEach(menu=>{
113
+ if(menu.primaryId){
114
+ menu.key = menu.primaryId
115
+ }else{
116
+ menu.key=`${refKey.current++}`;
117
+ }
118
+ refKeyMap.current[menu.key] = menu;
119
+
120
+ if(parent){
121
+ menu.parentKey = parent.key;
122
+ }
123
+ if(menu.children && menu.children.length>0){
124
+ setSourceMap(menu.children,menu);
125
+ }
126
+ });
127
+ if(parent==null){
128
+ setSource([...data]);
129
+ }
130
+
131
+ }
132
+
133
+ /**
134
+ * 获取当前菜单数据,用于渲染
135
+ * 会将没权限的菜单、隐藏菜单过滤掉
136
+ * @param {RouteMenuItem[]} menus - 需要被检查的菜单列表
137
+ * @param {string[]} userAuth - 用户当前的权限列表
138
+ * @returns {RouteMenuItem[]}
139
+ */
140
+ const getMenu=(menus=source, userAuth=[])=>{
141
+ const list = [];
142
+ menus.forEach(menu=>{
143
+ let auth= true;
144
+ // 判断用户有没有权限可以访问这个菜单
145
+ if(menu.auth){
146
+ if(menu.auth.some(menuAuthName=>userAuth.includes(menuAuthName)) == false){
147
+ auth=false;
148
+ }
149
+ }
150
+ // 隐藏的菜单
151
+ if(menu.hideMenu){
152
+ auth=false;
153
+ }
154
+
155
+ // 只有当前菜单有权限的前提下,才可以继续遍历子菜单查询子菜单是否有权限
156
+ if(auth){
157
+ const item= {...menu};
158
+ if(item.children&&item.children.length>0){
159
+ const chidList= getMenu(item.children, userAuth);
160
+ item.children = chidList.length>0?chidList:null;
161
+ }
162
+ list.push(item)
163
+ }
164
+ });
165
+ if(menus == source){
166
+ console.warn('[useRouteMenu]getMenu:',list)
167
+ }
168
+ return list
169
+ }
170
+
171
+ /**
172
+ * 计算pathname路由路径下的菜单路径
173
+ * @param {string} pathname - 需要匹配的路由路径
174
+ * @returns {RouteMenuItem[]}
175
+ */
176
+ const getOpenMenus=(pathname='/')=>{
177
+ /** @type RouteMenuItem */
178
+ let matchMenu;
179
+ for(const menuKeyName in refKeyMap.current){
180
+ const menu= refKeyMap.current[menuKeyName];
181
+ if( matchPath(menu.routeTemplate||menu.url, pathname) ){
182
+ matchMenu = menu;
183
+ break;
184
+ }
185
+ }
186
+
187
+ const getParentMenus = (menu,menus=[])=>{
188
+ menus.push(menu);
189
+ if(menu.parentKey){
190
+ return getParentMenus( refKeyMap.current[menu.parentKey],menus );
191
+ }
192
+ return menus;
193
+ }
194
+ if(matchMenu){
195
+ const menus= getParentMenus(matchMenu);
196
+ console.warn(`[useRouteMenu]openMenus:`,menus)
197
+ return menus;
198
+ }
199
+ return [];
200
+ }
201
+
202
+ /**
203
+ * 根据menu的key来查找menu数据
204
+ * @param {string} menuKey - 菜单的key
205
+ * @returns {RouteMenuItem}
206
+ */
207
+ const findMenuData= (menuKey)=>{
208
+ return refKeyMap.current[menuKey]
209
+ }
210
+
211
+ /**
212
+ * 设置菜单的属性
213
+ * @param {MenuConfig[]} params
214
+ */
215
+ const setMenuConfig=(params)=>{
216
+
217
+ params.forEach(param=>{
218
+ const {primaryId,key,value}= param;
219
+ const menu = refKeyMap.current[primaryId];
220
+ if(menu){
221
+ // @ts-ignore
222
+ menu[`${key}`] = value;
223
+ }
224
+ })
225
+ setSource([...source])
226
+ }
227
+
228
+ return {setSourceMap,getOpenMenus,getMenu,source,findMenuData,setMenuConfig}
229
+ }
230
+
231
+ export default useRouteMenu;
232
+
@@ -1,22 +1,273 @@
1
1
  // @ts-ignore
2
- import React,{ useState,useMemo } from 'react';
2
+ import React,{ useState,useMemo, useEffect } from 'react';
3
+ // @ts-ignore
4
+ import { useLocation } from 'react-router-dom';
3
5
  // @ts-ignore
4
6
  import { createContainer } from "unstated-next"
5
7
 
6
- import {GET_MENU} from '@/services/auth';
8
+ import useRouteMenu from '@/hooks/useRouteMenu';
9
+
10
+
11
+ // @ts-ignore
12
+ import { AppstoreOutlined, MailOutlined } from '@ant-design/icons';
13
+
14
+ const MenuRoute=[
15
+ {
16
+ name:'常用管理',
17
+ url:'/',
18
+ children:[
19
+ {
20
+ name:'内容管理',
21
+ icon:<AppstoreOutlined />,
22
+ url:'/content',
23
+ children:[
24
+ {name:'剧集管理',url:'/content/video'},
25
+ {name:'素材管理',url:'/content/material'},
26
+ ]
27
+
28
+ },
29
+ {
30
+ name:'数据管理',
31
+ icon:<MailOutlined />,
32
+ url:'/data',
33
+ children:[
34
+ {primaryId:'orderData',name:'订单数据',msgCount:3,url:'/data/order'},
35
+ {name:'用户数据',url:'/data/userData'},
36
+ ]
37
+ },
38
+ {
39
+ name:'用户管理',
40
+ url:'/user',
41
+ children:[
42
+ {name:'账户管理',url:'/user/user'},
43
+ {name:'客诉管理',url:'/user/suggest'},
44
+ ]
45
+ },
46
+ ]
47
+ },
48
+ {
49
+ name:'权限管理',
50
+ url:'/auth',
51
+ children:[
52
+ {
53
+ name:'权限设置',
54
+ icon:<AppstoreOutlined />,
55
+ url:'/auth/config',
56
+ children:[
57
+ {name:'角色配置',url:'/auth/config/permission'},
58
+ ]
59
+ },
60
+ ]
61
+ }
62
+ ]
63
+
64
+ const MenuRoute2=[
65
+ {
66
+ name:'数据管理',
67
+ icon:'AppstoreOutlined',
68
+ url:'/order',
69
+ auth:['super'],
70
+ children:[
71
+ {
72
+ name:'数据管理',
73
+ // icon:'AppstoreOutlined',
74
+ url:'/order',
75
+ // children:[
76
+ // {name:'剧集管理',url:'/video'},
77
+ // {name:'素材管理',url:'/material'},
78
+ // ]
79
+
80
+ },
81
+ ]
82
+ },
83
+ {
84
+ name:'手机管理',
85
+ url:'/device',
86
+ icon:'AppstoreOutlined',
87
+ children:[]
88
+ },
89
+ {
90
+ name:'服务设置',
91
+ url:'/service',
92
+ icon:'AppstoreOutlined',
93
+ children:[
94
+ {
95
+ name:'账号管理',
96
+ url:'/service/account'
97
+ },
98
+ // {
99
+ // name:'账号健康设置',
100
+ // url:'/service/accountHealth'
101
+ // },
102
+ // {
103
+ // name:'排班管理',
104
+ // url:'/service/workforce'
105
+ // },
106
+ // {
107
+ // name:'表单设置',
108
+ // url:'/service/workforce'
109
+ // },
110
+
111
+ ]
112
+
113
+ },
114
+ {
115
+ name:'内容管理',
116
+ url:'/video',
117
+ icon:'AppstoreOutlined',
118
+ children:[
119
+ {
120
+ name:'内容管理',
121
+ url:'/video',
122
+ // children:[
123
+ // {name:'剧集管理',url:'/video'},
124
+ // {name:'素材管理',url:'/material'},
125
+ // ]
126
+
127
+ },
128
+ // {
129
+ // name:'数据管理',
130
+ // icon:'AppstoreOutlined',
131
+ // children:[
132
+ // {name:'订单数据',url:'/order'},
133
+ // {name:'用户数据',url:'/userData'},
134
+ // ]
135
+ // },
136
+ // {
137
+ // name:'用户管理',
138
+ // icon:'UserOutlined',
139
+ // url:'/user'
140
+ // },
141
+ // {
142
+ // name:'客诉管理',
143
+ // icon:'CustomerServiceOutlined',
144
+ // url:'/suggest'
145
+ // },
146
+ // {
147
+ // name:'权限配置',
148
+ // icon:'CrownOutlined',
149
+ // url:'/permission'
150
+ // },
151
+ ]
152
+ },
153
+ {
154
+ name: '权限设置',
155
+ url: '/authUser',
156
+ icon: 'AppstoreOutlined',
157
+ children: [
158
+ {
159
+ name: '用户管理',
160
+ url: '/authUser',
161
+ },
162
+ {
163
+ name: '角色管理',
164
+ url: '/authRole',
165
+ },
166
+ {
167
+ name: '用户组',
168
+ url: '/authUserGroup',
169
+ },
170
+ ]
171
+ },
172
+ {
173
+ name:'消息中心',
174
+ url:'/message',
175
+ icon:'AppstoreOutlined',
176
+ children:[
177
+ {
178
+ primaryId:'myMessage',
179
+ name:'我的消息',
180
+ // msgCount:MSG_COUNT++,
181
+ url:'/message/my',
182
+ },
183
+ {
184
+ name:'消息设置',
185
+ url:'/message/config',
186
+ children:[
187
+ {
188
+ name:'消息查询-2',
189
+ url:'/message/config/2',
190
+ },
191
+ {
192
+ name:'消息查询-3',
193
+ url:'/message/config/3',
194
+ },
195
+ ]
196
+ },
197
+ {
198
+ name:'消息查询',
199
+ url:'/message/query',
200
+ children:[
201
+ {
202
+ name:'消息查询-设置',
203
+ hideMenu:true,
204
+ routeTemplate:'/message/query/:type',
205
+ },
206
+ ]
207
+ },
208
+
209
+ ]
210
+
211
+ },
212
+ ]
213
+
7
214
 
8
215
  const useProvider=() =>{
9
- const [menus,setMenus]= useState([]);
10
216
 
11
- const reload= async ()=>{
12
- await GET_MENU();
217
+ const routeMenu = useRouteMenu();
218
+
219
+ const curLocation =useLocation();
220
+
221
+ const [topMenu,setTopMenu]=useState([]);
222
+ const [leftMenu,setLeftMenu]=useState([]);
223
+
224
+ const [selectMenus,setSelectMenus] = useState({menus:[],keys:[]});
225
+
226
+
227
+ const reload = async ()=>{
228
+ routeMenu.setSourceMap([...MenuRoute]);
229
+ }
230
+ const getTopMenu=(userAuth=[])=>{
231
+ const list = routeMenu.getMenu(routeMenu.source,userAuth);
232
+ setTopMenu(list||[]);
13
233
  }
14
- const action=useMemo(()=>{
15
- return {
16
- menus,setMenus
234
+ const getLeftMenu=(userAuth=[])=>{
235
+ const list = routeMenu.getMenu(routeMenu.source,userAuth);
236
+ let menus= routeMenu.getOpenMenus(curLocation.pathname);
237
+
238
+ const req= list.filter(menu=>{
239
+ return menus.some(openMenu=>openMenu.key==menu.key)
240
+ })
241
+
242
+ let subs=[];
243
+ if(req && req[0]){
244
+ subs=req[0].children||[];
17
245
  }
18
- },[menus,setMenus]);
19
- return action
246
+ setLeftMenu(subs);
247
+ }
248
+
249
+ const findMenuData=(menuKey)=>{
250
+ return routeMenu.findMenuData(menuKey);
251
+
252
+ }
253
+ const setMenuConfig=(params)=>{
254
+ routeMenu.setMenuConfig(params);
255
+ }
256
+
257
+ useEffect(()=>{
258
+ let menus= routeMenu.getOpenMenus(curLocation.pathname);
259
+ let keys=menus.map(item=>item.key);
260
+ setSelectMenus({menus,keys});
261
+ },[curLocation,routeMenu.source]);
262
+
263
+ useEffect(()=>{
264
+ getTopMenu();
265
+ getLeftMenu();
266
+ },[routeMenu.source,curLocation])
267
+
268
+
269
+
270
+ return {leftMenu,topMenu,reload,selectMenus,findMenuData,setMenuConfig}
20
271
  }
21
272
  const App = createContainer(useProvider);
22
273
 
@@ -10,7 +10,7 @@ import PageLoading from '@/components/page/pageLoading';
10
10
  import Login from '@/pages/login';
11
11
  import {AuthLogin} from '@/components/auth';
12
12
  import LeftMenu from '@/components/menu';
13
- import TopMenu from '@/components/topMenu';
13
+ import TopMenu from '@/components/menu/topMenu';
14
14
 
15
15
  import ProviderMenu from '@/provider/menu';
16
16
 
@@ -54,14 +54,23 @@ export const RouteList = (
54
54
  </ProviderMenu.Provider>
55
55
  }
56
56
  >
57
- <Route path='/' element={<Navigate to="/video"></Navigate> } />
58
- <Route path='/video' element={<Video />} />
59
- <Route path='/suggest' element={<Suggest />} />
60
- <Route path='/order' element={<Order />} />
61
- <Route path='/permission' element={<Permission />} />
62
- <Route path='/material' element={<Material />} />
63
- <Route path='/user' element={<User />} />
64
- <Route path='/userData' element={<UserData />} />
57
+ <Route path='/' element={<Navigate to="/content/video"></Navigate> } />
58
+ <Route path='/content/video' element={<Video />} />
59
+ <Route path='/content/material' element={<Material />} />
60
+
61
+
62
+ <Route path='/data' element={<Navigate to="/data/order"></Navigate> } />
63
+ <Route path='/data/order' element={<Order />} />
64
+ <Route path='/data/userData' element={<UserData />} />
65
+
66
+ <Route path='/user' element={<Navigate to="/user/user"></Navigate> } />
67
+ <Route path='/user/user' element={<User />} />
68
+ <Route path='/user/suggest' element={<Suggest />} />
69
+
70
+ <Route path='/auth' element={<Navigate to="/auth/config/permission"></Navigate> } />
71
+ <Route path='/auth/config/permission' element={<Permission />} />
72
+
73
+
65
74
 
66
75
  </Route>
67
76
  </Route>
@@ -1,267 +0,0 @@
1
- // @ts-ignore
2
- import React, { useState,useEffect, useRef} from 'react';
3
- // @ts-ignore
4
- import { matchPath,useLocation,useNavigate } from 'react-router-dom';
5
- // @ts-ignore
6
- import { LogoutOutlined, UserOutlined } from '@ant-design/icons';
7
- // @ts-ignore
8
- import { Menu,Dropdown } from 'antd';
9
-
10
- import ProviderApp from '@/provider/app';
11
- import ProviderMenu from '@/provider/menu';
12
- import {GET_MENU} from '@/services/auth';
13
-
14
- // @ts-ignore
15
- import imgAvatar from '@/assets/images/avatar.png';
16
- // @ts-ignore
17
- import styles from './index.less';
18
-
19
- /**
20
- * 菜单下的隐藏子路由集
21
- * 当这些隐藏子路由打开的时候,导航栏的选中菜单会显示被匹配的这个子菜单
22
- */
23
- const SUB_MENU_CONFIG=[
24
- {
25
- path:'/video',//匹配菜单路由是/video的菜单
26
- // routeTemplate:''匹配某个模板菜单的模板
27
- subRoute:[
28
- {
29
- path:'/video/detail',//将video/detail这个路由匹配给菜单/video
30
- // routeTemplate:''//也可以将某个路由模板匹配给某个菜单
31
- }
32
- ]
33
- },
34
- ]
35
-
36
- /**
37
- * 顶部导航栏
38
- * 左侧导航栏的路由也继顶部导航栏来控制
39
- * 路由数据结构:
40
- * const MenuRoute=[
41
- {
42
- label:'顶部菜单A',
43
- path:'/home/sub/1',
44
- menus:[
45
- {
46
- label:'菜单组A-1',
47
- icon:'MailOutlined',
48
- children:[
49
- {label:'菜单组A-1-home',path:'/',},
50
- {label:'菜单组A-1-sub:id',path:'/home/sub/1',routeTemplate:'/home/sub/:id'},
51
- {label:'菜单组A-1-sub3:id',path:'/home/sub3/2',routeTemplate:'/home/sub3/:id'},
52
- ]
53
- },
54
- ]
55
- }
56
- ]
57
- 顶部的导航栏结构为{label,path,menus}
58
- 左侧导航栏数据为顶部导航栏数据的menus内子项{label,icon,children}组成
59
- routeTemplate用来识别动态参数的路由,同前端本地路由配置同步即可
60
- 当顶部导航栏只有一项时,将自动隐藏顶部导航菜单
61
- * @returns {JSX.Element}
62
- */
63
- const TopMenu=()=>{
64
- const app = ProviderApp.useContainer();
65
- const providerMenu = ProviderMenu.useContainer();
66
- const curRoute = useLocation();
67
-
68
- const [menus,setMenus]=useState([]);
69
- const navigate = useNavigate();
70
- const [selectedKeys,setSelectedKeys] = useState([]);
71
-
72
-
73
- const MENU_KEY_COUNTER=useRef(1);
74
- const MENU_KEY=useRef({})
75
- const translateMenu=(data={})=>{
76
- const {label,path}= data;
77
- let myKey = `${MENU_KEY_COUNTER.current}`
78
- let item ={
79
- key:myKey,
80
- label:`${label}`,
81
- path,
82
- source:data,
83
- }
84
- MENU_KEY.current[myKey] = item;
85
- MENU_KEY_COUNTER.current++;
86
- return item;
87
- }
88
-
89
- const getSubConfig=(menu)=>{
90
- const {url,routeTemplate}= menu;
91
- for(let i=0;i<SUB_MENU_CONFIG.length;i++){
92
- let sub= SUB_MENU_CONFIG[i];
93
- if(routeTemplate&&sub.routeTemplate&&routeTemplate==sub.routeTemplate){
94
- return sub.subRoute;
95
- }
96
- if(url&&sub.path&&url==sub.path){
97
- return sub.subRoute;
98
- }
99
- }
100
- return null;
101
- }
102
- const transServicesMenuChildren=(menu)=>{
103
- let req=[];
104
- for(let i=0;i<menu.length;i++){
105
- let item= menu[i];
106
- const {url:path,name:label,icon,children:menus,routeTemplate}= item;
107
- let children;
108
- if(menus&&menus.length>0){
109
- children= transServicesMenuChildren(menus);
110
- }
111
- let subRoute= getSubConfig(item);
112
- req.push({
113
- path,label,icon,children,routeTemplate,subRoute
114
- })
115
- }
116
- return req;
117
- }
118
- const loadMenu=async ()=>{
119
- let reqMenu = await GET_MENU();
120
- if(reqMenu?.code==0){
121
- let originMenu= reqMenu.data;
122
- reqMenu=[];
123
- for(let i=0;i<originMenu.length;i++){
124
- let topMenu= originMenu[i];
125
- const {url:path,children,name:label}=topMenu;
126
- const menus = transServicesMenuChildren(children);
127
- reqMenu.push({
128
- path,menus,label
129
- })
130
- }
131
- }
132
- if(!reqMenu){
133
- setMenus([]);
134
- return;
135
- }
136
- const data = reqMenu.map(topMenu=>{
137
- return translateMenu(topMenu);
138
- })
139
- setMenus(data);
140
- }
141
-
142
- const GET_ROUTE_MENU=(menu)=>{
143
- const {routeTemplate,path,subRoute} = menu;
144
- const children = menu.children||menu.menus||null;
145
- if(routeTemplate||path){
146
- let match= matchPath(routeTemplate||path,curRoute.pathname);
147
- if(!match&&subRoute){
148
- match= subRoute.some(sub=>{
149
- let subMatch= matchPath(sub.routeTemplate||sub.path,curRoute.pathname);
150
- if(subMatch){
151
- console.log(`match sub route: ${sub.routeTemplate||sub.path} -> ${curRoute.pathname}` )
152
- return true;
153
- }
154
- return false;
155
- });
156
- }
157
- if(match){
158
- console.log(`match route: ${routeTemplate||path} -> ${curRoute.pathname}` )
159
- return true;
160
- }
161
- }
162
- if(children){
163
- for(let subMenu of children){
164
- let match = GET_ROUTE_MENU(subMenu);
165
- if(match){
166
- return true;
167
- }
168
- }
169
- }
170
- return false;
171
- }
172
-
173
- const reloadOpenMenu=()=>{
174
- for(let menuKey in MENU_KEY.current){
175
- const topMenu = MENU_KEY.current[menuKey];
176
- const match= GET_ROUTE_MENU(topMenu.source);
177
- if(match){
178
- let changeLeftMenu=false;//是否重新设置左侧导航菜单数据
179
- if(selectedKeys[0] != menuKey){
180
- changeLeftMenu= true;
181
- }
182
- if(!providerMenu.menus||providerMenu.menus.length<=0){
183
- changeLeftMenu= true;
184
- }
185
- setSelectedKeys([menuKey]);
186
- if(changeLeftMenu){
187
- providerMenu.setMenus(topMenu.source.menus);
188
- }
189
- return;
190
- }
191
- }
192
- setSelectedKeys([]);
193
- providerMenu.setMenus([]);
194
- }
195
- useEffect(()=>{loadMenu()},[]);
196
-
197
- useEffect(()=>{
198
- reloadOpenMenu();
199
- },[curRoute,menus]);
200
-
201
-
202
-
203
- // 推出登录
204
- const onAvatarClick = (e)=>{
205
- if (e?.key === 'logout') {
206
- app.logout();
207
- }
208
- }
209
- const onHome=()=>{
210
- navigate('/');
211
- }
212
- const onClickMenuItem=(e)=>{
213
- const { item, key, keyPath, domEvent } = e;
214
- const menuData = MENU_KEY.current[key];
215
- const {path,source:{menus}} = menuData;
216
- providerMenu.setMenus(menus);
217
- if(path){
218
- navigate(path)
219
- }
220
- setSelectedKeys([key])
221
- }
222
-
223
- return (
224
- <section className={styles.topMenu}>
225
- <div className={styles.left}>
226
- <span className={styles.title} onClick={onHome}>后管系统</span>
227
- </div>
228
-
229
- <div className={styles.center}>
230
- {
231
- menus&&menus.length>1&&
232
- <Menu
233
- items={menus}
234
- mode="horizontal"
235
- onClick={onClickMenuItem}
236
-
237
- selectedKeys={selectedKeys}
238
- className={styles.menu}
239
- />
240
- }
241
- </div>
242
- <div className={styles.right}>
243
-
244
- <span className={styles.username}>Hi,{app?.user?.username||''}!</span>
245
- <Dropdown overlay={(
246
- <Menu onClick={onAvatarClick} className={styles.avatarMenu}>
247
- <Menu.Item key="user">
248
- <UserOutlined />
249
- 用户名
250
- </Menu.Item>
251
-
252
- <Menu.Item key="logout">
253
- <LogoutOutlined />
254
- 退出登录
255
- </Menu.Item>
256
- </Menu>
257
- )}>
258
- <img className={styles.avatar} src={imgAvatar}/>
259
- </Dropdown>
260
-
261
- </div>
262
-
263
- </section>
264
- )
265
- }
266
-
267
- export default TopMenu;