Compare commits

..

7 Commits

35 changed files with 2118 additions and 4420 deletions

View File

@@ -1,3 +1,20 @@
# 3.9.3 (2022-12-04)
### 🎫 Feat
- Add `@pureadmin/table` pagination and loading animation example
### 🐞 Bug fixes
- Fixed the problem that the refresh page would be blank due to changes in dynamic routes stored in local storage after enabling `CachingAsyncRoutes`
- Fixed `Tooltip` displayed abnormally after the menu is collapsed
### 🍏 Perf
- Expand the use of local icons, the first launch of the full version reduces `13` requests again
- When the menu loading is slow, add `loading` animation to optimize user experience
- Theme initialization is placed in `onBeforeMount` to avoid flashing of the initialization page
# 3.9.2 (2022-12-03)
### 🍏 Perf

View File

@@ -1,3 +1,20 @@
# 3.9.3 (2022-12-04)
### 🎫 Feat
- Add `@pureadmin/table` pagination and loading animation example
### 🐞 Bug fixes
- Fixed the problem that the refresh page would be blank due to changes in dynamic routes stored in local storage after enabling `CachingAsyncRoutes`
- Fixed `Tooltip` displayed abnormally after the menu is collapsed
### 🍏 Perf
- Expand the use of local icons, the first launch of the full version reduces `13` requests again
- When the menu loading is slow, add `loading` animation to optimize user experience
- Theme initialization is placed in `onBeforeMount` to avoid flashing of the initialization page
# 3.9.2 (2022-12-03)
### 🍏 Perf

View File

@@ -1,3 +1,20 @@
# 3.9.3 (2022-12-04)
### 🎫 Feat
- 添加 `@pureadmin/table` 分页和加载动画示例
### 🐞 Bug fixes
- 修复开启 `CachingAsyncRoutes` 后,存入本地存储的动态路由改变造成刷新页面空白的问题
- 修复菜单折叠后 `Tooltip` 显示异常
### 🍏 Perf
- 扩展本地图标使用方式,完整版首启动再次减少 `13` 个请求
- 当菜单加载慢时,添加 `loading` 动画,优化用户体验
- 主题初始化放在 `onBeforeMount` 里,避免初始化页面闪烁
# 3.9.2 (2022-12-03)
### 🍏 Perf

View File

@@ -99,7 +99,7 @@ menus:
hsInfiniteScroll: 表格无限滚动
hsdanmaku: 弹幕组件
hsPureTableBase: 基础用法23个示例
hsPureTableHigh: 高级用法(8个示例)
hsPureTableHigh: 高级用法(9个示例)
hsTree: 大数据树业务组件
hsMenuoverflow: 目录超出显示 Tooltip 文字提示
hsChildMenuoverflow: 菜单超出显示 Tooltip 文字提示

View File

@@ -2,14 +2,6 @@
import { MockMethod } from "vite-plugin-mock";
import { system, permission, frame, tabs } from "@/router/enums";
import FlUser from "@iconify-icons/ri/admin-line";
import Role from "@iconify-icons/ri/admin-fill";
import Dict from "@iconify-icons/ri/git-repository-line";
import Setting from "@iconify-icons/ri/settings-3-line";
import Dept from "@iconify-icons/ri/git-branch-line";
import Lollipop from "@iconify-icons/ep/lollipop";
import Monitor from "@iconify-icons/ep/monitor";
/**
* roles页面级别权限这里模拟二种 "admin"、"common"
* admin管理员角色
@@ -19,7 +11,7 @@ import Monitor from "@iconify-icons/ep/monitor";
const systemRouter = {
path: "/system",
meta: {
icon: Setting,
icon: "setting",
title: "menus.hssysManagement",
rank: system
},
@@ -28,7 +20,7 @@ const systemRouter = {
path: "/system/user/index",
name: "User",
meta: {
icon: FlUser,
icon: "flUser",
title: "menus.hsUser",
roles: ["admin"]
}
@@ -37,7 +29,7 @@ const systemRouter = {
path: "/system/role/index",
name: "Role",
meta: {
icon: Role,
icon: "role",
title: "menus.hsRole",
roles: ["admin"]
}
@@ -46,7 +38,7 @@ const systemRouter = {
path: "/system/dept/index",
name: "Dept",
meta: {
icon: Dept,
icon: "dept",
title: "menus.hsDept",
roles: ["admin"]
}
@@ -56,7 +48,7 @@ const systemRouter = {
component: "/system/dict/index",
name: "Dict",
meta: {
icon: Dict,
icon: "dict",
title: "menus.hsDict",
keepAlive: true,
roles: ["admin"]
@@ -69,7 +61,7 @@ const permissionRouter = {
path: "/permission",
meta: {
title: "menus.permission",
icon: Lollipop,
icon: "lollipop",
rank: permission
},
children: [
@@ -96,7 +88,7 @@ const permissionRouter = {
const frameRouter = {
path: "/iframe",
meta: {
icon: Monitor,
icon: "monitor",
title: "menus.hsExternalPage",
rank: frame
},

View File

@@ -1,6 +1,6 @@
{
"name": "vue-pure-admin",
"version": "3.9.2",
"version": "3.9.3",
"private": true,
"scripts": {
"dev": "NODE_OPTIONS=--max-old-space-size=4096 vite",
@@ -35,8 +35,8 @@
"@logicflow/core": "^1.1.30",
"@logicflow/extension": "^1.1.30",
"@pureadmin/descriptions": "^1.1.0",
"@pureadmin/table": "^1.8.2",
"@pureadmin/utils": "^1.7.2",
"@pureadmin/table": "^1.8.3",
"@pureadmin/utils": "^1.7.4",
"@vueuse/core": "^9.6.0",
"@vueuse/motion": "2.0.0-beta.12",
"@wangeditor/editor": "^5.1.21",

6138
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.3",
"Title": "PureAdmin",
"FixedHeader": true,
"HiddenSideBar": false,

View File

@@ -0,0 +1,51 @@
import { addIcon } from "@iconify/vue/dist/offline";
/**
* 这里存放本地图标,在 src/layout/index.vue 文件中加载,避免在首启动加载
*/
// 本地菜单图标后端在路由的icon中返回对应的图标字符串并且前端在此处使用addIcon添加即可渲染菜单图标
import UbuntuFill from "@iconify-icons/ri/ubuntu-fill";
import Menu from "@iconify-icons/ep/menu";
import Edit from "@iconify-icons/ep/edit";
import InformationLine from "@iconify-icons/ri/information-line";
import SetUp from "@iconify-icons/ep/set-up";
import TerminalWindowLine from "@iconify-icons/ri/terminal-window-line";
import Guide from "@iconify-icons/ep/guide";
import HomeFilled from "@iconify-icons/ep/home-filled";
import Card from "@iconify-icons/ri/bank-card-line";
import ListCheck from "@iconify-icons/ri/list-check";
import Histogram from "@iconify-icons/ep/histogram";
import Ppt from "@iconify-icons/ri/file-ppt-2-line";
import CheckboxCircleLine from "@iconify-icons/ri/checkbox-circle-line";
import FlUser from "@iconify-icons/ri/admin-line";
import Role from "@iconify-icons/ri/admin-fill";
import Dict from "@iconify-icons/ri/git-repository-line";
import Setting from "@iconify-icons/ri/settings-3-line";
import Dept from "@iconify-icons/ri/git-branch-line";
import Lollipop from "@iconify-icons/ep/lollipop";
import Monitor from "@iconify-icons/ep/monitor";
addIcon("ubuntuFill", UbuntuFill);
addIcon("menu", Menu);
addIcon("edit", Edit);
addIcon("informationLine", InformationLine);
addIcon("setUp", SetUp);
addIcon("terminalWindowLine", TerminalWindowLine);
addIcon("guide", Guide);
addIcon("homeFilled", HomeFilled);
addIcon("card", Card);
addIcon("listCheck", ListCheck);
addIcon("histogram", Histogram);
addIcon("ppt", Ppt);
addIcon("checkboxCircleLine", CheckboxCircleLine);
addIcon("flUser", FlUser);
addIcon("role", Role);
addIcon("dict", Dict);
addIcon("setting", Setting);
addIcon("dept", Dept);
addIcon("lollipop", Lollipop);
addIcon("monitor", Monitor);
// 非菜单图标
import RefreshRight from "@iconify-icons/ep/refresh-right";
addIcon("refreshRight", RefreshRight);

View File

@@ -13,7 +13,6 @@ import { isString } from "@pureadmin/utils";
import { propTypes } from "@/utils/propTypes";
import { IconifyIconOffline } from "../../ReIcon";
import QRCode, { QRCodeRenderersOptions } from "qrcode";
import RefreshRight from "@iconify-icons/ep/refresh-right";
interface QrcodeLogo {
src?: string;
@@ -248,7 +247,7 @@ export default defineComponent({
<div class="absolute top-[50%] left-[50%] font-bold">
<IconifyIconOffline
class="cursor-pointer"
icon={RefreshRight}
icon="refreshRight"
width="30"
color="var(--el-color-primary)"
/>

View File

@@ -6,7 +6,6 @@ import { IconifyIconOffline } from "../../ReIcon";
import Expand from "@iconify-icons/mdi/arrow-expand-down";
import ArrowCollapse from "@iconify-icons/mdi/arrow-collapse-vertical";
import Setting from "@iconify-icons/ri/settings-3-line";
import RefreshRight from "@iconify-icons/ep/refresh-right";
export const loadingSvg = `
<path class="path" d="
@@ -153,7 +152,7 @@ export default defineComponent({
<el-tooltip effect="dark" content="刷新" placement="top">
<IconifyIconOffline
class="cursor-pointer"
icon={RefreshRight}
icon="refreshRight"
width="16"
color="text_color_regular"
onClick={() => emit("refresh")}

View File

@@ -1,4 +1,13 @@
<script setup lang="ts">
import {
ref,
unref,
watch,
reactive,
computed,
nextTick,
onBeforeMount
} from "vue";
import {
useDark,
debounce,
@@ -17,7 +26,6 @@ import { useNav } from "@/layout/hooks/useNav";
import { useAppStoreHook } from "@/store/modules/app";
import { toggleTheme } from "@pureadmin/theme/dist/browser-utils";
import { useMultiTagsStoreHook } from "@/store/modules/multiTags";
import { ref, unref, watch, reactive, computed, nextTick } from "vue";
import { useDataThemeChange } from "@/layout/hooks/useDataThemeChange";
import dayIcon from "@/assets/svg/day.svg?component";
@@ -189,16 +197,6 @@ function setLayoutModel(layout: string) {
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 }) => {
switch (layout["layout"]) {
case "vertical":
@@ -218,6 +216,18 @@ watch($storage, ({ layout }) => {
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>
<template>

View File

@@ -41,7 +41,10 @@ watch(
</script>
<template>
<div class="horizontal-header">
<div
v-loading="usePermissionStoreHook().wholeMenus.length === 0"
class="horizontal-header"
>
<div class="horizontal-header-left" @click="backHome">
<FontIcon icon="team-iconlogo" svg style="width: 35px; height: 35px" />
<h4>{{ title }}</h4>
@@ -129,6 +132,10 @@ watch(
</template>
<style lang="scss" scoped>
:deep(.el-loading-mask) {
opacity: 0.45;
}
.translation {
::v-deep(.el-dropdown-menu__item) {
padding: 5px 40px;

View File

@@ -58,7 +58,11 @@ watch(
</script>
<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
router
ref="menuRef"
@@ -161,6 +165,10 @@ watch(
</template>
<style lang="scss" scoped>
:deep(.el-loading-mask) {
opacity: 0.45;
}
.translation {
::v-deep(.el-dropdown-menu__item) {
padding: 5px 40px;

View File

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

View File

@@ -59,7 +59,10 @@ watch(
</script>
<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" />
<el-scrollbar
wrap-class="scrollbar-wrapper"
@@ -91,3 +94,9 @@ watch(
/>
</div>
</template>
<style scoped>
:deep(.el-loading-mask) {
opacity: 0.45;
}
</style>

View File

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

View File

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

View File

@@ -1,12 +1,11 @@
import { $t } from "@/plugins/i18n";
import { able } from "@/router/enums";
import UbuntuFill from "@iconify-icons/ri/ubuntu-fill";
export default {
path: "/able",
redirect: "/able/watermark",
meta: {
icon: UbuntuFill,
icon: "ubuntuFill",
title: $t("menus.hsAble"),
rank: able
},

View File

@@ -1,12 +1,11 @@
import { $t } from "@/plugins/i18n";
import { components } from "@/router/enums";
import Menu from "@iconify-icons/ep/menu";
export default {
path: "/components",
redirect: "/components/video",
meta: {
icon: Menu,
icon: "menu",
title: $t("menus.hscomponents"),
rank: components
},

View File

@@ -1,12 +1,11 @@
import { $t } from "@/plugins/i18n";
import { editor } from "@/router/enums";
import Edit from "@iconify-icons/ep/edit";
export default {
path: "/editor",
redirect: "/editor/index",
meta: {
icon: Edit,
icon: "edit",
title: $t("menus.hseditor"),
rank: editor
},

View File

@@ -1,12 +1,11 @@
import { $t } from "@/plugins/i18n";
import { error } from "@/router/enums";
import InformationLine from "@iconify-icons/ri/information-line";
export default {
path: "/error",
redirect: "/error/403",
meta: {
icon: InformationLine,
icon: "informationLine",
title: $t("menus.hsabnormal"),
rank: error
},

View File

@@ -1,12 +1,11 @@
import { $t } from "@/plugins/i18n";
import { flowchart } from "@/router/enums";
import SetUp from "@iconify-icons/ep/set-up";
export default {
path: "/flowChart",
redirect: "/flowChart/index",
meta: {
icon: SetUp,
icon: "setUp",
title: $t("menus.hsflowChart"),
rank: flowchart
},

View File

@@ -2,13 +2,11 @@ import { $t } from "@/plugins/i18n";
import { formdesign } from "@/router/enums";
const IFrame = () => import("@/layout/frameView.vue");
import TerminalWindowLine from "@iconify-icons/ri/terminal-window-line";
export default {
path: "/formDesign",
redirect: "/formDesign/index",
meta: {
icon: TerminalWindowLine,
icon: "terminalWindowLine",
title: $t("menus.hsFormDesign"),
rank: formdesign
},

View File

@@ -1,12 +1,11 @@
import { $t } from "@/plugins/i18n";
import { guide } from "@/router/enums";
import Guide from "@iconify-icons/ep/guide";
export default {
path: "/guide",
redirect: "/guide/index",
meta: {
icon: Guide,
icon: "guide",
title: $t("menus.hsguide"),
rank: guide
},

View File

@@ -1,7 +1,6 @@
import { $t } from "@/plugins/i18n";
import { home } from "@/router/enums";
const Layout = () => import("@/layout/index.vue");
import HomeFilled from "@iconify-icons/ep/home-filled";
export default {
path: "/",
@@ -9,7 +8,7 @@ export default {
component: Layout,
redirect: "/welcome",
meta: {
icon: HomeFilled,
icon: "homeFilled",
title: $t("menus.hshome"),
rank: home
},

View File

@@ -1,13 +1,11 @@
import { $t } from "@/plugins/i18n";
import { list } from "@/router/enums";
import ListCheck from "@iconify-icons/ri/list-check";
import Card from "@iconify-icons/ri/bank-card-line";
export default {
path: "/list",
redirect: "/list/card",
meta: {
icon: ListCheck,
icon: "listCheck",
title: $t("menus.hsList"),
rank: list
},
@@ -17,7 +15,7 @@ export default {
name: "ListCard",
component: () => import("@/views/list/card/index.vue"),
meta: {
icon: Card,
icon: "card",
title: $t("menus.hsListCard"),
showParent: true
}

View File

@@ -1,13 +1,12 @@
import { $t } from "@/plugins/i18n";
import { nested } from "@/router/enums";
import Histogram from "@iconify-icons/ep/histogram";
export default {
path: "/nested",
redirect: "/nested/menu1/menu1-1",
meta: {
title: $t("menus.hsmenus"),
icon: Histogram,
icon: "histogram",
rank: nested
},
children: [

View File

@@ -1,12 +1,11 @@
import { ppt } from "@/router/enums";
const IFrame = () => import("@/layout/frameView.vue");
import Ppt from "@iconify-icons/ri/file-ppt-2-line";
export default {
path: "/ppt",
redirect: "/ppt/index",
meta: {
icon: Ppt,
icon: "ppt",
title: "PPT",
rank: ppt
},

View File

@@ -1,6 +1,5 @@
import { $t } from "@/plugins/i18n";
const Layout = () => import("@/layout/index.vue");
import HomeFilled from "@iconify-icons/ep/home-filled";
export default [
{
@@ -17,7 +16,7 @@ export default [
path: "/redirect",
component: Layout,
meta: {
icon: HomeFilled,
icon: "homeFilled",
title: $t("menus.hshome"),
showLink: false,
rank: 102

View File

@@ -1,12 +1,11 @@
import { $t } from "@/plugins/i18n";
import { result } from "@/router/enums";
import CheckboxCircleLine from "@iconify-icons/ri/checkbox-circle-line";
export default {
path: "/result",
redirect: "/result/success",
meta: {
icon: CheckboxCircleLine,
icon: "checkboxCircleLine",
title: $t("menus.hsResult"),
rank: result
},

View File

@@ -198,7 +198,7 @@ function initRouter() {
} else {
return new Promise(resolve => {
getAsyncRoutes().then(({ data }) => {
handleAsyncRoutes(data);
handleAsyncRoutes(cloneDeep(data));
storageSession.setItem(key, data);
resolve(router);
});
@@ -207,7 +207,7 @@ function initRouter() {
} else {
return new Promise(resolve => {
getAsyncRoutes().then(({ data }) => {
handleAsyncRoutes(data);
handleAsyncRoutes(cloneDeep(data));
resolve(router);
});
});

View File

@@ -1,3 +1,4 @@
import Page from "./page/index.vue";
import RowDrag from "./drag/row/index.vue";
import ColumnDrag from "./drag/column/index.vue";
import Contextmenu from "./contextmenu/index.vue";
@@ -11,6 +12,12 @@ const rendContent = (val: string) =>
`代码位置src/views/pure-table/high/${val}/index.vue`;
export const list = [
{
key: "page",
content: rendContent("page"),
title: "分页、加载动画",
component: Page
},
{
key: "rowDrag",
content: rendContent("drag/row"),

View File

@@ -0,0 +1,91 @@
import { tableData } from "../data";
import { clone, delay } from "@pureadmin/utils";
import { ref, onMounted, reactive, watchEffect } from "vue";
import type { PaginationProps, LoadingConfig, Align } from "@pureadmin/table";
export function useColumns() {
const dataList = ref([]);
const loading = ref(true);
const paginationAlign = ref("right");
const columns: TableColumnList = [
{
label: "日期",
prop: "date"
},
{
label: "姓名",
prop: "name"
},
{
label: "地址",
prop: "address"
}
];
/** 分页配置 */
const pagination = reactive<PaginationProps>({
pageSize: 10,
currentPage: 1,
pageSizes: [10, 15, 20],
total: 0,
align: "right",
background: true
});
/** 加载动画配置 */
const loadingConfig = reactive<LoadingConfig>({
text: "正在加载第一页...",
viewBox: "-10, -10, 50, 50",
spinner: `
<path class="path" d="
M 30 15
L 28 17
M 25.61 25.61
A 15 15, 0, 0, 1, 15 30
A 15 15, 0, 1, 1, 27.99 7.5
L 15 15
" style="stroke-width: 4px; fill: rgba(0, 0, 0, 0)"/>
`
// svg: "",
// background: rgba()
});
function onSizeChange(val) {
console.log("onSizeChange", val);
}
function onCurrentChange(val) {
loadingConfig.text = `正在加载第${val}页...`;
loading.value = true;
delay(600).then(() => {
loading.value = false;
});
}
watchEffect(() => {
pagination.align = paginationAlign.value as Align;
});
onMounted(() => {
delay(600).then(() => {
const newList = [];
Array.from({ length: 6 }).forEach(() => {
newList.push(clone(tableData, true));
});
dataList.value = newList.flat(Infinity);
pagination.total = dataList.value.length;
loading.value = false;
});
});
return {
loading,
columns,
dataList,
pagination,
loadingConfig,
paginationAlign,
onSizeChange,
onCurrentChange
};
}

View File

@@ -0,0 +1,46 @@
<script setup lang="ts">
import { useColumns } from "./columns";
const {
loading,
columns,
dataList,
pagination,
loadingConfig,
paginationAlign,
onSizeChange,
onCurrentChange
} = useColumns();
</script>
<template>
<div>
<el-space class="float-right mb-2">
<p>分页的对齐方式</p>
<el-radio-group v-model="paginationAlign">
<el-radio-button label="right">right</el-radio-button>
<el-radio-button label="center">center</el-radio-button>
<el-radio-button label="left">left</el-radio-button>
</el-radio-group>
</el-space>
<pure-table
border
row-key="id"
alignWhole="center"
showOverflowTooltip
:loading="loading"
:loading-config="loadingConfig"
:height="440"
:data="
dataList.slice(
(pagination.currentPage - 1) * pagination.pageSize,
pagination.currentPage * pagination.pageSize
)
"
:columns="columns"
:pagination="pagination"
@size-change="onSizeChange"
@current-change="onCurrentChange"
/>
</div>
</template>