Compare commits

...

7 Commits

Author SHA1 Message Date
xiaoxian521
4d57c9e3d7 release: update 3.9.5 2022-12-13 14:37:56 +08:00
xiaoxian521
1bcf391513 fix: 修复暗黑主题样式问题 2022-12-09 20:35:50 +08:00
xiaoxian521
30af2b78fe chore: update 2022-12-09 12:45:47 +08:00
xiaoxian521
0b1bd19179 fix: 修复动态路由 rank 问题 2022-12-07 17:22:07 +08:00
xiaoxian521
4ccf200059 perf: 优化路由 rank,当rank 不存在时,根据顺序自动创建,首页路由永远在第一位 2022-12-06 21:18:12 +08:00
xiaoxian521
27056e7560 release: update 3.9.4 2022-12-05 14:28:46 +08:00
xiaoxian521
132fbbade3 release: update 3.9.3 2022-12-04 18:00:16 +08:00
35 changed files with 4205 additions and 2272 deletions

View File

@@ -7,7 +7,7 @@ import { Plugin as importToCDN } from "vite-plugin-cdn-import";
* 注意上面提到的仅限外网使用也不是完全肯定的如果你们公司内网部署的有相关js、css文件也可以将下面配置对应改一下整一套内网版cdn * 注意上面提到的仅限外网使用也不是完全肯定的如果你们公司内网部署的有相关js、css文件也可以将下面配置对应改一下整一套内网版cdn
*/ */
export const cdn = importToCDN({ export const cdn = importToCDN({
//prodUrl解释 name: 对应下面modules的nameversion: 自动读取本地package.json中dependencies依赖中对应包的版本号path: 对应下面modules的path //prodUrl解释 name: 对应下面modules的nameversion: 自动读取本地package.json中dependencies依赖中对应包的版本号path: 对应下面modules的path当然也可写完整路径会替换prodUrl
prodUrl: "https://cdn.bootcdn.net/ajax/libs/{name}/{version}/{path}", prodUrl: "https://cdn.bootcdn.net/ajax/libs/{name}/{version}/{path}",
modules: [ modules: [
{ {
@@ -51,12 +51,6 @@ export const cdn = importToCDN({
name: "echarts", name: "echarts",
var: "echarts", var: "echarts",
path: "echarts.min.js" path: "echarts.min.js"
},
{
name: "lodash",
var: "lodash",
// 可写`完整路径`,会替换`prodUrl`
path: "https://cdn.bootcdn.net/ajax/libs/lodash.js/4.17.21/lodash.min.js"
} }
] ]
}); });

View File

@@ -10,15 +10,9 @@ const include = [
"dayjs", "dayjs",
"axios", "axios",
"pinia", "pinia",
"lodash",
"echarts", "echarts",
"xe-utils",
"vxe-table",
"js-cookie", "js-cookie",
"lodash-es",
"@vueuse/core", "@vueuse/core",
"lodash-unified",
"@ctrl/tinycolor",
"@pureadmin/utils", "@pureadmin/utils",
"responsive-storage", "responsive-storage",
"element-resize-detector" "element-resize-detector"

View File

@@ -1,8 +1,6 @@
// 模拟后端动态生成路由 // 模拟后端动态生成路由
import { MockMethod } from "vite-plugin-mock"; import { MockMethod } from "vite-plugin-mock";
import Lollipop from "@iconify-icons/ep/lollipop";
/** /**
* roles页面级别权限这里模拟二种 "admin"、"common" * roles页面级别权限这里模拟二种 "admin"、"common"
* admin管理员角色 * admin管理员角色
@@ -13,7 +11,7 @@ const permissionRouter = {
path: "/permission", path: "/permission",
meta: { meta: {
title: "权限管理", title: "权限管理",
icon: Lollipop, icon: "lollipop",
rank: 10 rank: 10
}, },
children: [ children: [

View File

@@ -1,6 +1,6 @@
{ {
"name": "pure-admin-thin", "name": "pure-admin-thin",
"version": "3.9.2", "version": "3.9.5",
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "NODE_OPTIONS=--max-old-space-size=4096 vite", "dev": "NODE_OPTIONS=--max-old-space-size=4096 vite",
@@ -29,10 +29,9 @@
"not op_mini all" "not op_mini all"
], ],
"dependencies": { "dependencies": {
"@ctrl/tinycolor": "^3.4.1",
"@pureadmin/descriptions": "^1.1.0", "@pureadmin/descriptions": "^1.1.0",
"@pureadmin/table": "^1.8.2", "@pureadmin/table": "^1.9.0",
"@pureadmin/utils": "^1.7.2", "@pureadmin/utils": "^1.8.5",
"@vueuse/core": "^9.6.0", "@vueuse/core": "^9.6.0",
"@vueuse/motion": "2.0.0-beta.12", "@vueuse/motion": "2.0.0-beta.12",
"animate.css": "^4.1.1", "animate.css": "^4.1.1",
@@ -42,9 +41,6 @@
"element-plus": "^2.2.26", "element-plus": "^2.2.26",
"element-resize-detector": "^1.2.4", "element-resize-detector": "^1.2.4",
"js-cookie": "^3.0.1", "js-cookie": "^3.0.1",
"lodash": "^4.17.21",
"lodash-es": "^4.17.21",
"lodash-unified": "^1.0.2",
"mitt": "^3.0.0", "mitt": "^3.0.0",
"mockjs": "^1.1.0", "mockjs": "^1.1.0",
"nprogress": "^0.2.0", "nprogress": "^0.2.0",
@@ -54,9 +50,7 @@
"responsive-storage": "^2.1.0", "responsive-storage": "^2.1.0",
"vue": "^3.2.45", "vue": "^3.2.45",
"vue-router": "^4.1.6", "vue-router": "^4.1.6",
"vue-types": "^4.2.1", "vue-types": "^4.2.1"
"vxe-table": "^4.3.6",
"xe-utils": "^3.5.7"
}, },
"devDependencies": { "devDependencies": {
"@commitlint/cli": "13.1.0", "@commitlint/cli": "13.1.0",
@@ -67,8 +61,6 @@
"@pureadmin/theme": "^2.4.0", "@pureadmin/theme": "^2.4.0",
"@types/element-resize-detector": "1.1.3", "@types/element-resize-detector": "1.1.3",
"@types/js-cookie": "^3.0.1", "@types/js-cookie": "^3.0.1",
"@types/lodash": "^4.14.180",
"@types/lodash-es": "^4.17.6",
"@types/mockjs": "^1.0.7", "@types/mockjs": "^1.0.7",
"@types/node": "^18.11.9", "@types/node": "^18.11.9",
"@types/nprogress": "0.2.0", "@types/nprogress": "0.2.0",
@@ -86,7 +78,6 @@
"eslint": "^8.8.0", "eslint": "^8.8.0",
"eslint-plugin-prettier": "^4.0.0", "eslint-plugin-prettier": "^4.0.0",
"eslint-plugin-vue": "^9.7.0", "eslint-plugin-vue": "^9.7.0",
"font-awesome": "^4.7.0",
"husky": "^7.0.4", "husky": "^7.0.4",
"lint-staged": "11.1.2", "lint-staged": "11.1.2",
"picocolors": "^1.0.0", "picocolors": "^1.0.0",

5970
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
{ {
"Version": "3.9.2", "Version": "3.9.5",
"Title": "PureAdmin", "Title": "PureAdmin",
"FixedHeader": true, "FixedHeader": true,
"HiddenSideBar": false, "HiddenSideBar": false,

View File

@@ -0,0 +1,14 @@
import { addIcon } from "@iconify/vue/dist/offline";
/**
* 这里存放本地图标,在 src/layout/index.vue 文件中加载,避免在首启动加载
*/
// 本地菜单图标后端在路由的icon中返回对应的图标字符串并且前端在此处使用addIcon添加即可渲染菜单图标
import HomeFilled from "@iconify-icons/ep/home-filled";
import InformationLine from "@iconify-icons/ri/information-line";
import Lollipop from "@iconify-icons/ep/lollipop";
addIcon("homeFilled", HomeFilled);
addIcon("informationLine", InformationLine);
addIcon("lollipop", Lollipop);

View File

@@ -5,7 +5,7 @@ export const auth: Directive = {
mounted(el: HTMLElement, binding: DirectiveBinding) { mounted(el: HTMLElement, binding: DirectiveBinding) {
const { value } = binding; const { value } = binding;
if (value) { if (value) {
!hasAuth(value) && el.parentNode.removeChild(el); !hasAuth(value) && el.parentNode?.removeChild(el);
} else { } else {
throw new Error("need auths! Like v-auth=\"['btn.add','btn.edit']\""); throw new Error("need auths! Like v-auth=\"['btn.add','btn.edit']\"");
} }

View File

@@ -1,6 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
import { useRouter } from "vue-router"; import { useRouter } from "vue-router";
import { cloneDeep } from "lodash-unified"; import { cloneDeep } from "@pureadmin/utils";
import SearchResult from "./SearchResult.vue"; import SearchResult from "./SearchResult.vue";
import SearchFooter from "./SearchFooter.vue"; import SearchFooter from "./SearchFooter.vue";
import { deleteChildren } from "@/utils/tree"; import { deleteChildren } from "@/utils/tree";

View File

@@ -1,4 +1,13 @@
<script setup lang="ts"> <script setup lang="ts">
import {
ref,
unref,
watch,
reactive,
computed,
nextTick,
onBeforeMount
} from "vue";
import { import {
useDark, useDark,
debounce, debounce,
@@ -17,7 +26,6 @@ import { useNav } from "@/layout/hooks/useNav";
import { useAppStoreHook } from "@/store/modules/app"; import { useAppStoreHook } from "@/store/modules/app";
import { toggleTheme } from "@pureadmin/theme/dist/browser-utils"; import { toggleTheme } from "@pureadmin/theme/dist/browser-utils";
import { useMultiTagsStoreHook } from "@/store/modules/multiTags"; import { useMultiTagsStoreHook } from "@/store/modules/multiTags";
import { ref, unref, watch, reactive, computed, nextTick } from "vue";
import { useDataThemeChange } from "@/layout/hooks/useDataThemeChange"; import { useDataThemeChange } from "@/layout/hooks/useDataThemeChange";
import dayIcon from "@/assets/svg/day.svg?component"; import dayIcon from "@/assets/svg/day.svg?component";
@@ -124,8 +132,8 @@ const multiTagsCacheChange = () => {
/** 清空缓存并返回登录页 */ /** 清空缓存并返回登录页 */
function onReset() { function onReset() {
removeToken(); removeToken();
storageLocal.clear(); storageLocal().clear();
storageSession.clear(); storageSession().clear();
const { Grey, Weak, MultiTagsCache, EpThemeColor, Layout } = getConfig(); const { Grey, Weak, MultiTagsCache, EpThemeColor, Layout } = getConfig();
useAppStoreHook().setLayout(Layout); useAppStoreHook().setLayout(Layout);
setEpThemeColor(EpThemeColor); setEpThemeColor(EpThemeColor);
@@ -189,16 +197,6 @@ function setLayoutModel(layout: string) {
useAppStoreHook().setLayout(layout); useAppStoreHook().setLayout(layout);
} }
/* 初始化项目配置 */
nextTick(() => {
settings.greyVal &&
document.querySelector("html")?.setAttribute("class", "html-grey");
settings.weakVal &&
document.querySelector("html")?.setAttribute("class", "html-weakness");
settings.tabsVal && tagsChange();
dataThemeChange();
});
watch($storage, ({ layout }) => { watch($storage, ({ layout }) => {
switch (layout["layout"]) { switch (layout["layout"]) {
case "vertical": case "vertical":
@@ -218,6 +216,18 @@ watch($storage, ({ layout }) => {
break; break;
} }
}); });
onBeforeMount(() => {
dataThemeChange();
/* 初始化项目配置 */
nextTick(() => {
settings.greyVal &&
document.querySelector("html")?.setAttribute("class", "html-grey");
settings.weakVal &&
document.querySelector("html")?.setAttribute("class", "html-weakness");
settings.tabsVal && tagsChange();
});
});
</script> </script>
<template> <template>

View File

@@ -35,7 +35,10 @@ watch(
</script> </script>
<template> <template>
<div class="horizontal-header"> <div
v-loading="usePermissionStoreHook().wholeMenus.length === 0"
class="horizontal-header"
>
<div class="horizontal-header-left" @click="backHome"> <div class="horizontal-header-left" @click="backHome">
<FontIcon icon="team-iconlogo" svg style="width: 35px; height: 35px" /> <FontIcon icon="team-iconlogo" svg style="width: 35px; height: 35px" />
<h4>{{ title }}</h4> <h4>{{ title }}</h4>
@@ -93,6 +96,10 @@ watch(
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>
:deep(.el-loading-mask) {
opacity: 0.45;
}
.logout { .logout {
max-width: 120px; max-width: 120px;

View File

@@ -51,7 +51,11 @@ watch(
</script> </script>
<template> <template>
<div v-if="device !== 'mobile'" class="horizontal-header"> <div
v-if="device !== 'mobile'"
class="horizontal-header"
v-loading="usePermissionStoreHook().wholeMenus.length === 0"
>
<el-menu <el-menu
router router
ref="menuRef" ref="menuRef"
@@ -124,6 +128,10 @@ watch(
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>
:deep(.el-loading-mask) {
opacity: 0.45;
}
.logout { .logout {
max-width: 120px; max-width: 120px;

View File

@@ -202,7 +202,7 @@ function resolvePath(routePath) {
placement="top" placement="top"
:effect="tooltipEffect" :effect="tooltipEffect"
:offset="-10" :offset="-10"
:disabled="!isCollapse && !onlyOneChild.showTooltip" :disabled="!onlyOneChild.showTooltip"
> >
<template #content> <template #content>
{{ onlyOneChild.meta.title }} {{ onlyOneChild.meta.title }}

View File

@@ -12,7 +12,8 @@ import { usePermissionStoreHook } from "@/store/modules/permission";
const route = useRoute(); const route = useRoute();
const showLogo = ref( const showLogo = ref(
storageLocal.getItem<StorageConfigs>("responsive-configure")?.showLogo ?? true storageLocal().getItem<StorageConfigs>("responsive-configure")?.showLogo ??
true
); );
const { routers, device, pureApp, isCollapse, menuSelect, toggleSideBar } = const { routers, device, pureApp, isCollapse, menuSelect, toggleSideBar } =
@@ -59,7 +60,10 @@ watch(
</script> </script>
<template> <template>
<div :class="['sidebar-container', showLogo ? 'has-logo' : '']"> <div
v-loading="menuData.length === 0"
:class="['sidebar-container', showLogo ? 'has-logo' : '']"
>
<Logo v-if="showLogo" :collapse="isCollapse" /> <Logo v-if="showLogo" :collapse="isCollapse" />
<el-scrollbar <el-scrollbar
wrap-class="scrollbar-wrapper" wrap-class="scrollbar-wrapper"
@@ -91,3 +95,9 @@ watch(
/> />
</div> </div>
</template> </template>
<style scoped>
:deep(.el-loading-mask) {
opacity: 0.45;
}
</style>

View File

@@ -3,7 +3,7 @@ import { emitter } from "@/utils/mitt";
import { RouteConfigs } from "../../types"; import { RouteConfigs } from "../../types";
import { useTags } from "../../hooks/useTag"; import { useTags } from "../../hooks/useTag";
import { routerArrays } from "@/layout/types"; import { routerArrays } from "@/layout/types";
import { isEqual, isEmpty } from "lodash-unified"; import { isEqual, isAllEmpty } from "@pureadmin/utils";
import { useSettingStoreHook } from "@/store/modules/settings"; import { useSettingStoreHook } from "@/store/modules/settings";
import { ref, watch, unref, nextTick, onBeforeMount } from "vue"; import { ref, watch, unref, nextTick, onBeforeMount } from "vue";
import { handleAliveRoute, delAliveRoutes } from "@/router/utils"; import { handleAliveRoute, delAliveRoutes } from "@/router/utils";
@@ -347,7 +347,7 @@ function showMenuModel(
const allRoute = multiTags.value; const allRoute = multiTags.value;
const routeLength = multiTags.value.length; const routeLength = multiTags.value.length;
let currentIndex = -1; let currentIndex = -1;
if (isEmpty(query)) { if (isAllEmpty(query)) {
currentIndex = allRoute.findIndex(v => v.path === currentPath); currentIndex = allRoute.findIndex(v => v.path === currentPath);
} else { } else {
currentIndex = allRoute.findIndex(v => isEqual(v.query, query)); currentIndex = allRoute.findIndex(v => isEqual(v.query, query));

View File

@@ -1,9 +1,7 @@
import { ref } from "vue"; import { ref } from "vue";
import { getConfig } from "@/config"; import { getConfig } from "@/config";
import { find } from "lodash-unified";
import { useLayout } from "./useLayout"; import { useLayout } from "./useLayout";
import { themeColorsType } from "../types"; import { themeColorsType } from "../types";
import { TinyColor } from "@ctrl/tinycolor";
import { useGlobal } from "@pureadmin/utils"; import { useGlobal } from "@pureadmin/utils";
import { useEpThemeStoreHook } from "@/store/modules/epTheme"; import { useEpThemeStoreHook } from "@/store/modules/epTheme";
import { import {
@@ -56,35 +54,27 @@ export function useDataThemeChange() {
if (theme === "default" || theme === "light") { if (theme === "default" || theme === "light") {
setEpThemeColor(getConfig().EpThemeColor); setEpThemeColor(getConfig().EpThemeColor);
} else { } else {
const colors = find(themeColors.value, { themeColor: theme }); const colors = themeColors.value.find(v => v.themeColor === theme);
setEpThemeColor(colors.color); setEpThemeColor(colors.color);
} }
} }
/** function setPropertyPrimary(mode: string, i: number, color: string) {
* @description 自动计算hover和active颜色 document.documentElement.style.setProperty(
* @see {@link https://element-plus.org/zh-CN/component/button.html#%E8%87%AA%E5%AE%9A%E4%B9%89%E9%A2%9C%E8%89%B2} `--el-color-primary-${mode}-${i}`,
*/ dataTheme.value ? darken(color, i / 10) : lighten(color, i / 10)
const shadeBgColor = (color: string): string => { );
return new TinyColor(color).shade(10).toString(); }
};
/** 设置 `element-plus` 主题色 */ /** 设置 `element-plus` 主题色 */
const setEpThemeColor = (color: string) => { const setEpThemeColor = (color: string) => {
useEpThemeStoreHook().setEpThemeColor(color); useEpThemeStoreHook().setEpThemeColor(color);
body.style.setProperty("--el-color-primary-active", shadeBgColor(color));
document.documentElement.style.setProperty("--el-color-primary", color); document.documentElement.style.setProperty("--el-color-primary", color);
for (let i = 1; i <= 9; i++) {
document.documentElement.style.setProperty(
`--el-color-primary-light-${i}`,
lighten(color, i / 10)
);
}
for (let i = 1; i <= 2; i++) { for (let i = 1; i <= 2; i++) {
document.documentElement.style.setProperty( setPropertyPrimary("dark", i, color);
`--el-color-primary-dark-${i}`, }
darken(color, i / 10) for (let i = 1; i <= 9; i++) {
); setPropertyPrimary("light", i, color);
} }
}; };

View File

@@ -40,13 +40,13 @@ export function useTags() {
/** 显示模式,默认灵动模式 */ /** 显示模式,默认灵动模式 */
const showModel = ref( const showModel = ref(
storageLocal.getItem<StorageConfigs>("responsive-configure")?.showModel || storageLocal().getItem<StorageConfigs>("responsive-configure")?.showModel ||
"smart" "smart"
); );
/** 是否隐藏标签页,默认显示 */ /** 是否隐藏标签页,默认显示 */
const showTags = const showTags =
ref( ref(
storageLocal.getItem<StorageConfigs>("responsive-configure").hideTabs storageLocal().getItem<StorageConfigs>("responsive-configure").hideTabs
) ?? ref("false"); ) ?? ref("false");
const multiTags: any = computed(() => { const multiTags: any = computed(() => {
return useMultiTagsStoreHook().multiTags; return useMultiTagsStoreHook().multiTags;
@@ -194,11 +194,11 @@ export function useTags() {
onMounted(() => { onMounted(() => {
if (!showModel.value) { if (!showModel.value) {
const configure = storageLocal.getItem<StorageConfigs>( const configure = storageLocal().getItem<StorageConfigs>(
"responsive-configure" "responsive-configure"
); );
configure.showModel = "card"; configure.showModel = "card";
storageLocal.setItem("responsive-configure", configure); storageLocal().setItem("responsive-configure", configure);
} }
}); });

View File

@@ -1,7 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
import "animate.css"; import "animate.css";
// vxe-table的所有icon不支持component模式间接依赖了font-awesome // 引入 src/components/ReIcon/src/offlineIcon.ts 文件中所有使用addIcon添加过的本地图标
// import "font-awesome/css/font-awesome.min.css"; import "@/components/ReIcon/src/offlineIcon";
import { setType } from "./types"; import { setType } from "./types";
import { emitter } from "@/utils/mitt"; import { emitter } from "@/utils/mitt";
import { useLayout } from "./hooks/useLayout"; import { useLayout } from "./hooks/useLayout";

View File

@@ -2,19 +2,14 @@
* @description ⚠️:此文件仅供主题插件使用,请不要在此文件中导出别的工具函数(仅在页面加载前运行) * @description ⚠️:此文件仅供主题插件使用,请不要在此文件中导出别的工具函数(仅在页面加载前运行)
*/ */
import { EpThemeColor } from "../../../public/serverConfig.json";
type MultipleScopeVarsItem = { type MultipleScopeVarsItem = {
scopeName: string; scopeName: string;
varsContent: string; varsContent: string;
}; };
/** 将vxe默认主题色和ep默认主题色保持一致 */
const vxeColor = EpThemeColor;
/** 预设主题色 */ /** 预设主题色 */
const themeColors = { const themeColors = {
default: { default: {
vxeColor,
subMenuActiveText: "#fff", subMenuActiveText: "#fff",
menuBg: "#001529", menuBg: "#001529",
menuHover: "#4091f7", menuHover: "#4091f7",
@@ -26,7 +21,6 @@ const themeColors = {
menuActiveBefore: "#4091f7" menuActiveBefore: "#4091f7"
}, },
light: { light: {
vxeColor,
subMenuActiveText: "#409eff", subMenuActiveText: "#409eff",
menuBg: "#fff", menuBg: "#fff",
menuHover: "#e0ebf6", menuHover: "#e0ebf6",
@@ -38,7 +32,6 @@ const themeColors = {
menuActiveBefore: "#4091f7" menuActiveBefore: "#4091f7"
}, },
dusk: { dusk: {
vxeColor: "#f5222d",
subMenuActiveText: "#fff", subMenuActiveText: "#fff",
menuBg: "#2a0608", menuBg: "#2a0608",
menuHover: "#e13c39", menuHover: "#e13c39",
@@ -50,7 +43,6 @@ const themeColors = {
menuActiveBefore: "#e13c39" menuActiveBefore: "#e13c39"
}, },
volcano: { volcano: {
vxeColor: "#fa541c",
subMenuActiveText: "#fff", subMenuActiveText: "#fff",
menuBg: "#2b0e05", menuBg: "#2b0e05",
menuHover: "#e85f33", menuHover: "#e85f33",
@@ -62,7 +54,6 @@ const themeColors = {
menuActiveBefore: "#e85f33" menuActiveBefore: "#e85f33"
}, },
yellow: { yellow: {
vxeColor: "#fadb14",
subMenuActiveText: "#d25f00", subMenuActiveText: "#d25f00",
menuBg: "#2b2503", menuBg: "#2b2503",
menuHover: "#f6da4d", menuHover: "#f6da4d",
@@ -74,7 +65,6 @@ const themeColors = {
menuActiveBefore: "#f6da4d" menuActiveBefore: "#f6da4d"
}, },
mingQing: { mingQing: {
vxeColor: "#13c2c2",
subMenuActiveText: "#fff", subMenuActiveText: "#fff",
menuBg: "#032121", menuBg: "#032121",
menuHover: "#59bfc1", menuHover: "#59bfc1",
@@ -86,7 +76,6 @@ const themeColors = {
menuActiveBefore: "#59bfc1" menuActiveBefore: "#59bfc1"
}, },
auroraGreen: { auroraGreen: {
vxeColor: "#52c41a",
subMenuActiveText: "#fff", subMenuActiveText: "#fff",
menuBg: "#0b1e15", menuBg: "#0b1e15",
menuHover: "#60ac80", menuHover: "#60ac80",
@@ -98,7 +87,6 @@ const themeColors = {
menuActiveBefore: "#60ac80" menuActiveBefore: "#60ac80"
}, },
pink: { pink: {
vxeColor: "#eb2f96",
subMenuActiveText: "#fff", subMenuActiveText: "#fff",
menuBg: "#28081a", menuBg: "#28081a",
menuHover: "#d84493", menuHover: "#d84493",
@@ -110,7 +98,6 @@ const themeColors = {
menuActiveBefore: "#d84493" menuActiveBefore: "#d84493"
}, },
saucePurple: { saucePurple: {
vxeColor: "#722ed1",
subMenuActiveText: "#fff", subMenuActiveText: "#fff",
menuBg: "#130824", menuBg: "#130824",
menuHover: "#693ac9", menuHover: "#693ac9",
@@ -132,7 +119,6 @@ export const genScssMultipleScopeVars = (): MultipleScopeVarsItem[] => {
result.push({ result.push({
scopeName: `layout-theme-${key}`, scopeName: `layout-theme-${key}`,
varsContent: ` varsContent: `
$vxe-primary-color: ${themeColors[key].vxeColor} !default;
$subMenuActiveText: ${themeColors[key].subMenuActiveText} !default; $subMenuActiveText: ${themeColors[key].subMenuActiveText} !default;
$menuBg: ${themeColors[key].menuBg} !default; $menuBg: ${themeColors[key].menuBg} !default;
$menuHover: ${themeColors[key].menuHover} !default; $menuHover: ${themeColors[key].menuHover} !default;

View File

@@ -1,5 +1,4 @@
import type { IconifyIcon } from "@iconify/vue"; import type { IconifyIcon } from "@iconify/vue";
import HomeFilled from "@iconify-icons/ep/home-filled";
export const routerArrays: Array<RouteConfigs> = [ export const routerArrays: Array<RouteConfigs> = [
{ {
@@ -7,7 +6,7 @@ export const routerArrays: Array<RouteConfigs> = [
parentPath: "/", parentPath: "/",
meta: { meta: {
title: "首页", title: "首页",
icon: HomeFilled icon: "homeFilled"
} }
} }
]; ];

View File

@@ -6,7 +6,6 @@ import { getServerConfig } from "./config";
import { createApp, Directive } from "vue"; import { createApp, Directive } from "vue";
import { MotionPlugin } from "@vueuse/motion"; import { MotionPlugin } from "@vueuse/motion";
// import { useEcharts } from "@/plugins/echarts"; // import { useEcharts } from "@/plugins/echarts";
// import { useTable } from "@/plugins/vxe-table";
import { injectResponsiveStorage } from "@/utils/responsive"; import { injectResponsiveStorage } from "@/utils/responsive";
// import Table from "@pureadmin/table"; // import Table from "@pureadmin/table";
@@ -52,6 +51,5 @@ getServerConfig(app).then(async config => {
// .use(useEcharts); // .use(useEcharts);
// .use(Table); // .use(Table);
// .use(PureDescriptions); // .use(PureDescriptions);
// .use(useTable);
app.mount("#app"); app.mount("#app");
}); });

View File

@@ -1,6 +0,0 @@
@import "vxe-table/styles/variable.scss";
@import "vxe-table/styles/modules.scss";
i {
border-color: initial;
}

View File

@@ -1,97 +0,0 @@
import "xe-utils";
import "./index.scss";
import { App } from "vue";
import {
// 核心
VXETable,
// 表格功能
Icon,
Filter,
Edit,
Menu,
Export,
Keyboard,
Validator,
// 可选组件
Column,
Colgroup,
Grid,
Tooltip,
Toolbar,
Pager,
Form,
FormItem,
FormGather,
Checkbox,
CheckboxGroup,
Radio,
RadioGroup,
RadioButton,
Switch,
Input,
Select,
Optgroup,
Option,
Textarea,
Button,
Modal,
List,
Pulldown,
// 表格
Table
} from "vxe-table";
// 全局默认参数
VXETable.setup({
size: "medium",
version: 0,
zIndex: 1002,
table: {
// 自动监听父元素的变化去重新计算表格
autoResize: true,
// 鼠标移到行是否要高亮显示
highlightHoverRow: true
},
input: {
clearable: true
}
});
export function useTable(app: App) {
app
.use(Icon)
.use(Filter)
.use(Edit)
.use(Menu)
.use(Export)
.use(Keyboard)
.use(Validator)
// 可选组件
.use(Column)
.use(Colgroup)
.use(Grid)
.use(Tooltip)
.use(Toolbar)
.use(Pager)
.use(Form)
.use(FormItem)
.use(FormGather)
.use(Checkbox)
.use(CheckboxGroup)
.use(Radio)
.use(RadioGroup)
.use(RadioButton)
.use(Switch)
.use(Input)
.use(Select)
.use(Optgroup)
.use(Option)
.use(Textarea)
.use(Button)
.use(Modal)
.use(List)
.use(Pulldown)
// 安装表格
.use(Table);
}

View File

@@ -1,7 +1,6 @@
// import "@/utils/sso"; // import "@/utils/sso";
import { getConfig } from "@/config"; import { getConfig } from "@/config";
import NProgress from "@/utils/progress"; import NProgress from "@/utils/progress";
import { findIndex } from "lodash-unified";
import { sessionKey, type DataInfo } from "@/utils/auth"; import { sessionKey, type DataInfo } from "@/utils/auth";
import { useMultiTagsStoreHook } from "@/store/modules/multiTags"; import { useMultiTagsStoreHook } from "@/store/modules/multiTags";
import { usePermissionStoreHook } from "@/store/modules/permission"; import { usePermissionStoreHook } from "@/store/modules/permission";
@@ -105,7 +104,7 @@ router.beforeEach((to: toRouteType, _from, next) => {
handleAliveRoute(newMatched); handleAliveRoute(newMatched);
} }
} }
const userInfo = storageSession.getItem<DataInfo<number>>(sessionKey); const userInfo = storageSession().getItem<DataInfo<number>>(sessionKey);
NProgress.start(); NProgress.start();
const externalLink = isUrl(to?.name as string); const externalLink = isUrl(to?.name as string);
if (!externalLink) { if (!externalLink) {
@@ -142,14 +141,10 @@ router.beforeEach((to: toRouteType, _from, next) => {
initRouter().then((router: Router) => { initRouter().then((router: Router) => {
if (!useMultiTagsStoreHook().getMultiTagsCache) { if (!useMultiTagsStoreHook().getMultiTagsCache) {
const { path } = to; const { path } = to;
const index = findIndex(remainingRouter, v => { const route = findRouteByPath(
return v.path == path; path,
}); router.options.routes[0].children
const routes: any = );
index === -1
? router.options.routes[0].children
: router.options.routes;
const route = findRouteByPath(path, routes);
// query、params模式路由传参数的标签页不在此处处理 // query、params模式路由传参数的标签页不在此处处理
if (route && route.meta?.title) { if (route && route.meta?.title) {
useMultiTagsStoreHook().handleTags("push", { useMultiTagsStoreHook().handleTags("push", {

View File

@@ -1,11 +1,10 @@
import InformationLine from "@iconify-icons/ri/information-line";
export default { export default {
path: "/error", path: "/error",
redirect: "/error/403", redirect: "/error/403",
meta: { meta: {
icon: InformationLine, icon: "informationLine",
title: "异常页面", title: "异常页面",
// showLink: false,
rank: 9 rank: 9
}, },
children: [ children: [

View File

@@ -1,5 +1,4 @@
const Layout = () => import("@/layout/index.vue"); const Layout = () => import("@/layout/index.vue");
import HomeFilled from "@iconify-icons/ep/home-filled";
export default { export default {
path: "/", path: "/",
@@ -7,7 +6,7 @@ export default {
component: Layout, component: Layout,
redirect: "/welcome", redirect: "/welcome",
meta: { meta: {
icon: HomeFilled, icon: "homeFilled",
title: "首页", title: "首页",
rank: 0 rank: 0
}, },

View File

@@ -1,5 +1,4 @@
const Layout = () => import("@/layout/index.vue"); const Layout = () => import("@/layout/index.vue");
import HomeFilled from "@iconify-icons/ep/home-filled";
export default [ export default [
{ {
@@ -16,7 +15,7 @@ export default [
path: "/redirect", path: "/redirect",
component: Layout, component: Layout,
meta: { meta: {
icon: HomeFilled, icon: "homeFilled",
title: "首页", title: "首页",
showLink: false, showLink: false,
rank: 104 rank: 104

View File

@@ -13,12 +13,14 @@ import { useTimeoutFn } from "@vueuse/core";
import { RouteConfigs } from "@/layout/types"; import { RouteConfigs } from "@/layout/types";
import { import {
isString, isString,
cloneDeep,
isAllEmpty,
intersection,
storageSession, storageSession,
isIncludeAllChildren isIncludeAllChildren
} from "@pureadmin/utils"; } from "@pureadmin/utils";
import { getConfig } from "@/config"; import { getConfig } from "@/config";
import { buildHierarchyTree } from "@/utils/tree"; import { buildHierarchyTree } from "@/utils/tree";
import { cloneDeep, intersection } from "lodash-unified";
import { sessionKey, type DataInfo } from "@/utils/auth"; import { sessionKey, type DataInfo } from "@/utils/auth";
import { usePermissionStoreHook } from "@/store/modules/permission"; import { usePermissionStoreHook } from "@/store/modules/permission";
const IFrame = () => import("@/layout/frameView.vue"); const IFrame = () => import("@/layout/frameView.vue");
@@ -28,19 +30,25 @@ const modulesRoutes = import.meta.glob("/src/views/**/*.{vue,tsx}");
// 动态路由 // 动态路由
import { getAsyncRoutes } from "@/api/routes"; import { getAsyncRoutes } from "@/api/routes";
function handRank(routeInfo: any) {
const { name, path, parentId, meta } = routeInfo;
return isAllEmpty(parentId)
? isAllEmpty(meta?.rank) ||
(meta?.rank === 0 && name !== "Home" && path !== "/")
? true
: false
: false;
}
/** 按照路由中meta下的rank等级升序来排序路由 */ /** 按照路由中meta下的rank等级升序来排序路由 */
function ascending(arr: any[]) { function ascending(arr: any[]) {
arr.forEach(v => { arr.forEach((v, index) => {
if (v?.meta?.rank === null) v.meta.rank = undefined; // 当rank不存在时根据顺序自动创建首页路由永远在第一位
if (v?.meta?.rank === 0) { if (handRank(v)) v.meta.rank = index + 2;
if (v.name !== "Home" && v.path !== "/") {
console.warn("rank only the home page can be 0");
}
}
}); });
return arr.sort( return arr.sort(
(a: { meta: { rank: number } }, b: { meta: { rank: number } }) => { (a: { meta: { rank: number } }, b: { meta: { rank: number } }) => {
return a?.meta?.rank - b?.meta?.rank; return a?.meta.rank - b?.meta.rank;
} }
); );
} }
@@ -77,7 +85,7 @@ function isOneOfArray(a: Array<string>, b: Array<string>) {
/** 从sessionStorage里取出当前登陆用户的角色roles过滤无权限的菜单 */ /** 从sessionStorage里取出当前登陆用户的角色roles过滤无权限的菜单 */
function filterNoPermissionTree(data: RouteComponent[]) { function filterNoPermissionTree(data: RouteComponent[]) {
const currentRoles = const currentRoles =
storageSession.getItem<DataInfo<number>>(sessionKey)?.roles ?? []; storageSession().getItem<DataInfo<number>>(sessionKey)?.roles ?? [];
const newTree = cloneDeep(data).filter((v: any) => const newTree = cloneDeep(data).filter((v: any) =>
isOneOfArray(v.meta?.roles, currentRoles) isOneOfArray(v.meta?.roles, currentRoles)
); );
@@ -189,7 +197,7 @@ function initRouter() {
if (getConfig()?.CachingAsyncRoutes) { if (getConfig()?.CachingAsyncRoutes) {
// 开启动态路由缓存本地sessionStorage // 开启动态路由缓存本地sessionStorage
const key = "async-routes"; const key = "async-routes";
const asyncRouteList = storageSession.getItem(key) as any; const asyncRouteList = storageSession().getItem(key) as any;
if (asyncRouteList && asyncRouteList?.length > 0) { if (asyncRouteList && asyncRouteList?.length > 0) {
return new Promise(resolve => { return new Promise(resolve => {
handleAsyncRoutes(asyncRouteList); handleAsyncRoutes(asyncRouteList);
@@ -198,8 +206,8 @@ function initRouter() {
} else { } else {
return new Promise(resolve => { return new Promise(resolve => {
getAsyncRoutes().then(({ data }) => { getAsyncRoutes().then(({ data }) => {
handleAsyncRoutes(data); handleAsyncRoutes(cloneDeep(data));
storageSession.setItem(key, data); storageSession().setItem(key, data);
resolve(router); resolve(router);
}); });
}); });
@@ -207,7 +215,7 @@ function initRouter() {
} else { } else {
return new Promise(resolve => { return new Promise(resolve => {
getAsyncRoutes().then(({ data }) => { getAsyncRoutes().then(({ data }) => {
handleAsyncRoutes(data); handleAsyncRoutes(cloneDeep(data));
resolve(router); resolve(router);
}); });
}); });
@@ -252,7 +260,7 @@ function formatTwoStageRoutes(routesList: RouteRecordRaw[]) {
children: [] children: []
}); });
} else { } else {
newRoutesList[0].children.push({ ...v }); newRoutesList[0]?.children.push({ ...v });
} }
}); });
return newRoutesList; return newRoutesList;
@@ -348,6 +356,7 @@ function hasAuth(value: string | Array<string>): boolean {
if (!value) return false; if (!value) return false;
/** 从当前路由的`meta`字段里获取按钮级别的所有自定义`code`值 */ /** 从当前路由的`meta`字段里获取按钮级别的所有自定义`code`值 */
const metaAuths = getAuths(); const metaAuths = getAuths();
if (!metaAuths) return false;
const isAuths = isString(value) const isAuths = isString(value)
? metaAuths.includes(value) ? metaAuths.includes(value)
: isIncludeAllChildren(value, metaAuths); : isIncludeAllChildren(value, metaAuths);

View File

@@ -9,14 +9,14 @@ export const useAppStore = defineStore({
state: (): appType => ({ state: (): appType => ({
sidebar: { sidebar: {
opened: opened:
storageLocal.getItem<StorageConfigs>("responsive-layout") storageLocal().getItem<StorageConfigs>("responsive-layout")
?.sidebarStatus ?? getConfig().SidebarStatus, ?.sidebarStatus ?? getConfig().SidebarStatus,
withoutAnimation: false, withoutAnimation: false,
isClickCollapse: false isClickCollapse: false
}, },
// 这里的layout用于监听容器拖拉后恢复对应的导航模式 // 这里的layout用于监听容器拖拉后恢复对应的导航模式
layout: layout:
storageLocal.getItem<StorageConfigs>("responsive-layout")?.layout ?? storageLocal().getItem<StorageConfigs>("responsive-layout")?.layout ??
getConfig().Layout, getConfig().Layout,
device: deviceDetection() ? "mobile" : "desktop" device: deviceDetection() ? "mobile" : "desktop"
}), }),
@@ -30,7 +30,8 @@ export const useAppStore = defineStore({
}, },
actions: { actions: {
TOGGLE_SIDEBAR(opened?: boolean, resize?: string) { TOGGLE_SIDEBAR(opened?: boolean, resize?: string) {
const layout = storageLocal.getItem<StorageConfigs>("responsive-layout"); const layout =
storageLocal().getItem<StorageConfigs>("responsive-layout");
if (opened && resize) { if (opened && resize) {
this.sidebar.withoutAnimation = true; this.sidebar.withoutAnimation = true;
this.sidebar.opened = true; this.sidebar.opened = true;
@@ -45,7 +46,7 @@ export const useAppStore = defineStore({
this.sidebar.isClickCollapse = !this.sidebar.opened; this.sidebar.isClickCollapse = !this.sidebar.opened;
layout.sidebarStatus = this.sidebar.opened; layout.sidebarStatus = this.sidebar.opened;
} }
storageLocal.setItem("responsive-layout", layout); storageLocal().setItem("responsive-layout", layout);
}, },
async toggleSideBar(opened?: boolean, resize?: string) { async toggleSideBar(opened?: boolean, resize?: string) {
await this.TOGGLE_SIDEBAR(opened, resize); await this.TOGGLE_SIDEBAR(opened, resize);

View File

@@ -7,10 +7,10 @@ export const useEpThemeStore = defineStore({
id: "pure-epTheme", id: "pure-epTheme",
state: () => ({ state: () => ({
epThemeColor: epThemeColor:
storageLocal.getItem<StorageConfigs>("responsive-layout")?.epThemeColor ?? storageLocal().getItem<StorageConfigs>("responsive-layout")
getConfig().EpThemeColor, ?.epThemeColor ?? getConfig().EpThemeColor,
epTheme: epTheme:
storageLocal.getItem<StorageConfigs>("responsive-layout")?.theme ?? storageLocal().getItem<StorageConfigs>("responsive-layout")?.theme ??
getConfig().Theme getConfig().Theme
}), }),
getters: { getters: {
@@ -30,12 +30,13 @@ export const useEpThemeStore = defineStore({
}, },
actions: { actions: {
setEpThemeColor(newColor: string): void { setEpThemeColor(newColor: string): void {
const layout = storageLocal.getItem<StorageConfigs>("responsive-layout"); const layout =
storageLocal().getItem<StorageConfigs>("responsive-layout");
this.epTheme = layout?.theme; this.epTheme = layout?.theme;
this.epThemeColor = newColor; this.epThemeColor = newColor;
if (!layout) return; if (!layout) return;
layout.epThemeColor = newColor; layout.epThemeColor = newColor;
storageLocal.setItem("responsive-layout", layout); storageLocal().setItem("responsive-layout", layout);
} }
} }
}); });

View File

@@ -9,12 +9,13 @@ export const useMultiTagsStore = defineStore({
id: "pure-multiTags", id: "pure-multiTags",
state: () => ({ state: () => ({
// 存储标签页信息(路由信息) // 存储标签页信息(路由信息)
multiTags: storageLocal.getItem<StorageConfigs>("responsive-configure") multiTags: storageLocal().getItem<StorageConfigs>("responsive-configure")
?.multiTagsCache ?.multiTagsCache
? storageLocal.getItem<StorageConfigs>("responsive-tags") ? storageLocal().getItem<StorageConfigs>("responsive-tags")
: [...routerArrays], : [...routerArrays],
multiTagsCache: storageLocal.getItem<StorageConfigs>("responsive-configure") multiTagsCache: storageLocal().getItem<StorageConfigs>(
?.multiTagsCache "responsive-configure"
)?.multiTagsCache
}), }),
getters: { getters: {
getMultiTagsCache() { getMultiTagsCache() {
@@ -25,14 +26,14 @@ export const useMultiTagsStore = defineStore({
multiTagsCacheChange(multiTagsCache: boolean) { multiTagsCacheChange(multiTagsCache: boolean) {
this.multiTagsCache = multiTagsCache; this.multiTagsCache = multiTagsCache;
if (multiTagsCache) { if (multiTagsCache) {
storageLocal.setItem("responsive-tags", this.multiTags); storageLocal().setItem("responsive-tags", this.multiTags);
} else { } else {
storageLocal.removeItem("responsive-tags"); storageLocal().removeItem("responsive-tags");
} }
}, },
tagsCache(multiTags) { tagsCache(multiTags) {
this.getMultiTagsCache && this.getMultiTagsCache &&
storageLocal.setItem("responsive-tags", multiTags); storageLocal().setItem("responsive-tags", multiTags);
}, },
handleTags<T>( handleTags<T>(
mode: string, mode: string,

View File

@@ -14,9 +14,9 @@ export const useUserStore = defineStore({
state: (): userType => ({ state: (): userType => ({
// 用户名 // 用户名
username: username:
storageSession.getItem<DataInfo<number>>(sessionKey)?.username ?? "", storageSession().getItem<DataInfo<number>>(sessionKey)?.username ?? "",
// 页面级别权限 // 页面级别权限
roles: storageSession.getItem<DataInfo<number>>(sessionKey)?.roles ?? [] roles: storageSession().getItem<DataInfo<number>>(sessionKey)?.roles ?? []
}), }),
actions: { actions: {
/** 存储用户名 */ /** 存储用户名 */

View File

@@ -1,4 +1,4 @@
@import "element-plus/theme-chalk/src/dark/css-vars.scss"; @use "element-plus/theme-chalk/src/dark/css-vars.scss" as *;
/* 暗黑模式适配 */ /* 暗黑模式适配 */
html.dark { html.dark {
@@ -39,97 +39,6 @@ html.dark {
} }
} }
/* vxe-table */
.vxe-table--header-wrapper,
.vxe-table--body-wrapper {
color: var(--el-text-color-primary);
background: var(--el-bg-color) !important;
}
.vxe-table--render-default.border--full .vxe-header--column,
.vxe-table--render-default.border--full .vxe-body--column,
.vxe-table--render-default.border--full .vxe-footer--column {
background-image: linear-gradient(
var(--el-border-color-lighter),
var(--el-border-color-lighter)
),
linear-gradient(
var(--el-border-color-lighter),
var(--el-border-color-lighter)
);
}
/* 表头 */
.vxe-table--header-wrapper {
background: #262727 !important;
}
.vxe-table--render-wrapper,
.vxe-table--main-wrapper {
border: none;
}
.vxe-pager.is--perfect,
.vxe-table--render-default .vxe-table--border-line {
border: 1px solid var(--el-border-color-lighter);
}
.vxe-table--header-border-line {
border-bottom: 1px solid var(--el-border-color-lighter) !important;
}
.vxe-body--row.row--hover,
.vxe-pager {
background-color: #262727;
}
.vxe-input--inner,
.vxe-pager .vxe-pager--jump-prev,
.vxe-pager .vxe-pager--prev-btn,
.vxe-pager .vxe-pager--next-btn,
.vxe-pager .vxe-pager--jump-next,
.vxe-pager .vxe-pager--num-btn,
.vxe-pager .vxe-pager--jump .vxe-pager--goto {
background-color: transparent;
color: var(--el-text-color-primary);
// outline: none !important;
}
.vxe-select-option--wrapper {
background: var(--el-bg-color) !important;
}
.vxe-select-option:not(.is--disabled).is--hover {
background: var(--el-color-primary-light-6) !important;
}
.vxe-modal--wrapper.type--modal .vxe-modal--box,
.vxe-modal--wrapper.type--alert .vxe-modal--box,
.vxe-modal--wrapper.type--confirm .vxe-modal--box,
.vxe-form {
background: var(--el-bg-color) !important;
}
.vxe-modal--box,
.vxe-modal--header {
border: none;
background: var(--el-bg-color) !important;
}
.vxe-modal--title,
.vxe-button--content,
.vxe-modal--header-title {
color: var(--el-text-color-primary);
}
.vxe-button.type--button:hover {
background: var(--el-color-primary) !important;
}
.vxe-button {
background-color: transparent;
}
/* 项目配置面板 */ /* 项目配置面板 */
.right-panel-items { .right-panel-items {
.el-divider__text { .el-divider__text {

View File

@@ -19,9 +19,3 @@
.html-weakness { .html-weakness {
filter: invert(80%); filter: invert(80%);
} }
/* 重置 vxe-table 样式 */
.vxe-button.type--button.theme--primary:hover,
.vxe-pager .vxe-pager--num-btn:not(.is--disabled).is--active {
color: #fff !important;
}

View File

@@ -23,7 +23,7 @@ export function getToken(): DataInfo<number> {
// 此处与`TokenKey`相同,此写法解决初始化时`Cookies`中不存在`TokenKey`报错 // 此处与`TokenKey`相同,此写法解决初始化时`Cookies`中不存在`TokenKey`报错
return Cookies.get(TokenKey) return Cookies.get(TokenKey)
? JSON.parse(Cookies.get(TokenKey)) ? JSON.parse(Cookies.get(TokenKey))
: storageSession.getItem(sessionKey); : storageSession().getItem(sessionKey);
} }
/** /**
@@ -47,7 +47,7 @@ export function setToken(data: DataInfo<Date>) {
function setSessionKey(username: string, roles: Array<string>) { function setSessionKey(username: string, roles: Array<string>) {
useUserStoreHook().SET_USERNAME(username); useUserStoreHook().SET_USERNAME(username);
useUserStoreHook().SET_ROLES(roles); useUserStoreHook().SET_ROLES(roles);
storageSession.setItem(sessionKey, { storageSession().setItem(sessionKey, {
refreshToken, refreshToken,
expires, expires,
username, username,
@@ -60,9 +60,9 @@ export function setToken(data: DataInfo<Date>) {
setSessionKey(username, roles); setSessionKey(username, roles);
} else { } else {
const username = const username =
storageSession.getItem<DataInfo<number>>(sessionKey)?.username ?? ""; storageSession().getItem<DataInfo<number>>(sessionKey)?.username ?? "";
const roles = const roles =
storageSession.getItem<DataInfo<number>>(sessionKey)?.roles ?? []; storageSession().getItem<DataInfo<number>>(sessionKey)?.roles ?? [];
setSessionKey(username, roles); setSessionKey(username, roles);
} }
} }