From 6191cd6ef71449b7211f869d8c891c29cb371279 Mon Sep 17 00:00:00 2001 From: hacker233 Date: Sat, 15 Jun 2024 11:48:12 +0800 Subject: [PATCH] =?UTF-8?q?feature(=E9=A6=96=E9=A1=B5=E5=AF=BC=E8=88=AA?= =?UTF-8?q?=E8=8F=9C=E5=8D=95=E9=85=8D=E7=BD=AE=E5=8C=96=E3=80=81=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E7=94=A8=E6=88=B7=E5=B0=81=E7=A6=81=E3=80=81=E5=90=84?= =?UTF-8?q?=E7=A7=8D=E4=BC=98=E5=8C=96):=20=E9=A6=96=E9=A1=B5=E5=AF=BC?= =?UTF-8?q?=E8=88=AA=E8=8F=9C=E5=8D=95=E9=85=8D=E7=BD=AE=E5=8C=96=E3=80=81?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E7=94=A8=E6=88=B7=E5=B0=81=E7=A6=81=E3=80=81?= =?UTF-8?q?=E5=90=84=E7=A7=8D=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .eslintrc-auto-import.json | 1 + src/App.vue | 4 + src/auto-import.d.ts | 1 + src/components.d.ts | 5 +- src/components/NavBar/NavBar.vue | 284 +++++++++--------- src/components/NavBar/components/MenuItem.vue | 42 +++ src/config/index.ts | 2 +- src/http/api/menu.ts | 35 +++ src/http/index.ts | 52 +++- src/router/index.ts | 13 +- src/store/index.ts | 3 + src/store/menu.ts | 31 ++ src/utils/common.ts | 27 ++ .../admin/ArticleManage/ArticleList/index.vue | 70 ++++- .../components/IndexMenuDialog.vue | 243 +++++++++++++++ .../MenuManage/IndexMenuManage/index.vue | 168 +++++++++++ src/views/admin/leftMenu/index.vue | 12 + .../payStats/payList/components/PayDialog.vue | 2 +- src/views/admin/payStats/payList/index.vue | 47 ++- .../uerList/components/EditDialog.vue | 25 +- src/views/designer/components/DesignNav.vue | 5 +- src/views/index/components/WebData.vue | 1 - src/views/person/components/ContentTitle.vue | 20 +- 23 files changed, 912 insertions(+), 181 deletions(-) create mode 100644 src/components/NavBar/components/MenuItem.vue create mode 100644 src/http/api/menu.ts create mode 100644 src/store/menu.ts create mode 100644 src/views/admin/MenuManage/IndexMenuManage/components/IndexMenuDialog.vue create mode 100644 src/views/admin/MenuManage/IndexMenuManage/index.vue diff --git a/.eslintrc-auto-import.json b/.eslintrc-auto-import.json index eec82726..1d488164 100644 --- a/.eslintrc-auto-import.json +++ b/.eslintrc-auto-import.json @@ -2,6 +2,7 @@ "globals": { "EffectScope": true, "ElMessage": true, + "ElMessageBox": true, "asyncComputed": true, "autoResetRef": true, "computed": true, diff --git a/src/App.vue b/src/App.vue index 3c266bad..c1a052b1 100644 --- a/src/App.vue +++ b/src/App.vue @@ -25,6 +25,10 @@ const { refreshUuid } = appStore.useRefreshStore; const route = useRoute(); + // 查询首页导航菜单 + const { getIndexMenuList } = appStore.useIndexMenuStore; + getIndexMenuList(); + // 查询和更新用户信息 const { getAndUpdateUserInfo } = appStore.useUserInfoStore; const { token } = appStore.useTokenStore; diff --git a/src/auto-import.d.ts b/src/auto-import.d.ts index 0bec82ec..5013cff0 100644 --- a/src/auto-import.d.ts +++ b/src/auto-import.d.ts @@ -3,6 +3,7 @@ export {} declare global { const EffectScope: typeof import('vue')['EffectScope'] const ElMessage: typeof import('element-plus/es')['ElMessage'] + const ElMessageBox: typeof import('element-plus/es')['ElMessageBox'] const asyncComputed: typeof import('@vueuse/core')['asyncComputed'] const autoResetRef: typeof import('@vueuse/core')['autoResetRef'] const computed: typeof import('vue')['computed'] diff --git a/src/components.d.ts b/src/components.d.ts index c2c10bc2..5d6aeee4 100644 --- a/src/components.d.ts +++ b/src/components.d.ts @@ -24,23 +24,21 @@ declare module '@vue/runtime-core' { DateFormItem: typeof import('./components/DateFormItem/DateFormItem.vue')['default'] Editor: typeof import('./components/packages/components/editor/editor.vue')['default'] EduBackgroundCom: typeof import('./components/ModelComs/EduBackgroundCom.vue')['default'] - ElAlert: typeof import('element-plus/es')['ElAlert'] ElAvatar: typeof import('element-plus/es')['ElAvatar'] ElBacktop: typeof import('element-plus/es')['ElBacktop'] ElButton: typeof import('element-plus/es')['ElButton'] ElCarousel: typeof import('element-plus/es')['ElCarousel'] ElCarouselItem: typeof import('element-plus/es')['ElCarouselItem'] + ElCheckbox: typeof import('element-plus/es')['ElCheckbox'] ElCollapse: typeof import('element-plus/es')['ElCollapse'] ElCollapseItem: typeof import('element-plus/es')['ElCollapseItem'] ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider'] ElDatePicker: typeof import('element-plus/es')['ElDatePicker'] ElDialog: typeof import('element-plus/es')['ElDialog'] - ElDivider: typeof import('element-plus/es')['ElDivider'] ElDrawer: typeof import('element-plus/es')['ElDrawer'] ElDropdown: typeof import('element-plus/es')['ElDropdown'] ElDropdownItem: typeof import('element-plus/es')['ElDropdownItem'] ElDropdownMenu: typeof import('element-plus/es')['ElDropdownMenu'] - ElEmpty: typeof import('element-plus/es')['ElEmpty'] ElForm: typeof import('element-plus/es')['ElForm'] ElFormItem: typeof import('element-plus/es')['ElFormItem'] ElIcon: typeof import('element-plus/es')['ElIcon'] @@ -88,6 +86,7 @@ declare module '@vue/runtime-core' { LoginDialog: typeof import('./components/LoginDialog/LoginDialog.vue')['default'] LoginDialogOld: typeof import('./components/LoginDialog/LoginDialogOld.vue')['default'] LogoCom: typeof import('./components/LogoCom/LogoCom.vue')['default'] + MenuItem: typeof import('./components/NavBar/components/MenuItem.vue')['default'] ModelBox: typeof import('./components/ModelBox/ModelBox.vue')['default'] ModelPreviewBox: typeof import('./components/ResumePreview/components/ModelPreviewBox.vue')['default'] NavBar: typeof import('./components/NavBar/NavBar.vue')['default'] diff --git a/src/components/NavBar/NavBar.vue b/src/components/NavBar/NavBar.vue index 22d8e885..297372f5 100644 --- a/src/components/NavBar/NavBar.vue +++ b/src/components/NavBar/NavBar.vue @@ -7,33 +7,19 @@ >
- +
@@ -119,123 +105,114 @@ }); // 菜单列表 - const menuList = reactive([ - { - iconfont: '', - name: 'Template', - title: '简历制作', - children: [ - { - iconfont: '', - name: 'Template', - title: '在线制作', - children: null, - path: '/template' - }, - { - iconfont: '', - name: 'LegoTemplateList', - title: '积木创作', - children: null, - path: '/legoTemplateList' - } - ] - }, - { - iconfont: '', - name: 'TemplateDownload', - title: '模板下载', - children: [ - { - iconfont: '', - name: 'Word', - title: '简历模板', - children: null, - path: '/word' - }, - { - iconfont: '', - name: 'PPT', - title: 'PPT模板', - children: null, - path: '/ppt' - } - ] - }, - { - iconfont: '', - name: 'Resourceshare', - title: '资源分享', - children: [ - { - iconfont: '', - name: 'Soft', - title: '软件分享', - children: null, - path: '/soft' - }, - { - iconfont: '', - name: 'Website', - title: '网站分享', - children: null, - path: '/website' - }, - { - iconfont: '', - name: 'PanShare', - title: '网盘资源', - children: null, - path: '/panshare' - } - ] - }, - { - iconfont: '', - name: 'OnlineTool', - title: '在线工具', - children: [ - { - iconfont: '', - name: 'ImgCompress', - title: '图片压缩', - children: null, - path: '/imgCompress' - } - ] - }, - { - iconfont: '', - name: 'WebCode', - title: '私有部署', - children: [ - { - iconfont: '', - name: 'WebCode', - title: '源码获取', - children: null, - path: '/webcode' - }, - { - iconfont: '', - name: 'DeployDoc', - title: '知识库', - children: null, - path: '/deployDoc' - } - ] - } - ]); - - // 菜单 - const currentMenu = ref(''); - const handleSelect = (key: string) => { - currentMenu.value = key; - router.push({ - name: key - }); - console.log(currentMenu.value); - }; + const { indexMenuList } = appStore.useIndexMenuStore; + // const menuList = reactive([ + // { + // iconfont: '', + // name: 'Template', + // title: '简历制作', + // children: [ + // { + // iconfont: '', + // name: 'Template', + // title: '在线制作', + // children: null, + // path: '/template' + // }, + // { + // iconfont: '', + // name: 'LegoTemplateList', + // title: '积木创作', + // children: null, + // path: '/legoTemplateList' + // } + // ] + // }, + // { + // iconfont: '', + // name: 'TemplateDownload', + // title: '模板下载', + // children: [ + // { + // iconfont: '', + // name: 'Word', + // title: '简历模板', + // children: null, + // path: '/word' + // }, + // { + // iconfont: '', + // name: 'PPT', + // title: 'PPT模板', + // children: null, + // path: '/ppt' + // } + // ] + // }, + // { + // iconfont: '', + // name: 'Resourceshare', + // title: '资源分享', + // children: [ + // { + // iconfont: '', + // name: 'Soft', + // title: '软件分享', + // children: null, + // path: '/soft' + // }, + // { + // iconfont: '', + // name: 'Website', + // title: '网站分享', + // children: null, + // path: '/website' + // }, + // { + // iconfont: '', + // name: 'PanShare', + // title: '网盘资源', + // children: null, + // path: '/panshare' + // } + // ] + // }, + // { + // iconfont: '', + // name: 'OnlineTool', + // title: '在线工具', + // children: [ + // { + // iconfont: '', + // name: 'ImgCompress', + // title: '图片压缩', + // children: null, + // path: '/imgCompress' + // } + // ] + // }, + // { + // iconfont: '', + // name: 'WebCode', + // title: '私有部署', + // children: [ + // { + // iconfont: '', + // name: 'WebCode', + // title: '源码获取', + // children: null, + // path: '/webcode' + // }, + // { + // iconfont: '', + // name: 'DeployDoc', + // title: '知识库', + // children: null, + // path: '/deployDoc' + // } + // ] + // } + // ]); const nameColor = computed(() => { return props.fontColor ? '#2ddd9d' : 'green'; @@ -538,7 +515,7 @@ diff --git a/src/components/NavBar/components/MenuItem.vue b/src/components/NavBar/components/MenuItem.vue new file mode 100644 index 00000000..6f0aab26 --- /dev/null +++ b/src/components/NavBar/components/MenuItem.vue @@ -0,0 +1,42 @@ + + + diff --git a/src/config/index.ts b/src/config/index.ts index 71a543a9..314c65b8 100644 --- a/src/config/index.ts +++ b/src/config/index.ts @@ -1,8 +1,8 @@ const serverAddress = 'https://91huajian.cn'; const CONFIG = { + maxUserResume: 4, // 允许每位用户最多制作多少份在线制作的简历,限制数量是为了减少数据库存储压力 isEmailVerify: true, // 邮箱注册后,是否需要去邮件验证。true:开启验证。false:未开启验证 // serverAddress: 'http://localhost:3399', // 后台地址 - // serverAddress: 'https://91huajian.cn', serverAddress: serverAddress, smallpigAddress: 'https://smallpig.site' // 另一个后台地址,如果没有多个后台, 无需填写此字段 }; diff --git a/src/http/api/menu.ts b/src/http/api/menu.ts new file mode 100644 index 00000000..76a1990c --- /dev/null +++ b/src/http/api/menu.ts @@ -0,0 +1,35 @@ +import http from '../request'; + +// 查询查询首页菜单列表 +export const getIndexMenuListAsync: any = () => { + return http.request({ + url: '/huajian/common/getIndexMenuList', + method: 'get' + }); +}; + +// 新增一个菜单 +export const addIndexMenuAsync: any = (data: any) => { + return http.request({ + url: '/huajian/menu/addIndexMenu', + method: 'post', + data: data + }); +}; + +// 更新菜单 +export const updateIndexMenuAsync: any = (data: any) => { + return http.request({ + url: '/huajian/menu/updateIndexMenu', + method: 'put', + data: data + }); +}; + +// 删除菜单以及所有子菜单 +export const deleteIndexMenuAsync: any = (id: string) => { + return http.request({ + url: `/huajian/menu/deleteIndexMenu/${id}`, + method: 'delete' + }); +}; diff --git a/src/http/index.ts b/src/http/index.ts index 004ca45b..88f8e3dd 100644 --- a/src/http/index.ts +++ b/src/http/index.ts @@ -32,8 +32,6 @@ class Request { // 拦截器执行顺序 接口请求 -> 实例请求 -> 全局请求 -> 实例响应 -> 全局响应 -> 接口响应 this.instance.interceptors.request.use( (res: AxiosRequestConfig) => { - console.log('res', res); - return res; }, (err: any) => err @@ -54,11 +52,50 @@ class Request { (res: AxiosResponse) => { return res.data; }, - (err: any) => { - return { - status: 500, - message: err - }; + (error: any) => { + if (error && error.response) { + switch (error.response.status) { + case 400: + error.message = '请求错误(400)'; + break; + case 401: + error.message = '未授权,请重新登录(401)'; + break; + case 403: + error.message = '拒绝访问(403)'; + break; + case 404: + error.message = '请求出错(404)'; + break; + case 408: + error.message = '请求超时(408)'; + break; + case 500: + error.message = '服务器错误(500)'; + break; + case 501: + error.message = '服务未实现(501)'; + break; + case 502: + error.message = '网络错误(502)'; + break; + case 503: + error.message = '服务不可用(503)'; + break; + case 504: + error.message = '网络超时(504)'; + break; + case 505: + error.message = 'HTTP版本不受支持(505)'; + break; + default: + error.message = `连接出错(${error.response.status})!`; + } + } else { + error.message = '连接服务器失败!'; + } + ElMessage.error(error.response.data.message || error.message); + return Promise.reject(error); } ); } @@ -115,7 +152,6 @@ class Request { if (localStorage.getItem('token')) { config.headers.Authorization = localStorage.getItem('token') as string; } - console.log('config', config); this.instance .request(config) .then((res) => { diff --git a/src/router/index.ts b/src/router/index.ts index 3cb7b335..d40097d0 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -84,6 +84,7 @@ const WebsiteCategoryManage = () => import('@/views/admin/SourceShare/websiteCat const WebsiteManage = () => import('@/views/admin/SourceShare/websiteShare/index.vue'); const PanShareCategoryManage = () => import('@/views/admin/SourceShare/panCategory/index.vue'); const PanShareManage = () => import('@/views/admin/SourceShare/panShare/index.vue'); +const IndexMenuManage = () => import('@/views/admin/MenuManage/IndexMenuManage/index.vue'); const routes: Array = [ { @@ -869,6 +870,17 @@ const routes: Array = [ requireLogin: true }, component: PanShareManage + }, + { + path: 'indexMenuManage', + name: 'IndexMenuManage', + meta: { + title: '首页导航菜单管理', + keepAlive: true, + isShowComNav: false, + requireLogin: true + }, + component: IndexMenuManage } ] } @@ -881,7 +893,6 @@ const router = createRouter({ // 全局守卫:登录拦截 本地没有存token,请重新登录 router.beforeEach((to, from, next) => { - console.log(to, from); const token = localStorage.getItem('token'); const userInfo = localStorage.getItem('userInfo'); // 需要权限且已经登录 diff --git a/src/store/index.ts b/src/store/index.ts index bdcbacde..c5c1ad64 100644 --- a/src/store/index.ts +++ b/src/store/index.ts @@ -6,6 +6,7 @@ import { useTokenStore } from './token'; import { useUserInfoStore } from './user'; import { useRefreshStore } from './refresh'; import { useLegoJsonStore, useLegoSelectWidgetStore, useUndoAndRedoStore } from './lego'; +import { useIndexMenuStore } from './menu'; export interface IAppStore { useLoadingStore: ReturnType; @@ -18,6 +19,7 @@ export interface IAppStore { useLegoJsonStore: ReturnType; useLegoSelectWidgetStore: ReturnType; useUndoAndRedoStore: ReturnType; + useIndexMenuStore: ReturnType; } const appStore: IAppStore = {} as IAppStore; @@ -36,6 +38,7 @@ export const registerStore = () => { appStore.useLegoJsonStore = useLegoJsonStore(); appStore.useLegoSelectWidgetStore = useLegoSelectWidgetStore(); appStore.useUndoAndRedoStore = useUndoAndRedoStore(); + appStore.useIndexMenuStore = useIndexMenuStore(); // 重写reset方法 initResetFun(appStore); }; diff --git a/src/store/menu.ts b/src/store/menu.ts new file mode 100644 index 00000000..c13391a3 --- /dev/null +++ b/src/store/menu.ts @@ -0,0 +1,31 @@ +import { getIndexMenuListAsync } from '@/http/api/menu'; +import { buildTree } from '@/utils/common'; +import { defineStore } from 'pinia'; + +// 用户信息 +export const useIndexMenuStore = defineStore('indexMenuStore', () => { + const indexMenuList = ref([]); + function saveIndexMenu(indexMenu: any) { + indexMenuList.value = indexMenu; + } + + // 查询用户当前用户简币信息 + async function getIndexMenuList() { + const data = await getIndexMenuListAsync(); + if (data.status === 200) { + const treeData = buildTree(data.data); + console.log('首页导航菜单', treeData); + saveIndexMenu(treeData); + } else { + ElMessage({ + message: data.message, + type: 'error' + }); + } + } + return { + indexMenuList, + saveIndexMenu, + getIndexMenuList + }; +}); diff --git a/src/utils/common.ts b/src/utils/common.ts index a2f239d4..e6dc2da0 100644 --- a/src/utils/common.ts +++ b/src/utils/common.ts @@ -265,3 +265,30 @@ export const formatNumber = (num: number) => { ? (num / 1e4).toFixed(1) + 'w' : num; }; + +// 将扁平数组转化成树形数组 +export const buildTree = (items: any) => { + // 创建一个映射,用于快速查找父节点 + const map: any = {}; + items.forEach((item: any) => (map[item._id] = { ...item, children: [] })); + + // 创建一个结果数组,存放最终的树形结构 + const tree: Array = []; + + // 遍历所有项,将它们添加到其父节点的children数组中 + items.forEach((item: any) => { + // 如果parentId为空,说明是根节点 + if (item.parentId === '') { + // 添加到结果数组中 + tree.push(map[item._id]); + } else { + // 否则,找到其父节点并添加子节点 + if (map[item.parentId]) { + map[item.parentId].children.push(map[item._id]); + } + } + }); + + // 返回最终的树形结构 + return tree; +}; diff --git a/src/views/admin/ArticleManage/ArticleList/index.vue b/src/views/admin/ArticleManage/ArticleList/index.vue index 89f2a12e..3eb0eab8 100644 --- a/src/views/admin/ArticleManage/ArticleList/index.vue +++ b/src/views/admin/ArticleManage/ArticleList/index.vue @@ -1,5 +1,23 @@ diff --git a/src/views/admin/MenuManage/IndexMenuManage/index.vue b/src/views/admin/MenuManage/IndexMenuManage/index.vue new file mode 100644 index 00000000..605dc64c --- /dev/null +++ b/src/views/admin/MenuManage/IndexMenuManage/index.vue @@ -0,0 +1,168 @@ + + + + diff --git a/src/views/admin/leftMenu/index.vue b/src/views/admin/leftMenu/index.vue index 0f76933c..53110553 100644 --- a/src/views/admin/leftMenu/index.vue +++ b/src/views/admin/leftMenu/index.vue @@ -70,6 +70,18 @@ } ] }, + { + index: 'menu-list', + iconfont: 'icon-hengxiangbuju', + title: '菜单管理', + children: [ + { + index: 'IndexMenuManage', + iconfont: 'icon-ruanjianguanli', + title: '首页导航管理' + } + ] + }, { index: '1', iconfont: 'icon-gongzuoleixing', diff --git a/src/views/admin/payStats/payList/components/PayDialog.vue b/src/views/admin/payStats/payList/components/PayDialog.vue index 520ca18d..bc43939e 100644 --- a/src/views/admin/payStats/payList/components/PayDialog.vue +++ b/src/views/admin/payStats/payList/components/PayDialog.vue @@ -17,7 +17,7 @@ label-position="left" > - + diff --git a/src/views/admin/payStats/payList/index.vue b/src/views/admin/payStats/payList/index.vue index c36d81ce..b753d15a 100644 --- a/src/views/admin/payStats/payList/index.vue +++ b/src/views/admin/payStats/payList/index.vue @@ -1,10 +1,15 @@