将 menu 从 appstore 提出.
This commit is contained in:
@@ -98,6 +98,7 @@ body {
|
||||
.circle {
|
||||
display: inline-block;
|
||||
margin-right: 4px;
|
||||
margin-bottom: 2px;
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: 50%;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div class="block">
|
||||
<h5 class="title">{{ title }}</h5>
|
||||
<div v-for="option in options" :key="option.name" class="switch-wrapper">
|
||||
<span>{{ $t(option.name) }}</span>
|
||||
<span>{{ option.name }}</span>
|
||||
<form-wrapper
|
||||
:type="option.type || 'switch'"
|
||||
:name="option.key"
|
||||
|
||||
@@ -1,18 +1,15 @@
|
||||
<template>
|
||||
<a-input-number
|
||||
v-if="type === 'number'"
|
||||
:style="{ width: '80px' }"
|
||||
size="small"
|
||||
:default-value="defaultValue as number"
|
||||
@change="handleChange"
|
||||
hide-button
|
||||
/>
|
||||
<a-switch
|
||||
v-else
|
||||
:default-checked="defaultValue"
|
||||
size="small"
|
||||
@change="handleChange"
|
||||
<a-input-number v-if="type === 'number'"
|
||||
:style="{ width: '80px' }"
|
||||
size="small"
|
||||
:default-value="defaultValue as number"
|
||||
@change="handleChange"
|
||||
hide-button
|
||||
/>
|
||||
<a-switch v-else
|
||||
:default-checked="defaultValue"
|
||||
size="small"
|
||||
@change="handleChange" />
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
|
||||
@@ -6,35 +6,27 @@
|
||||
</template>
|
||||
</a-button>
|
||||
</div>
|
||||
<a-drawer
|
||||
:width="300"
|
||||
unmount-on-close
|
||||
:visible="visible"
|
||||
:cancel-text="$t('settings.close')"
|
||||
:ok-text="$t('settings.copySettings')"
|
||||
@ok="copySettings"
|
||||
@cancel="cancel"
|
||||
>
|
||||
<template #title> {{ $t('settings.title') }}</template>
|
||||
<Block :options="contentOpts" :title="$t('settings.content')" />
|
||||
<Block :options="othersOpts" :title="$t('settings.otherSettings')" />
|
||||
<a-alert>{{ $t('settings.alertContent') }}</a-alert>
|
||||
<a-drawer title="偏好设置"
|
||||
:width="300"
|
||||
unmount-on-close
|
||||
:visible="visible"
|
||||
ok-text="保存"
|
||||
cancel-text="关闭"
|
||||
@ok="saveConfig"
|
||||
@cancel="cancel">
|
||||
<Block :options="contentOpts" title="内容区域" />
|
||||
<Block :options="othersOpts" title="其他设置" />
|
||||
</a-drawer>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed } from 'vue';
|
||||
import { Message } from '@arco-design/web-vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useClipboard } from '@vueuse/core';
|
||||
import { useAppStore } from '@/store';
|
||||
import Block from './block.vue';
|
||||
|
||||
const emit = defineEmits(['cancel']);
|
||||
|
||||
const appStore = useAppStore();
|
||||
const { t } = useI18n();
|
||||
const { copy } = useClipboard();
|
||||
const visible = computed(() => appStore.globalSettings);
|
||||
|
||||
/**
|
||||
@@ -42,33 +34,32 @@
|
||||
*/
|
||||
const contentOpts = computed(() => [
|
||||
{
|
||||
name: 'settings.navbar',
|
||||
name: '导航栏',
|
||||
key: 'navbar',
|
||||
defaultVal: appStore.navbar
|
||||
},
|
||||
{
|
||||
name: 'settings.menu',
|
||||
name: '菜单栏',
|
||||
key: 'menu',
|
||||
defaultVal: appStore.menu,
|
||||
},
|
||||
{
|
||||
name: 'settings.topMenu',
|
||||
name: '顶部菜单栏',
|
||||
key: 'topMenu',
|
||||
defaultVal: appStore.topMenu,
|
||||
},
|
||||
{
|
||||
name: 'settings.footer',
|
||||
name: '底部',
|
||||
key: 'footer',
|
||||
defaultVal:
|
||||
appStore.footer
|
||||
defaultVal: appStore.footer
|
||||
},
|
||||
{
|
||||
name: 'settings.tabBar',
|
||||
name: '多页签',
|
||||
key: 'tabBar',
|
||||
defaultVal: appStore.tabBar
|
||||
},
|
||||
{
|
||||
name: 'settings.menuWidth',
|
||||
name: '菜单宽度 (px)',
|
||||
key: 'menuWidth',
|
||||
defaultVal: appStore.menuWidth,
|
||||
type: 'number',
|
||||
@@ -80,7 +71,7 @@
|
||||
*/
|
||||
const othersOpts = computed(() => [
|
||||
{
|
||||
name: 'settings.colorWeak',
|
||||
name: '色弱模式',
|
||||
key: 'colorWeak',
|
||||
defaultVal: appStore.colorWeak,
|
||||
},
|
||||
@@ -97,10 +88,7 @@
|
||||
/**
|
||||
* 复制配置
|
||||
*/
|
||||
const copySettings = async () => {
|
||||
const text = JSON.stringify(appStore.$state, null, 2);
|
||||
await copy(text);
|
||||
Message.success(t('settings.copySettings.message'));
|
||||
const saveConfig = async () => {
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { computed } from 'vue';
|
||||
import { RouteRecordRaw, RouteRecordNormalized } from 'vue-router';
|
||||
import { useAppStore } from '@/store';
|
||||
import { useMenuStore } from '@/store';
|
||||
import { cloneDeep } from 'lodash';
|
||||
|
||||
export default function useMenuTree() {
|
||||
const appStore = useAppStore();
|
||||
const menuStore = useMenuStore();
|
||||
const appRoute = computed(() => {
|
||||
return appStore.appAsyncMenus;
|
||||
return menuStore.appMenus;
|
||||
});
|
||||
const menuTree = computed(() => {
|
||||
const copyRouter = cloneDeep(appRoute.value) as RouteRecordNormalized[];
|
||||
|
||||
@@ -6,17 +6,15 @@
|
||||
<span> {{ item.title }}{{ formatUnreadLength(item.key) }} </span>
|
||||
</template>
|
||||
<a-result v-if="!renderList.length" status="404">
|
||||
<template #subtitle> {{ $t('messageBox.noContent') }}</template>
|
||||
<template #subtitle>暂无内容</template>
|
||||
</a-result>
|
||||
<List
|
||||
:render-list="renderList"
|
||||
:unread-count="unreadCount"
|
||||
@item-click="handleItemClick"
|
||||
/>
|
||||
<List :render-list="renderList"
|
||||
:unread-count="unreadCount"
|
||||
@item-click="handleItemClick" />
|
||||
</a-tab-pane>
|
||||
<template #extra>
|
||||
<a-button type="text" @click="emptyList">
|
||||
{{ $t('messageBox.tab.button') }}
|
||||
清空
|
||||
</a-button>
|
||||
</template>
|
||||
</a-tabs>
|
||||
@@ -25,7 +23,6 @@
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive, toRefs, computed } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import {
|
||||
queryMessageList,
|
||||
setMessageStatus,
|
||||
@@ -43,7 +40,6 @@
|
||||
|
||||
const { loading, setLoading } = useLoading(true);
|
||||
const messageType = ref('message');
|
||||
const { t } = useI18n();
|
||||
const messageData = reactive<{
|
||||
renderList: MessageRecord[];
|
||||
messageList: MessageRecord[];
|
||||
@@ -55,15 +51,15 @@
|
||||
const tabList: TabItem[] = [
|
||||
{
|
||||
key: 'message',
|
||||
title: t('messageBox.tab.title.message'),
|
||||
title: '消息',
|
||||
},
|
||||
{
|
||||
key: 'notice',
|
||||
title: t('messageBox.tab.title.notice'),
|
||||
title: '通知',
|
||||
},
|
||||
{
|
||||
key: 'todo',
|
||||
title: t('messageBox.tab.title.todo'),
|
||||
title: '待办',
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
@@ -57,10 +57,10 @@
|
||||
:class="{ 'add-border-top': renderList.length < showMax }"
|
||||
>
|
||||
<div class="footer-wrap">
|
||||
<a-link @click="allRead">{{ $t('messageBox.allRead') }}</a-link>
|
||||
<a-link @click="allRead">全部已读</a-link>
|
||||
</div>
|
||||
<div class="footer-wrap">
|
||||
<a-link>{{ $t('messageBox.viewMore') }}</a-link>
|
||||
<a-link>查看更多</a-link>
|
||||
</div>
|
||||
</a-space>
|
||||
</template>
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
export default {
|
||||
'messageBox.tab.title.message': '消息',
|
||||
'messageBox.tab.title.notice': '通知',
|
||||
'messageBox.tab.title.todo': '待办',
|
||||
'messageBox.tab.button': '清空',
|
||||
'messageBox.allRead': '全部已读',
|
||||
'messageBox.viewMore': '查看更多',
|
||||
'messageBox.noContent': '暂无内容',
|
||||
'messageBox.userCenter': '用户中心',
|
||||
'messageBox.userSettings': '用户设置',
|
||||
'messageBox.logout': '登出登录',
|
||||
};
|
||||
@@ -31,7 +31,7 @@
|
||||
<ul class="right-side">
|
||||
<!-- 搜索 -->
|
||||
<li v-if="false">
|
||||
<a-tooltip :content="$t('settings.search')">
|
||||
<a-tooltip content="搜索">
|
||||
<a-button class="nav-btn" type="outline" shape="circle">
|
||||
<template #icon>
|
||||
<icon-search />
|
||||
@@ -41,7 +41,7 @@
|
||||
</li>
|
||||
<!-- 切换语言 -->
|
||||
<li v-if="false">
|
||||
<a-tooltip :content="$t('settings.language')">
|
||||
<a-tooltip content="语言">
|
||||
<a-button
|
||||
class="nav-btn"
|
||||
type="outline"
|
||||
@@ -69,8 +69,8 @@
|
||||
<!-- 暗色模式 -->
|
||||
<li>
|
||||
<a-tooltip :content="theme === 'light'
|
||||
? $t('settings.navbar.theme.toDark')
|
||||
: $t('settings.navbar.theme.toLight')">
|
||||
? '点击切换为暗黑模式'
|
||||
: '点击切换为亮色模式'">
|
||||
<a-button
|
||||
class="nav-btn"
|
||||
type="outline"
|
||||
@@ -85,7 +85,7 @@
|
||||
</li>
|
||||
<!-- 消息列表 -->
|
||||
<li v-if="false">
|
||||
<a-tooltip :content="$t('settings.navbar.alerts')">
|
||||
<a-tooltip content="消息通知">
|
||||
<div class="message-box-trigger">
|
||||
<a-badge :count="9" dot>
|
||||
<a-button
|
||||
@@ -112,8 +112,8 @@
|
||||
<!-- 全屏模式 -->
|
||||
<li>
|
||||
<a-tooltip :content="isFullscreen
|
||||
? $t('settings.navbar.screen.toExit')
|
||||
: $t('settings.navbar.screen.toFull')">
|
||||
? '点击退出全屏模式'
|
||||
: '点击切换全屏模式'">
|
||||
<a-button
|
||||
class="nav-btn"
|
||||
type="outline"
|
||||
@@ -126,9 +126,9 @@
|
||||
</a-button>
|
||||
</a-tooltip>
|
||||
</li>
|
||||
<!-- 页面配置 -->
|
||||
<li v-if="false">
|
||||
<a-tooltip :content="$t('settings.title')">
|
||||
<!-- 偏好设置 -->
|
||||
<li>
|
||||
<a-tooltip content="偏好设置">
|
||||
<a-button
|
||||
class="nav-btn"
|
||||
type="outline"
|
||||
@@ -154,27 +154,21 @@
|
||||
<a-doption>
|
||||
<a-space @click="$router.push({ name: 'Info' })">
|
||||
<icon-user />
|
||||
<span>
|
||||
{{ $t('messageBox.userCenter') }}
|
||||
</span>
|
||||
<span>用户中心</span>
|
||||
</a-space>
|
||||
</a-doption>
|
||||
<!-- 用户设置 -->
|
||||
<a-doption>
|
||||
<a-space @click="$router.push({ name: 'Setting' })">
|
||||
<icon-settings />
|
||||
<span>
|
||||
{{ $t('messageBox.userSettings') }}
|
||||
</span>
|
||||
<span>用户设置</span>
|
||||
</a-space>
|
||||
</a-doption>
|
||||
<!-- 退出登录 -->
|
||||
<a-doption>
|
||||
<a-space @click="handleLogout">
|
||||
<icon-export />
|
||||
<span>
|
||||
{{ $t('messageBox.logout') }}
|
||||
</span>
|
||||
<span>退出登录</span>
|
||||
</a-space>
|
||||
</a-doption>
|
||||
</template>
|
||||
|
||||
@@ -7,10 +7,8 @@
|
||||
"hideMenu": false,
|
||||
"menuCollapse": false,
|
||||
"footer": true,
|
||||
"themeColor": "#165DFF",
|
||||
"menuWidth": 220,
|
||||
"globalSettings": false,
|
||||
"device": "desktop",
|
||||
"tabBar": true,
|
||||
"serverMenu": []
|
||||
"tabBar": true
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { RouteLocationNormalized, RouteRecordNormalized, RouteRecordRaw } from 'vue-router';
|
||||
import { useAppStore, useUserStore } from '@/store';
|
||||
import { useMenuStore, useUserStore } from '@/store';
|
||||
import { STATUS_ROUTER_LIST, WHITE_ROUTER_LIST } from '@/router/constants';
|
||||
|
||||
export default function usePermission() {
|
||||
const appStore = useAppStore();
|
||||
const menuStore = useMenuStore();
|
||||
const userStore = useUserStore();
|
||||
return {
|
||||
/**
|
||||
@@ -15,7 +15,7 @@ export default function usePermission() {
|
||||
return false;
|
||||
}
|
||||
// 检查路由是否存在于授权路由中
|
||||
const menuConfig = [...appStore.appAsyncMenus, ...WHITE_ROUTER_LIST, ...STATUS_ROUTER_LIST];
|
||||
const menuConfig = [...menuStore.appMenus, ...WHITE_ROUTER_LIST, ...STATUS_ROUTER_LIST];
|
||||
let exist = false;
|
||||
while (menuConfig.length && !exist) {
|
||||
const element = menuConfig.shift();
|
||||
|
||||
@@ -1,14 +1,8 @@
|
||||
import localeMessageBox from '@/components/message-box/locale/zh-CN';
|
||||
import localeLogin from '@/views/login/locale/zh-CN';
|
||||
|
||||
import localeWorkplace from '@/views/dashboard/workplace/locale/zh-CN';
|
||||
|
||||
import localeSettings from './zh-CN/settings';
|
||||
|
||||
export default {
|
||||
'navbar.action.locale': '切换为中文',
|
||||
...localeSettings,
|
||||
...localeMessageBox,
|
||||
...localeLogin,
|
||||
...localeWorkplace,
|
||||
};
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
export default {
|
||||
'settings.title': '页面配置',
|
||||
'settings.themeColor': '主题色',
|
||||
'settings.content': '内容区域',
|
||||
'settings.search': '搜索',
|
||||
'settings.language': '语言',
|
||||
'settings.navbar': '导航栏',
|
||||
'settings.menuWidth': '菜单宽度 (px)',
|
||||
'settings.navbar.theme.toLight': '点击切换为亮色模式',
|
||||
'settings.navbar.theme.toDark': '点击切换为暗黑模式',
|
||||
'settings.navbar.screen.toFull': '点击切换全屏模式',
|
||||
'settings.navbar.screen.toExit': '点击退出全屏模式',
|
||||
'settings.navbar.alerts': '消息通知',
|
||||
'settings.menu': '菜单栏',
|
||||
'settings.topMenu': '顶部菜单栏',
|
||||
'settings.tabBar': '多页签',
|
||||
'settings.footer': '底部',
|
||||
'settings.otherSettings': '其他设置',
|
||||
'settings.colorWeak': '色弱模式',
|
||||
'settings.alertContent':
|
||||
'配置之后仅是临时生效,要想真正作用于项目,点击下方的 "复制配置" 按钮,将配置替换到 settings.json 中即可。',
|
||||
'settings.copySettings': '复制配置',
|
||||
'settings.copySettings.message':
|
||||
'复制成功,请粘贴到 src/settings.json 文件中',
|
||||
'settings.close': '关闭',
|
||||
'settings.color.tooltip':
|
||||
'根据主题颜色生成的 10 个梯度色(将配置复制到项目中,主题色才能对亮色 / 暗黑模式同时生效)',
|
||||
};
|
||||
@@ -1,25 +1,25 @@
|
||||
import type { Router } from 'vue-router';
|
||||
import NProgress from 'nprogress';
|
||||
import { useAppStore } from '@/store';
|
||||
import { useMenuStore } from '@/store';
|
||||
import { NOT_FOUND_ROUTER_NAME, WHITE_ROUTER_LIST } from '../constants';
|
||||
import usePermission from '@/hooks/permission';
|
||||
|
||||
export default function setupPermissionGuard(router: Router) {
|
||||
router.beforeEach(async (to, from, next) => {
|
||||
const appStore = useAppStore();
|
||||
const menuStore = useMenuStore();
|
||||
// 未加载菜单 并且 不在白名单内 则加载菜单
|
||||
if (
|
||||
!appStore.menuFetched &&
|
||||
!menuStore.menuFetched &&
|
||||
!WHITE_ROUTER_LIST.find((el) => el.name === to.name)
|
||||
) {
|
||||
// 加载菜单
|
||||
await appStore.fetchMenuConfig();
|
||||
await menuStore.fetchMenu();
|
||||
}
|
||||
// 检测是否可以访问
|
||||
const permission = usePermission();
|
||||
const access = permission.accessRouter(to);
|
||||
// 刚进入页面时 重定向的 meta 是空的
|
||||
if (access && to.meta.locale === undefined && appStore.menuFetched) {
|
||||
if (access && to.meta.locale === undefined && menuStore.menuFetched) {
|
||||
to.meta = to.matched[to.matched.length - 1].meta;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { createPinia } from 'pinia';
|
||||
import useAppStore from './modules/app';
|
||||
import useMenuStore from './modules/menu';
|
||||
import useUserStore from './modules/user';
|
||||
import useTabBarStore from './modules/tab-bar';
|
||||
import useCacheStore from './modules/cache';
|
||||
@@ -8,6 +9,7 @@ const pinia = createPinia();
|
||||
|
||||
export {
|
||||
useAppStore,
|
||||
useMenuStore,
|
||||
useUserStore,
|
||||
useTabBarStore,
|
||||
useCacheStore
|
||||
|
||||
@@ -1,15 +1,10 @@
|
||||
import { defineStore } from 'pinia';
|
||||
import { Notification } from '@arco-design/web-vue';
|
||||
import type { RouteRecordNormalized } from 'vue-router';
|
||||
import defaultSettings from '@/config/settings.json';
|
||||
import { getMenuList } from '@/api/user/auth';
|
||||
import { AppState } from './types';
|
||||
import router from '@/router';
|
||||
|
||||
const useAppStore = defineStore('app', {
|
||||
export default defineStore('app', {
|
||||
state: (): AppState => ({
|
||||
...defaultSettings,
|
||||
menuFetched: false,
|
||||
}),
|
||||
|
||||
getters: {
|
||||
@@ -19,23 +14,16 @@ const useAppStore = defineStore('app', {
|
||||
appDevice(state: AppState) {
|
||||
return state.device;
|
||||
},
|
||||
appAsyncMenus(state: AppState): RouteRecordNormalized[] {
|
||||
return state.serverMenu as unknown as RouteRecordNormalized[];
|
||||
},
|
||||
},
|
||||
|
||||
actions: {
|
||||
/**
|
||||
* 更新配置
|
||||
*/
|
||||
// 更新配置
|
||||
updateSettings(partial: Partial<AppState>) {
|
||||
// @ts-ignore-next-line
|
||||
this.$patch(partial);
|
||||
this.$patch(partial as object);
|
||||
console.log(partial);
|
||||
},
|
||||
|
||||
/**
|
||||
* 修改颜色主题
|
||||
*/
|
||||
// 修改颜色主题
|
||||
toggleTheme(dark: boolean) {
|
||||
if (dark) {
|
||||
this.theme = 'dark';
|
||||
@@ -46,90 +34,14 @@ const useAppStore = defineStore('app', {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 切换设备
|
||||
*/
|
||||
// 切换设备
|
||||
toggleDevice(device: string) {
|
||||
this.device = device;
|
||||
},
|
||||
|
||||
/**
|
||||
* 切换菜单状态
|
||||
*/
|
||||
// 切换菜单状态
|
||||
toggleMenu(value: boolean) {
|
||||
this.hideMenu = value;
|
||||
},
|
||||
|
||||
/**
|
||||
* 加载菜单
|
||||
*/
|
||||
async fetchMenuConfig() {
|
||||
try {
|
||||
const { data } = await getMenuList();
|
||||
// @ts-ignore
|
||||
this.serverMenu = (data as Array<any>).map(s => {
|
||||
// 转换
|
||||
const convert = (item: any) => {
|
||||
// 设置路由属性
|
||||
const meta = {
|
||||
locale: item.name,
|
||||
icon: item.icon,
|
||||
order: item.sort,
|
||||
hideInMenu: item.visible === 0,
|
||||
hideChildrenInMenu: item.visible === 0,
|
||||
noAffix: item.visible === 0,
|
||||
ignoreCache: item.cache === 0,
|
||||
};
|
||||
// 获取 router
|
||||
const route = router.getRoutes().find(r => {
|
||||
return r.name === item.component;
|
||||
});
|
||||
// 设置 router meta
|
||||
if (route) {
|
||||
// 路由配置覆盖菜单配置
|
||||
route.meta = { ...meta, ...route.meta };
|
||||
}
|
||||
// 返回
|
||||
return {
|
||||
name: item.component,
|
||||
path: item.path,
|
||||
meta: meta,
|
||||
children: undefined as unknown
|
||||
};
|
||||
};
|
||||
// 构建父目录
|
||||
const menu = convert(s);
|
||||
// 构建子目录
|
||||
if (s.children) {
|
||||
menu.children = (s.children as Array<any>).map(convert);
|
||||
}
|
||||
return menu;
|
||||
});
|
||||
// 是否已加载过
|
||||
this.menuFetched = true;
|
||||
// 未配置菜单
|
||||
if (this.serverMenu.length === 0) {
|
||||
Notification.error({
|
||||
content: '该用户未配置菜单, 请先联系管理员配置',
|
||||
closable: true,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
Notification.error({
|
||||
content: '加载菜单失败',
|
||||
closable: true,
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 清空菜单
|
||||
*/
|
||||
clearMenu() {
|
||||
this.serverMenu = [];
|
||||
this.menuFetched = false;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export default useAppStore;
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import type { RouteRecordNormalized } from 'vue-router';
|
||||
|
||||
export interface AppState {
|
||||
theme: string;
|
||||
colorWeak: boolean;
|
||||
@@ -9,13 +7,10 @@ export interface AppState {
|
||||
hideMenu: boolean;
|
||||
menuCollapse: boolean;
|
||||
footer: boolean;
|
||||
themeColor: string;
|
||||
menuWidth: number;
|
||||
globalSettings: boolean;
|
||||
device: string;
|
||||
tabBar: boolean;
|
||||
serverMenu: RouteRecordNormalized[];
|
||||
menuFetched: boolean;
|
||||
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ import { CacheState } from './types';
|
||||
|
||||
export type CacheType = 'menus' | 'roles' | 'tags' | 'hostKeys' | 'hostIdentities'
|
||||
|
||||
const useCacheStore = defineStore('cache', {
|
||||
export default defineStore('cache', {
|
||||
state: (): CacheState => ({
|
||||
menus: [],
|
||||
roles: [],
|
||||
@@ -15,13 +15,9 @@ const useCacheStore = defineStore('cache', {
|
||||
getters: {},
|
||||
|
||||
actions: {
|
||||
/**
|
||||
* 设置
|
||||
*/
|
||||
// 设置
|
||||
set(name: CacheType, value: any) {
|
||||
this[name] = value;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
export default useCacheStore;
|
||||
|
||||
87
orion-ops-ui/src/store/modules/menu/index.ts
Normal file
87
orion-ops-ui/src/store/modules/menu/index.ts
Normal file
@@ -0,0 +1,87 @@
|
||||
import { defineStore } from 'pinia';
|
||||
import { Notification } from '@arco-design/web-vue';
|
||||
import type { RouteRecordNormalized } from 'vue-router';
|
||||
import { getMenuList } from '@/api/user/auth';
|
||||
import { MenuState } from './types';
|
||||
import router from '@/router';
|
||||
|
||||
export default defineStore('menu', {
|
||||
state: (): MenuState => ({
|
||||
serverMenus: [],
|
||||
menuFetched: false,
|
||||
}),
|
||||
|
||||
getters: {
|
||||
appMenus(state: MenuState): RouteRecordNormalized[] {
|
||||
return state.serverMenus as unknown as RouteRecordNormalized[];
|
||||
},
|
||||
},
|
||||
|
||||
actions: {
|
||||
// 加载菜单
|
||||
async fetchMenu() {
|
||||
try {
|
||||
const { data } = await getMenuList();
|
||||
// @ts-ignore
|
||||
this.serverMenus = (data as Array<any>).map(s => {
|
||||
// 转换
|
||||
const convert = (item: any) => {
|
||||
// 设置路由属性
|
||||
const meta = {
|
||||
locale: item.name,
|
||||
icon: item.icon,
|
||||
order: item.sort,
|
||||
hideInMenu: item.visible === 0,
|
||||
hideChildrenInMenu: item.visible === 0,
|
||||
noAffix: item.visible === 0,
|
||||
ignoreCache: item.cache === 0,
|
||||
};
|
||||
// 获取 router
|
||||
const route = router.getRoutes().find(r => {
|
||||
return r.name === item.component;
|
||||
});
|
||||
// 设置 router meta
|
||||
if (route) {
|
||||
// 路由配置覆盖菜单配置
|
||||
route.meta = { ...meta, ...route.meta };
|
||||
}
|
||||
// 返回
|
||||
return {
|
||||
name: item.component,
|
||||
path: item.path,
|
||||
meta: meta,
|
||||
children: undefined as unknown
|
||||
};
|
||||
};
|
||||
// 构建父目录
|
||||
const menu = convert(s);
|
||||
// 构建子目录
|
||||
if (s.children) {
|
||||
menu.children = (s.children as Array<any>).map(convert);
|
||||
}
|
||||
return menu;
|
||||
});
|
||||
// 是否已加载过
|
||||
this.menuFetched = true;
|
||||
// 未配置菜单
|
||||
if (this.serverMenus.length === 0) {
|
||||
Notification.error({
|
||||
content: '该用户未配置菜单, 请先联系管理员配置',
|
||||
closable: true,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
Notification.error({
|
||||
content: '加载菜单失败',
|
||||
closable: true,
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
// 清空菜单
|
||||
clearMenu() {
|
||||
this.serverMenus = [];
|
||||
this.menuFetched = false;
|
||||
},
|
||||
},
|
||||
});
|
||||
6
orion-ops-ui/src/store/modules/menu/types.ts
Normal file
6
orion-ops-ui/src/store/modules/menu/types.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import type { RouteRecordNormalized } from 'vue-router';
|
||||
|
||||
export interface MenuState {
|
||||
serverMenus: RouteRecordNormalized[];
|
||||
menuFetched: boolean;
|
||||
}
|
||||
@@ -3,7 +3,7 @@ import { DEFAULT_ROUTE_NAME, DEFAULT_TAB } from '@/router/constants';
|
||||
import { isString } from '@/utils/is';
|
||||
import { TabBarState, TagProps } from './types';
|
||||
|
||||
const useTabBarStore = defineStore('tabBar', {
|
||||
export default defineStore('tabBar', {
|
||||
state: (): TabBarState => ({
|
||||
cacheTabList: new Set([DEFAULT_ROUTE_NAME]),
|
||||
tagList: [DEFAULT_TAB],
|
||||
@@ -19,9 +19,7 @@ const useTabBarStore = defineStore('tabBar', {
|
||||
},
|
||||
|
||||
actions: {
|
||||
/**
|
||||
* 添加 tab
|
||||
*/
|
||||
// 添加 tab
|
||||
addTab(tag: TagProps, ignoreCache: boolean) {
|
||||
this.tagList.push(tag);
|
||||
if (!ignoreCache) {
|
||||
@@ -29,31 +27,23 @@ const useTabBarStore = defineStore('tabBar', {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 移除 tab
|
||||
*/
|
||||
// 移除 tab
|
||||
deleteTab(idx: number, tag: TagProps) {
|
||||
this.tagList.splice(idx, 1);
|
||||
this.cacheTabList.delete(tag.name);
|
||||
},
|
||||
|
||||
/**
|
||||
* 添加缓存
|
||||
*/
|
||||
// 添加缓存
|
||||
addCache(name: string) {
|
||||
if (isString(name) && name !== '') this.cacheTabList.add(name);
|
||||
},
|
||||
|
||||
/**
|
||||
* 删除缓存
|
||||
*/
|
||||
// 删除缓存
|
||||
deleteCache(tag: TagProps) {
|
||||
this.cacheTabList.delete(tag.name);
|
||||
},
|
||||
|
||||
/**
|
||||
* 重设缓存
|
||||
*/
|
||||
// 重设缓存
|
||||
freshTabList(tags: TagProps[]) {
|
||||
this.tagList = tags;
|
||||
this.cacheTabList.clear();
|
||||
@@ -63,9 +53,7 @@ const useTabBarStore = defineStore('tabBar', {
|
||||
.forEach((x) => this.cacheTabList.add(x));
|
||||
},
|
||||
|
||||
/**
|
||||
* 重设 tab
|
||||
*/
|
||||
// 重设 tab
|
||||
resetTabList() {
|
||||
this.tagList = [DEFAULT_TAB];
|
||||
this.cacheTabList.clear();
|
||||
@@ -73,5 +61,3 @@ const useTabBarStore = defineStore('tabBar', {
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export default useTabBarStore;
|
||||
|
||||
@@ -4,9 +4,9 @@ import { clearToken, setToken } from '@/utils/auth';
|
||||
import { md5 } from '@/utils';
|
||||
import { removeRouteListener } from '@/utils/route-listener';
|
||||
import { UserState } from './types';
|
||||
import useAppStore from '../app';
|
||||
import { useMenuStore, useTabBarStore } from '@/store';
|
||||
|
||||
const useUserStore = defineStore('user', {
|
||||
export default defineStore('user', {
|
||||
state: (): UserState => ({
|
||||
id: undefined,
|
||||
username: undefined,
|
||||
@@ -23,16 +23,12 @@ const useUserStore = defineStore('user', {
|
||||
},
|
||||
|
||||
actions: {
|
||||
/**
|
||||
* 设置用户信息
|
||||
*/
|
||||
// 设置用户信息
|
||||
setInfo(partial: Partial<UserState>) {
|
||||
this.$patch(partial);
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取用户信息
|
||||
*/
|
||||
// 获取用户信息
|
||||
async info() {
|
||||
const { data } = await getUserPermission();
|
||||
this.setInfo({
|
||||
@@ -45,9 +41,7 @@ const useUserStore = defineStore('user', {
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 登录
|
||||
*/
|
||||
// 登录
|
||||
async login(loginForm: LoginRequest) {
|
||||
try {
|
||||
const loginRequest: LoginRequest = {
|
||||
@@ -64,9 +58,7 @@ const useUserStore = defineStore('user', {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 登出
|
||||
*/
|
||||
// 登出
|
||||
async logout() {
|
||||
try {
|
||||
await userLogout();
|
||||
@@ -77,19 +69,18 @@ const useUserStore = defineStore('user', {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 登出回调
|
||||
*/
|
||||
// 登出回调
|
||||
logoutCallBack() {
|
||||
this.$reset();
|
||||
clearToken();
|
||||
// 移除路由监听器
|
||||
removeRouteListener();
|
||||
// 清空菜单
|
||||
const appStore = useAppStore();
|
||||
appStore.clearMenu();
|
||||
const menuStore = useMenuStore();
|
||||
menuStore.clearMenu();
|
||||
// 清除 tabs
|
||||
const tabBarStore = useTabBarStore();
|
||||
tabBarStore.resetTabList();
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export default useUserStore;
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<a-input v-model="formModel.nickname" placeholder="请输入花名" allow-clear />
|
||||
</a-form-item>
|
||||
<!-- 用户状态 -->
|
||||
<a-form-item field="status" label="用户状态 " label-col-flex="50px">
|
||||
<a-form-item field="status" label="用户状态" label-col-flex="50px">
|
||||
<a-select
|
||||
v-model="formModel.status"
|
||||
:options="toOptions(UserStatusEnum)"
|
||||
|
||||
Reference in New Issue
Block a user