Compare commits

...

5 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
23 changed files with 646 additions and 656 deletions

View File

@@ -7,7 +7,7 @@ import { Plugin as importToCDN } from "vite-plugin-cdn-import";
* 注意上面提到的仅限外网使用也不是完全肯定的如果你们公司内网部署的有相关js、css文件也可以将下面配置对应改一下整一套内网版cdn
*/
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}",
modules: [
{
@@ -51,12 +51,6 @@ export const cdn = importToCDN({
name: "echarts",
var: "echarts",
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,13 +10,9 @@ const include = [
"dayjs",
"axios",
"pinia",
"lodash",
"echarts",
"js-cookie",
"lodash-es",
"@vueuse/core",
"lodash-unified",
"@ctrl/tinycolor",
"@pureadmin/utils",
"responsive-storage",
"element-resize-detector"

View File

@@ -1,6 +1,6 @@
{
"name": "pure-admin-thin",
"version": "3.9.4",
"version": "3.9.5",
"private": true,
"scripts": {
"dev": "NODE_OPTIONS=--max-old-space-size=4096 vite",
@@ -29,10 +29,9 @@
"not op_mini all"
],
"dependencies": {
"@ctrl/tinycolor": "^3.4.1",
"@pureadmin/descriptions": "^1.1.0",
"@pureadmin/table": "^1.8.3",
"@pureadmin/utils": "^1.7.4",
"@pureadmin/table": "^1.9.0",
"@pureadmin/utils": "^1.8.5",
"@vueuse/core": "^9.6.0",
"@vueuse/motion": "2.0.0-beta.12",
"animate.css": "^4.1.1",
@@ -42,9 +41,6 @@
"element-plus": "^2.2.26",
"element-resize-detector": "^1.2.4",
"js-cookie": "^3.0.1",
"lodash": "^4.17.21",
"lodash-es": "^4.17.21",
"lodash-unified": "^1.0.2",
"mitt": "^3.0.0",
"mockjs": "^1.1.0",
"nprogress": "^0.2.0",
@@ -65,8 +61,6 @@
"@pureadmin/theme": "^2.4.0",
"@types/element-resize-detector": "1.1.3",
"@types/js-cookie": "^3.0.1",
"@types/lodash": "^4.14.180",
"@types/lodash-es": "^4.17.6",
"@types/mockjs": "^1.0.7",
"@types/node": "^18.11.9",
"@types/nprogress": "0.2.0",

1119
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

@@ -132,8 +132,8 @@ const multiTagsCacheChange = () => {
/** 清空缓存并返回登录页 */
function onReset() {
removeToken();
storageLocal.clear();
storageSession.clear();
storageLocal().clear();
storageSession().clear();
const { Grey, Weak, MultiTagsCache, EpThemeColor, Layout } = getConfig();
useAppStoreHook().setLayout(Layout);
setEpThemeColor(EpThemeColor);

View File

@@ -99,6 +99,7 @@ watch(
:deep(.el-loading-mask) {
opacity: 0.45;
}
.logout {
max-width: 120px;

View File

@@ -131,6 +131,7 @@ watch(
:deep(.el-loading-mask) {
opacity: 0.45;
}
.logout {
max-width: 120px;

View File

@@ -12,7 +12,8 @@ import { usePermissionStoreHook } from "@/store/modules/permission";
const route = useRoute();
const showLogo = ref(
storageLocal.getItem<StorageConfigs>("responsive-configure")?.showLogo ?? true
storageLocal().getItem<StorageConfigs>("responsive-configure")?.showLogo ??
true
);
const { routers, device, pureApp, isCollapse, menuSelect, toggleSideBar } =

View File

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

View File

@@ -1,9 +1,7 @@
import { ref } from "vue";
import { getConfig } from "@/config";
import { find } from "lodash-unified";
import { useLayout } from "./useLayout";
import { themeColorsType } from "../types";
import { TinyColor } from "@ctrl/tinycolor";
import { useGlobal } from "@pureadmin/utils";
import { useEpThemeStoreHook } from "@/store/modules/epTheme";
import {
@@ -56,35 +54,27 @@ export function useDataThemeChange() {
if (theme === "default" || theme === "light") {
setEpThemeColor(getConfig().EpThemeColor);
} else {
const colors = find(themeColors.value, { themeColor: theme });
const colors = themeColors.value.find(v => v.themeColor === theme);
setEpThemeColor(colors.color);
}
}
/**
* @description 自动计算hover和active颜色
* @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}
*/
const shadeBgColor = (color: string): string => {
return new TinyColor(color).shade(10).toString();
};
function setPropertyPrimary(mode: string, i: number, color: string) {
document.documentElement.style.setProperty(
`--el-color-primary-${mode}-${i}`,
dataTheme.value ? darken(color, i / 10) : lighten(color, i / 10)
);
}
/** 设置 `element-plus` 主题色 */
const setEpThemeColor = (color: string) => {
useEpThemeStoreHook().setEpThemeColor(color);
body.style.setProperty("--el-color-primary-active", shadeBgColor(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++) {
document.documentElement.style.setProperty(
`--el-color-primary-dark-${i}`,
darken(color, i / 10)
);
setPropertyPrimary("dark", i, color);
}
for (let i = 1; i <= 9; i++) {
setPropertyPrimary("light", i, color);
}
};

View File

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

View File

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

View File

@@ -4,6 +4,7 @@ export default {
meta: {
icon: "informationLine",
title: "异常页面",
// showLink: false,
rank: 9
},
children: [

View File

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

View File

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

View File

@@ -7,10 +7,10 @@ export const useEpThemeStore = defineStore({
id: "pure-epTheme",
state: () => ({
epThemeColor:
storageLocal.getItem<StorageConfigs>("responsive-layout")?.epThemeColor ??
getConfig().EpThemeColor,
storageLocal().getItem<StorageConfigs>("responsive-layout")
?.epThemeColor ?? getConfig().EpThemeColor,
epTheme:
storageLocal.getItem<StorageConfigs>("responsive-layout")?.theme ??
storageLocal().getItem<StorageConfigs>("responsive-layout")?.theme ??
getConfig().Theme
}),
getters: {
@@ -30,12 +30,13 @@ export const useEpThemeStore = defineStore({
},
actions: {
setEpThemeColor(newColor: string): void {
const layout = storageLocal.getItem<StorageConfigs>("responsive-layout");
const layout =
storageLocal().getItem<StorageConfigs>("responsive-layout");
this.epTheme = layout?.theme;
this.epThemeColor = newColor;
if (!layout) return;
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",
state: () => ({
// 存储标签页信息(路由信息)
multiTags: storageLocal.getItem<StorageConfigs>("responsive-configure")
multiTags: storageLocal().getItem<StorageConfigs>("responsive-configure")
?.multiTagsCache
? storageLocal.getItem<StorageConfigs>("responsive-tags")
? storageLocal().getItem<StorageConfigs>("responsive-tags")
: [...routerArrays],
multiTagsCache: storageLocal.getItem<StorageConfigs>("responsive-configure")
?.multiTagsCache
multiTagsCache: storageLocal().getItem<StorageConfigs>(
"responsive-configure"
)?.multiTagsCache
}),
getters: {
getMultiTagsCache() {
@@ -25,14 +26,14 @@ export const useMultiTagsStore = defineStore({
multiTagsCacheChange(multiTagsCache: boolean) {
this.multiTagsCache = multiTagsCache;
if (multiTagsCache) {
storageLocal.setItem("responsive-tags", this.multiTags);
storageLocal().setItem("responsive-tags", this.multiTags);
} else {
storageLocal.removeItem("responsive-tags");
storageLocal().removeItem("responsive-tags");
}
},
tagsCache(multiTags) {
this.getMultiTagsCache &&
storageLocal.setItem("responsive-tags", multiTags);
storageLocal().setItem("responsive-tags", multiTags);
},
handleTags<T>(
mode: string,

View File

@@ -14,9 +14,9 @@ export const useUserStore = defineStore({
state: (): userType => ({
// 用户名
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: {
/** 存储用户名 */

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 {

View File

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