Merge branch 'main' into pages

This commit is contained in:
xiaoxian521 2025-10-17 10:19:32 +08:00
commit 401d99b3be
14 changed files with 1353 additions and 1254 deletions

2
.nvmrc
View File

@ -1 +1 @@
v22.17.1
v22.20.0

View File

@ -1,4 +1,5 @@
{
"tailwindCSS.experimental.configFile": "src/style/tailwind.css",
"editor.formatOnType": true,
"editor.formatOnSave": true,
"[vue]": {

View File

@ -1,3 +1,23 @@
# 6.2.0 (2025-10-16)
### 🎫 Features
- Added full-screen `403`, `404`, and `500` error pages. These full-screen error pages are clear and secure, improving the user experience.
### 🐞 Bug Fixes
- Fixed an issue where the built-in homepage did not have a `name` configured, causing cache invalidation after setting the page cache.
- Fixed an issue where, in an embedded same-origin `iframe` page, when the `beforeunload` event was registered, right-clicking a tab and reloading it would cause the browser to prompt two confirmation blocks.
- Fixed an issue where pages with `keepAlive: true` set to cache invalidation when the initial load was slow.
- Fixed an issue where multiple tabs could be activated simultaneously when using the same parameters in different routes.
- Fixed an issue where the right-click menu on a tab displayed incorrectly when passing `params` parameters.
### 🍏 Perf
- Optimized the `nprogress` progress bar. It no longer displays when reloading a page or requesting an interface, improving the user experience.
- Optimized the timing of capturing all unmatched routes and redirecting to a full-screen `404` page.
- Explicitly configured the `Tailwind CSS` entry file path to improve the contextual recognition and prompting performance of the `Tailwind CSS IntelliSense` plugin
# 6.1.0 (2025-07-31)
### ✔️ Refactor

View File

@ -1,3 +1,23 @@
# 6.2.0 (2025-10-16)
### 🎫 Features
- Added full-screen `403`, `404`, and `500` error pages. These full-screen error pages are clear and secure, improving the user experience.
### 🐞 Bug Fixes
- Fixed an issue where the built-in homepage did not have a `name` configured, causing cache invalidation after setting the page cache.
- Fixed an issue where, in an embedded same-origin `iframe` page, when the `beforeunload` event was registered, right-clicking a tab and reloading it would cause the browser to prompt two confirmation blocks.
- Fixed an issue where pages with `keepAlive: true` set to cache invalidation when the initial load was slow.
- Fixed an issue where multiple tabs could be activated simultaneously when using the same parameters in different routes.
- Fixed an issue where the right-click menu on a tab displayed incorrectly when passing `params` parameters.
### 🍏 Perf
- Optimized the `nprogress` progress bar. It no longer displays when reloading a page or requesting an interface, improving the user experience.
- Optimized the timing of capturing all unmatched routes and redirecting to a full-screen `404` page.
- Explicitly configured the `Tailwind CSS` entry file path to improve the contextual recognition and prompting performance of the `Tailwind CSS IntelliSense` plugin
# 6.1.0 (2025-07-31)
### ✔️ Refactor

View File

@ -1,3 +1,23 @@
# 6.2.0 (2025-10-16)
### 🎫 Feat
- 添加全屏`403``404``500`页面,全屏错误页面清晰且安全,提升用户体验
### 🐞 Bug fixes
- 修复内置的首页未设置`name`导致设置页面缓存后缓存无效的问题
- 修复在内嵌同源`iframe`页面中,当其注册了`beforeunload`事件时,右键标签页点击重新加载导致浏览器弹出两次确认拦截的问题
- 修复设置了`keepAlive: true`的页面在初次加载缓慢的情况下出现的页面缓存失效的问题
- 修复不同路由使用相同参数时,多个标签页会同时被激活的问题
- 修复`params`传参模式下标签页右键菜单显示不正确的问题
### 🍏 Perf
- 优化`nprogress`进度条,页面重进或接口请求时不再显示进度条,提升用户体验
- 优化当捕获所有未匹配路由并跳转全屏`404`页面的时机
- 显式配置`Tailwind CSS`入口文件路径,优化`Tailwind CSS IntelliSense`插件的上下文识别与提示性能
# 6.1.0 (2025-07-31)
### ✔️ Refactor

View File

@ -1,6 +1,6 @@
{
"name": "vue-pure-admin",
"version": "6.1.0",
"version": "6.2.0",
"private": true,
"type": "module",
"scripts": {
@ -56,7 +56,7 @@
"@pureadmin/table": "^3.3.0",
"@pureadmin/utils": "^2.6.2",
"@vue-flow/background": "^1.3.2",
"@vue-flow/core": "^1.46.4",
"@vue-flow/core": "^1.47.0",
"@vueuse/core": "^13.9.0",
"@vueuse/motion": "^3.0.3",
"@wangeditor/editor": "^5.1.23",
@ -70,9 +70,9 @@
"cropperjs": "^1.6.2",
"dayjs": "^1.11.18",
"deep-chat": "^2.2.2",
"echarts": "^5.6.0",
"echarts": "^6.0.0",
"el-table-infinite-scroll": "^3.0.7",
"element-plus": "^2.11.2",
"element-plus": "^2.11.4",
"highlight.js": "^11.11.1",
"intro.js": "^7.2.0",
"js-cookie": "^3.0.5",
@ -90,13 +90,13 @@
"qs": "^6.14.0",
"responsive-storage": "^2.2.0",
"sortablejs": "^1.15.6",
"swiper": "^11.2.10",
"swiper": "^12.0.2",
"typeit": "^8.8.7",
"v-contextmenu": "^3.2.0",
"v3-infinite-loading": "^1.3.2",
"vditor": "^3.11.2",
"version-rocket": "^1.7.4",
"vue": "^3.5.21",
"vue": "^3.5.22",
"vue-i18n": "^11.1.12",
"vue-json-pretty": "^2.5.0",
"vue-pdf-embed": "^2.1.3",
@ -109,25 +109,25 @@
"vue3-puzzle-vcode": "^1.1.7",
"vuedraggable": "^4.1.0",
"vxe-table": "4.6.25",
"wavesurfer.js": "^7.10.1",
"wavesurfer.js": "^7.11.0",
"xgplayer": "^3.0.23",
"xlsx": "^0.18.5"
},
"devDependencies": {
"@commitlint/cli": "^19.8.1",
"@commitlint/config-conventional": "^19.8.1",
"@commitlint/types": "^19.8.1",
"@eslint/js": "^9.35.0",
"@faker-js/faker": "^9.9.0",
"@iconify/json": "^2.2.384",
"@commitlint/cli": "^20.1.0",
"@commitlint/config-conventional": "^20.0.0",
"@commitlint/types": "^20.0.0",
"@eslint/js": "^9.37.0",
"@faker-js/faker": "^10.0.0",
"@iconify/json": "^2.2.393",
"@iconify/vue": "4.2.0",
"@intlify/unplugin-vue-i18n": "^6.0.8",
"@tailwindcss/vite": "^4.1.13",
"@intlify/unplugin-vue-i18n": "^11.0.1",
"@tailwindcss/vite": "^4.1.14",
"@types/codemirror": "^5.60.16",
"@types/dagre": "^0.7.53",
"@types/intro.js": "^5.1.5",
"@types/js-cookie": "^3.0.6",
"@types/node": "^20.19.14",
"@types/node": "^20.19.19",
"@types/nprogress": "^0.2.3",
"@types/path-browserify": "^1.0.3",
"@types/qrcode": "^1.5.5",
@ -136,35 +136,35 @@
"@vitejs/plugin-vue": "^6.0.1",
"@vitejs/plugin-vue-jsx": "^5.1.1",
"boxen": "^8.0.1",
"code-inspector-plugin": "^1.2.8",
"code-inspector-plugin": "^1.2.10",
"cssnano": "^7.1.1",
"dagre": "^0.8.5",
"eslint": "^9.35.0",
"eslint": "^9.37.0",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-prettier": "^5.5.4",
"eslint-plugin-vue": "^10.4.0",
"eslint-plugin-vue": "^10.5.0",
"gradient-string": "^3.0.0",
"husky": "^9.1.7",
"lint-staged": "^16.1.6",
"lint-staged": "^16.2.3",
"postcss": "^8.5.6",
"postcss-html": "^1.8.0",
"postcss-load-config": "^6.0.1",
"postcss-scss": "^4.0.9",
"prettier": "^3.6.2",
"rimraf": "^6.0.1",
"rollup-plugin-visualizer": "^6.0.3",
"sass": "^1.92.1",
"stylelint": "^16.24.0",
"rollup-plugin-visualizer": "^6.0.4",
"sass": "^1.93.2",
"stylelint": "^16.25.0",
"stylelint-config-recess-order": "^7.3.0",
"stylelint-config-recommended-vue": "^1.6.1",
"stylelint-config-standard-scss": "^14.0.0",
"stylelint-prettier": "^5.0.3",
"svgo": "^4.0.0",
"tailwindcss": "^4.1.13",
"typescript": "^5.9.2",
"typescript-eslint": "^8.43.0",
"unplugin-icons": "^22.3.0",
"vite": "^7.1.5",
"tailwindcss": "^4.1.14",
"typescript": "^5.9.3",
"typescript-eslint": "^8.46.0",
"unplugin-icons": "^22.4.2",
"vite": "^7.1.9",
"vite-plugin-cdn-import": "^1.0.1",
"vite-plugin-compression": "^0.5.1",
"vite-plugin-fake-server": "^2.2.0",
@ -172,10 +172,10 @@
"vite-plugin-router-warn": "^1.0.0",
"vite-svg-loader": "^5.1.0",
"vue-eslint-parser": "^10.2.0",
"vue-tsc": "^3.0.7"
"vue-tsc": "^3.1.1"
},
"engines": {
"node": "^20.19.0 || >=22.12.0",
"node": "^20.19.0 || >=22.13.0",
"pnpm": ">=9"
},
"pnpm": {

2353
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -356,7 +356,7 @@ function onClickDrop(key, item, selectRoute?: RouteConfigs) {
break;
}
setTimeout(() => {
showMenuModel(route.fullPath, route.query);
showMenuModel(route.fullPath, route.query, route.params);
});
}
@ -391,15 +391,18 @@ function disabledMenus(value: boolean, fixedTag = false) {
function showMenuModel(
currentPath: string,
query: object = {},
params: object = {},
refresh = false
) {
const allRoute = multiTags.value;
const routeLength = multiTags.value.length;
let currentIndex = -1;
if (isAllEmpty(query)) {
currentIndex = allRoute.findIndex(v => v.path === currentPath);
} else {
if (!isAllEmpty(params)) {
currentIndex = allRoute.findIndex(v => isEqual(v.params, params));
} else if (!isAllEmpty(query)) {
currentIndex = allRoute.findIndex(v => isEqual(v.query, query));
} else {
currentIndex = allRoute.findIndex(v => v.path === currentPath);
}
function fixedTagDisabled() {
if (allRoute[currentIndex]?.meta?.fixedTag) {
@ -465,14 +468,14 @@ function openMenu(tag, e) {
} else if (route.path !== tag.path && route.name !== tag.name) {
//
tagsViews[0].show = false;
showMenuModel(tag.path, tag.query);
showMenuModel(tag.path, tag.query, tag.params);
} else if (multiTags.value.length === 2 && route.path !== tag.path) {
showMenus(true);
//
tagsViews[4].show = false;
} else if (route.path === tag.path) {
//
showMenuModel(tag.path, tag.query, true);
showMenuModel(tag.path, tag.query, tag.params);
} else {
showMenuModel(tag.path, tag.query, tag.params, true);
}
currentSelect.value = tag;

View File

@ -19,13 +19,22 @@ const loading = ref(true);
const currentRoute = useRoute();
const frameSrc = ref<string>("");
const frameRef = ref<HTMLElement | null>(null);
const fallbackTimer = ref<number | null>(null);
if (unref(currentRoute.meta)?.frameSrc) {
frameSrc.value = unref(currentRoute.meta)?.frameSrc as string;
}
unref(currentRoute.meta)?.frameLoading === false && hideLoading();
function clearFallbackTimer() {
if (fallbackTimer.value !== null) {
clearTimeout(fallbackTimer.value);
fallbackTimer.value = null;
}
}
function hideLoading() {
loading.value = false;
clearFallbackTimer();
}
function init() {
@ -34,32 +43,42 @@ function init() {
if (!iframe) return;
const _frame = iframe as any;
if (_frame.attachEvent) {
_frame.attachEvent("onload", () => {
hideLoading();
});
_frame.attachEvent("onload", hideLoading);
} else {
iframe.onload = () => {
hideLoading();
};
iframe.onload = hideLoading;
}
});
}
let isRedirect = false;
watch(
() => currentRoute.fullPath,
path => {
if (
currentRoute.name === "Redirect" &&
path.includes(props.frameInfo?.fullPath)
props.frameInfo?.fullPath &&
path.includes(props.frameInfo.fullPath)
) {
frameSrc.value = path; // redirect
isRedirect = true;
loading.value = true;
return;
}
//
if (props.frameInfo?.fullPath === path) {
frameSrc.value = props.frameInfo?.frameSrc;
if (props.frameInfo?.fullPath === path && isRedirect) {
loading.value = true;
clearFallbackTimer();
const url = new URL(props.frameInfo.frameSrc, window.location.origin);
const joinChar = url.search ? "&" : "?";
frameSrc.value = `${props.frameInfo.frameSrc}${joinChar}t=${Date.now()}`;
fallbackTimer.value = window.setTimeout(() => {
if (loading.value) {
hideLoading();
}
}, 1500);
isRedirect = false;
}
},
{ immediate: true }
);
onMounted(() => {

View File

@ -114,14 +114,21 @@ export function useTags() {
]);
function conditionHandle(item, previous, next) {
const currentName = route.name || "";
const itemName = item.name || "";
if (isBoolean(route?.meta?.showLink) && route?.meta?.showLink === false) {
if (Object.keys(route.query).length > 0) {
return isEqual(route.query, item.query) ? previous : next;
return currentName === itemName && isEqual(route.query, item.query)
? previous
: next;
} else {
return isEqual(route.params, item.params) ? previous : next;
return currentName === itemName && isEqual(route.params, item.params)
? previous
: next;
}
} else {
return route.path === item.path ? previous : next;
return currentName === itemName ? previous : next;
}
}

View File

@ -80,22 +80,15 @@ export const useMultiTagsStore = defineStore("pure-multiTags", {
if (isBoolean(tagVal?.meta?.showLink) && !tagVal?.meta?.showLink)
return;
const tagPath = tagVal.path;
// 判断tag是否已存在
const tagHasExits = this.multiTags.some(tag => {
return tag.path === tagPath;
return (
tag.path === tagPath &&
isEqual(tag?.query, tagVal?.query) &&
isEqual(tag?.params, tagVal?.params)
);
});
// 判断tag中的query键值是否相等
const tagQueryHasExits = this.multiTags.some(tag => {
return isEqual(tag?.query, tagVal?.query);
});
// 判断tag中的params键值是否相等
const tagParamsHasExits = this.multiTags.some(tag => {
return isEqual(tag?.params, tagVal?.params);
});
if (tagHasExits && tagQueryHasExits && tagParamsHasExits) return;
if (tagHasExits) return;
// 动态路由可打开的最大数量
const dynamicLevel = tagVal?.meta?.dynamicLevel ?? -1;

View File

@ -2,7 +2,6 @@ import { defineStore } from "pinia";
import {
type cacheType,
store,
debounce,
ascending,
getKeyList,
filterTree,
@ -33,21 +32,8 @@ export const usePermissionStore = defineStore("pure-permission", {
this.constantMenus.concat(routes) as any
);
},
cacheOperate({ mode, name }: cacheType) {
const delIndex = this.cachePageList.findIndex(v => v === name);
switch (mode) {
case "refresh":
this.cachePageList = this.cachePageList.filter(v => v !== name);
break;
case "add":
this.cachePageList.push(name);
break;
case "delete":
delIndex !== -1 && this.cachePageList.splice(delIndex, 1);
break;
}
/** 监听缓存页面是否存在于标签页,不存在则删除 */
debounce(() => {
clearCache() {
let cacheLength = this.cachePageList.length;
const nameList = getKeyList(useMultiTagsStoreHook().multiTags, "name");
while (cacheLength > 0) {
@ -59,7 +45,22 @@ export const usePermissionStore = defineStore("pure-permission", {
);
cacheLength--;
}
})();
},
cacheOperate({ mode, name }: cacheType) {
const delIndex = this.cachePageList.findIndex(v => v === name);
switch (mode) {
case "refresh":
this.cachePageList = this.cachePageList.filter(v => v !== name);
this.clearCache();
break;
case "add":
this.cachePageList.push(name);
break;
case "delete":
delIndex !== -1 && this.cachePageList.splice(delIndex, 1);
this.clearCache();
break;
}
},
/** 清空缓存页面 */
clearAllCachePage() {

View File

@ -68,7 +68,7 @@ onMounted(() => {
<span class="font-medium">
流程图组件采用开源的
<el-link
href="https://site.logic-flow.cn/docs/#/zh/guide/start"
href="https://site.logic-flow.cn/tutorial/get-started"
target="_blank"
style="margin: 0 4px 5px; font-size: 16px"
>