Fixes bug 动态路由配置重构

This commit is contained in:
xiaoma
2021-08-10 17:16:58 +08:00
parent 737f967aab
commit 9c512002d2
23 changed files with 179 additions and 98 deletions

View File

@@ -1,3 +1,19 @@
# CHANGELOG
## 1.5.4 (2021-08-10)
### 🐛 Bug Fixes
- `暗色模式下多页签背景问题 ` 合并 [#23](https://github.com/jekip/naive-ui-admin/pull/23) 感谢 [@Dishone](https://github.com/Dishone)
- `表格设置列重复添加action列样式错乱问题` 合并 [#24](https://github.com/jekip/naive-ui-admin/pull/24) 感谢 [@CasbaL](https://github.com/CasbaL)
- ### ✨ Features
-(破坏性更新)
- 优化 `动态路由配置` 取消`constantRouterComponents.ts`,中组件映射配置,更名为 `router-icons.ts`
- 优化 `admin_info接口结构`roles 更名为permissionsroles.roleName更名为label
- 优化 多级路由,当没有配置时,`redirect` `redirect` 默认为第一个子路由,配置则优先按配置
- 依赖升级
# 1.5.3 (2021-08-09) # 1.5.3 (2021-08-09)
### 🐛 Bug Fixes ### 🐛 Bug Fixes
- 修复顶部菜单,选中联动 - 修复顶部菜单,选中联动

View File

@@ -4,7 +4,7 @@ const menusList = [
{ {
path: '/dashboard', path: '/dashboard',
name: 'Dashboard', name: 'Dashboard',
component: 'Layout', component: 'LAYOUT',
redirect: '/dashboard/console', redirect: '/dashboard/console',
meta: { meta: {
icon: 'DashboardOutlined', icon: 'DashboardOutlined',
@@ -14,7 +14,7 @@ const menusList = [
{ {
path: 'console', path: 'console',
name: 'dashboard_console', name: 'dashboard_console',
component: 'DashboardConsole', component: '/dashboard/console/console',
meta: { meta: {
title: '主控台', title: '主控台',
}, },
@@ -22,7 +22,7 @@ const menusList = [
{ {
path: 'monitor', path: 'monitor',
name: 'dashboard_monitor', name: 'dashboard_monitor',
component: 'DashboardMonitor', component: '/dashboard/monitor/monitor',
meta: { meta: {
title: '监控页', title: '监控页',
}, },
@@ -30,7 +30,7 @@ const menusList = [
{ {
path: 'workplace', path: 'workplace',
name: 'dashboard_workplace', name: 'dashboard_workplace',
component: 'DashboardWorkplace', component: '/dashboard/workplace/workplace',
meta: { meta: {
hidden: true, hidden: true,
title: '工作台', title: '工作台',

View File

@@ -13,25 +13,25 @@ const adminInfo = {
desc: 'manager', desc: 'manager',
password: Random.string('upper', 4, 16), password: Random.string('upper', 4, 16),
token, token,
roles: [ permissions: [
{ {
roleName: '主控台', label: '主控台',
value: 'dashboard_console', value: 'dashboard_console',
}, },
{ {
roleName: '监控页', label: '监控页',
value: 'dashboard_monitor', value: 'dashboard_monitor',
}, },
{ {
roleName: '工作台', label: '工作台',
value: 'dashboard_workplace', value: 'dashboard_workplace',
}, },
{ {
roleName: '基础列表', label: '基础列表',
value: 'basic_list', value: 'basic_list',
}, },
{ {
roleName: '基础列表删除', label: '基础列表删除',
value: 'basic_list_delete', value: 'basic_list_delete',
}, },
], ],

View File

@@ -1,6 +1,6 @@
{ {
"name": "naive-ui-admin", "name": "naive-ui-admin",
"version": "1.5.3", "version": "1.5.4",
"author": { "author": {
"name": "Ahjung", "name": "Ahjung",
"email": "735878602@qq.com", "email": "735878602@qq.com",
@@ -87,7 +87,7 @@
"stylelint-scss": "^3.19.0", "stylelint-scss": "^3.19.0",
"tailwindcss": "^2.2.7", "tailwindcss": "^2.2.7",
"typescript": "^4.3.5", "typescript": "^4.3.5",
"vite": "2.3.6", "vite": "2.4.4",
"vite-plugin-compression": "^0.3.1", "vite-plugin-compression": "^0.3.1",
"vite-plugin-html": "^2.0.7", "vite-plugin-html": "^2.0.7",
"vite-plugin-mock": "^2.9.3", "vite-plugin-mock": "^2.9.3",

View File

@@ -1,6 +1,6 @@
// @ts-ignore // @ts-ignore
import { NButton } from 'naive-ui'; import { NButton } from 'naive-ui';
import { RoleEnum } from '@/enums/roleEnum'; import { PermissionsEnum } from '@/enums/permissionsEnum';
// @ts-ignore // @ts-ignore
export interface ActionItem extends NButton.props { export interface ActionItem extends NButton.props {
onClick?: Fn; onClick?: Fn;
@@ -11,7 +11,7 @@ export interface ActionItem extends NButton.props {
disabled?: boolean; disabled?: boolean;
divider?: boolean; divider?: boolean;
// 权限编码控制是否显示 // 权限编码控制是否显示
auth?: RoleEnum | RoleEnum[] | string | string[]; auth?: PermissionsEnum | PermissionsEnum[] | string | string[];
// 业务控制是否显示 // 业务控制是否显示
ifShow?: boolean | ((action: ActionItem) => boolean); ifShow?: boolean | ((action: ActionItem) => boolean);
} }

View File

@@ -7,8 +7,8 @@ export function usePermission() {
* 检查权限 * 检查权限
* @param accesses * @param accesses
*/ */
function _someRoles(accesses: string[]) { function _somePermissions(accesses: string[]) {
return userStore.getRoles.some((item) => { return userStore.getPermissions.some((item) => {
const { value }: any = item; const { value }: any = item;
return accesses.includes(value); return accesses.includes(value);
}); });
@@ -20,7 +20,7 @@ export function usePermission() {
* */ * */
function hasPermission(accesses: string[]): boolean { function hasPermission(accesses: string[]): boolean {
if (!accesses || !accesses.length) return true; if (!accesses || !accesses.length) return true;
return _someRoles(accesses); return _somePermissions(accesses);
} }
/** /**
@@ -28,9 +28,9 @@ export function usePermission() {
* @param accesses * @param accesses
*/ */
function hasEveryPermission(accesses: string[]): boolean { function hasEveryPermission(accesses: string[]): boolean {
const rolesList = userStore.getRoles; const permissionsList = userStore.getPermissions;
if (Array.isArray(accesses)) { if (Array.isArray(accesses)) {
return accesses.every((access) => !!rolesList[access]); return accesses.every((access) => !!permissionsList[access]);
} }
throw new Error(`[hasEveryPermission]: ${accesses} should be a array !`); throw new Error(`[hasEveryPermission]: ${accesses} should be a array !`);
} }
@@ -41,9 +41,9 @@ export function usePermission() {
* @param accessMap * @param accessMap
*/ */
function hasSomePermission(accesses: string[]): boolean { function hasSomePermission(accesses: string[]): boolean {
const rolesList = userStore.getRoles; const permissionsList = userStore.getPermissions;
if (Array.isArray(accesses)) { if (Array.isArray(accesses)) {
return accesses.some((access) => !!rolesList[access]); return accesses.some((access) => !!permissionsList[access]);
} }
throw new Error(`[hasSomePermission]: ${accesses} should be a array !`); throw new Error(`[hasSomePermission]: ${accesses} should be a array !`);
} }

View File

@@ -13,7 +13,7 @@ export const ErrorPageRoute: AppRouteRecordRaw = {
children: [ children: [
{ {
path: '/:path(.*)*', path: '/:path(.*)*',
name: 'ErrorPage', name: 'ErrorPageSon',
component: ErrorPage, component: ErrorPage,
meta: { meta: {
title: 'ErrorPage', title: 'ErrorPage',

View File

@@ -1,16 +0,0 @@
import { renderIcon } from '@/utils/index';
import { DashboardOutlined } from '@vicons/antd';
// import { RouterTransition } from '@/components/transition'
//前端路由映射表
export const constantRouterComponents = {
Layout: () => import('@/layout/index.vue'), //布局
DashboardConsole: () => import('@/views/dashboard/console/console.vue'), // 主控台
DashboardMonitor: () => import('@/views/dashboard/monitor/monitor.vue'), // 监控页
DashboardWorkplace: () => import('@/views/dashboard/workplace/workplace.vue'), // 工作台
};
//前端路由图标映射表
export const constantRouterIcon = {
DashboardOutlined: renderIcon(DashboardOutlined),
};

View File

@@ -1,12 +1,19 @@
import { adminMenus } from '@/api/system/menu'; import { adminMenus } from '@/api/system/menu';
import { constantRouterComponents, constantRouterIcon } from './constantRouterComponents'; import { constantRouterIcon } from './router-icons';
import router from '@/router/index'; import router from '@/router/index';
import { constantRouter } from '@/router/index'; import { constantRouter } from '@/router/index';
import { RouteRecordRaw } from 'vue-router'; import { RouteRecordRaw } from 'vue-router';
import { Layout, ParentLayout } from '@/router/constant';
import type { AppRouteRecordRaw } from '@/router/types';
const Iframe = () => import('@/views/iframe/index.vue');
const LayoutMap = new Map<string, () => Promise<typeof import('*.vue')>>();
LayoutMap.set('LAYOUT', Layout);
LayoutMap.set('IFRAME', Iframe);
/** /**
* 格式化 后端 结构信息并递归生成层级路由表 * 格式化 后端 结构信息并递归生成层级路由表
*
* @param routerMap * @param routerMap
* @param parent * @param parent
* @returns {*} * @returns {*}
@@ -19,21 +26,24 @@ export const routerGenerator = (routerMap, parent?): any[] => {
// 路由名称,建议唯一 // 路由名称,建议唯一
name: item.name || '', name: item.name || '',
// 该路由对应页面的 组件 // 该路由对应页面的 组件
component: constantRouterComponents[item.component], component: item.component,
// meta: 页面标题, 菜单图标, 页面权限(供指令权限用,可去掉) // meta: 页面标题, 菜单图标, 页面权限(供指令权限用,可去掉)
meta: { meta: {
...item.meta, ...item.meta,
label: item.meta.title, label: item.meta.title,
icon: constantRouterIcon[item.meta.icon] || null, icon: constantRouterIcon[item.meta.icon] || null,
permission: item.meta.permission || null, permissions: item.meta.permissions || null,
}, },
}; };
// 为了防止出现后端返回结果不规范,处理有可能出现拼接出两个 反斜杠 // 为了防止出现后端返回结果不规范,处理有可能出现拼接出两个 反斜杠
currentRouter.path = currentRouter.path.replace('//', '/'); currentRouter.path = currentRouter.path.replace('//', '/');
// 重定向 // 重定向
item.redirect && (currentRouter.redirect = item.redirect); item.redirect && (currentRouter.redirect = item.redirect);
// 是否有子菜单,并递归处理 // 是否有子菜单,并递归处理
if (item.children && item.children.length > 0) { if (item.children && item.children.length > 0) {
//如果未定义 redirect 默认第一个子路由为 redirect
!item.redirect && (currentRouter.redirect = `${item.path}/${item.children[0].path}`);
// Recursion // Recursion
currentRouter.children = routerGenerator(item.children, currentRouter); currentRouter.children = routerGenerator(item.children, currentRouter);
} }
@@ -43,7 +53,6 @@ export const routerGenerator = (routerMap, parent?): any[] => {
/** /**
* 动态生成菜单 * 动态生成菜单
* @param token
* @returns {Promise<Router>} * @returns {Promise<Router>}
*/ */
export const generatorDynamicRouter = (): Promise<RouteRecordRaw[]> => { export const generatorDynamicRouter = (): Promise<RouteRecordRaw[]> => {
@@ -51,7 +60,8 @@ export const generatorDynamicRouter = (): Promise<RouteRecordRaw[]> => {
adminMenus() adminMenus()
.then((result) => { .then((result) => {
const routeList = routerGenerator(result); const routeList = routerGenerator(result);
const asyncRoutesList = [...constantRouter, ...routeList]; asyncImportRoute(routeList);
const asyncRoutesList = [...routeList, ...constantRouter];
asyncRoutesList.forEach((item) => { asyncRoutesList.forEach((item) => {
router.addRoute(item); router.addRoute(item);
}); });
@@ -62,3 +72,56 @@ export const generatorDynamicRouter = (): Promise<RouteRecordRaw[]> => {
}); });
}); });
}; };
/**
* 查找views中对应的组件文件
* */
let viewsModules: Record<string, () => Promise<Recordable>>;
export const asyncImportRoute = (routes: AppRouteRecordRaw[] | undefined): void => {
viewsModules = viewsModules || import.meta.glob('../views/**/*.{vue,tsx}');
if (!routes) return;
routes.forEach((item) => {
if (!item.component && item.meta?.frameSrc) {
item.component = 'IFRAME';
}
const { component, name } = item;
const { children } = item;
if (component) {
const layoutFound = LayoutMap.get(component as string);
if (layoutFound) {
item.component = layoutFound;
} else {
item.component = dynamicImport(viewsModules, component as string);
}
} else if (name) {
item.component = ParentLayout;
}
children && asyncImportRoute(children);
});
};
/**
* 动态导入
* */
export const dynamicImport = (
viewsModules: Record<string, () => Promise<Recordable>>,
component: string
) => {
const keys = Object.keys(viewsModules);
const matchKeys = keys.filter((key) => {
let k = key.replace('../views', '');
const lastIndex = k.lastIndexOf('.');
k = k.substring(0, lastIndex);
return k === component;
});
if (matchKeys?.length === 1) {
const matchKey = matchKeys[0];
return viewsModules[matchKey];
}
if (matchKeys?.length > 1) {
console.warn(
'Please do not create `.vue` and `.TSX` files with the same file name in the same hierarchical directory under the views folder. This will cause dynamic introduction failure'
);
return;
}
};

View File

@@ -1,6 +1,6 @@
import { App } from 'vue'; import { App } from 'vue';
import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router'; import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router';
import { ErrorPageRoute, RedirectRoute } from '@/router/base'; import { RedirectRoute } from '@/router/base';
import { PageEnum } from '@/enums/pageEnum'; import { PageEnum } from '@/enums/pageEnum';
import { createRouterGuards } from './router-guards'; import { createRouterGuards } from './router-guards';
@@ -40,7 +40,7 @@ export const LoginRoute: RouteRecordRaw = {
}; };
//需要验证权限 //需要验证权限
export const asyncRoutes = [ErrorPageRoute, ...routeModuleList]; export const asyncRoutes = [...routeModuleList];
//普通路由 无需验证权限 //普通路由 无需验证权限
export const constantRouter: any[] = [LoginRoute, RootRoute, RedirectRoute]; export const constantRouter: any[] = [LoginRoute, RootRoute, RedirectRoute];

View File

@@ -24,7 +24,7 @@ const routes: Array<RouteRecordRaw> = [
meta: { meta: {
title: 'Dashboard', title: 'Dashboard',
icon: renderIcon(DashboardOutlined), icon: renderIcon(DashboardOutlined),
permission: ['dashboard_console', 'dashboard_console', 'dashboard_workplace'], permissions: ['dashboard_console', 'dashboard_console', 'dashboard_workplace'],
sort: 0, sort: 0,
}, },
children: [ children: [
@@ -33,7 +33,7 @@ const routes: Array<RouteRecordRaw> = [
name: `${routeName}_console`, name: `${routeName}_console`,
meta: { meta: {
title: '主控台', title: '主控台',
permission: ['dashboard_console'], permissions: ['dashboard_console'],
}, },
component: () => import('@/views/dashboard/console/console.vue'), component: () => import('@/views/dashboard/console/console.vue'),
}, },
@@ -42,7 +42,7 @@ const routes: Array<RouteRecordRaw> = [
// name: `${ routeName }_monitor`, // name: `${ routeName }_monitor`,
// meta: { // meta: {
// title: '监控页', // title: '监控页',
// permission: ['dashboard_monitor'] // permissions: ['dashboard_monitor']
// }, // },
// component: () => import('@/views/dashboard/monitor/monitor.vue') // component: () => import('@/views/dashboard/monitor/monitor.vue')
// }, // },
@@ -52,7 +52,7 @@ const routes: Array<RouteRecordRaw> = [
meta: { meta: {
title: '工作台', title: '工作台',
keepAlive: true, keepAlive: true,
permission: ['dashboard_workplace'], permissions: ['dashboard_workplace'],
}, },
component: () => import('@/views/dashboard/workplace/workplace.vue'), component: () => import('@/views/dashboard/workplace/workplace.vue'),
}, },

View File

@@ -1,9 +1,11 @@
import type { RouteRecordRaw } from 'vue-router';
import { isNavigationFailure, Router } from 'vue-router'; import { isNavigationFailure, Router } from 'vue-router';
import { useUserStoreWidthOut } from '@/store/modules/user'; import { useUserStoreWidthOut } from '@/store/modules/user';
import { useAsyncRouteStoreWidthOut } from '@/store/modules/asyncRoute'; import { useAsyncRouteStoreWidthOut } from '@/store/modules/asyncRoute';
import { ACCESS_TOKEN } from '@/store/mutation-types'; import { ACCESS_TOKEN } from '@/store/mutation-types';
import { storage } from '@/utils/Storage'; import { storage } from '@/utils/Storage';
import { PageEnum } from '@/enums/pageEnum'; import { PageEnum } from '@/enums/pageEnum';
import { ErrorPageRoute } from '@/router/base';
const LOGIN_PATH = PageEnum.BASE_LOGIN; const LOGIN_PATH = PageEnum.BASE_LOGIN;
@@ -29,7 +31,7 @@ export function createRouterGuards(router: Router) {
const token = storage.get(ACCESS_TOKEN); const token = storage.get(ACCESS_TOKEN);
if (!token) { if (!token) {
// You can access without permission. You need to set the routing meta.ignoreAuth to true // You can access without permissions. You need to set the routing meta.ignoreAuth to true
if (to.meta.ignoreAuth) { if (to.meta.ignoreAuth) {
next(); next();
return; return;
@@ -60,9 +62,15 @@ export function createRouterGuards(router: Router) {
// 动态添加可访问路由表 // 动态添加可访问路由表
routes.forEach((item) => { routes.forEach((item) => {
router.addRoute(item); router.addRoute(item as unknown as RouteRecordRaw);
}); });
//添加404
const isErrorPage = router.getRoutes().findIndex((item) => item.name === ErrorPageRoute.name);
if (isErrorPage === -1) {
router.addRoute(ErrorPageRoute as unknown as RouteRecordRaw);
}
const redirectPath = (from.query.redirect || to.path) as string; const redirectPath = (from.query.redirect || to.path) as string;
const redirect = decodeURIComponent(redirectPath); const redirect = decodeURIComponent(redirectPath);
const nextData = to.path === redirect ? { ...to, replace: true } : { path: redirect }; const nextData = to.path === redirect ? { ...to, replace: true } : { path: redirect };

View File

@@ -0,0 +1,7 @@
import { renderIcon } from '@/utils/index';
import { DashboardOutlined } from '@vicons/antd';
//前端路由图标映射表
export const constantRouterIcon = {
DashboardOutlined: renderIcon(DashboardOutlined),
};

View File

@@ -1,6 +1,5 @@
import type { RouteRecordRaw, RouteMeta } from 'vue-router'; import type { RouteRecordRaw, RouteMeta } from 'vue-router';
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
import { RoleEnum } from '@/enums/roleEnum';
export type Component<T extends any = any> = export type Component<T extends any = any> =
| ReturnType<typeof defineComponent> | ReturnType<typeof defineComponent>
@@ -23,7 +22,7 @@ export interface Meta {
title: string; title: string;
// 是否忽略权限 // 是否忽略权限
ignoreAuth?: boolean; ignoreAuth?: boolean;
roles?: RoleEnum[]; permissions?: string[];
// 是否不缓存 // 是否不缓存
noKeepAlive?: boolean; noKeepAlive?: boolean;
// 是否固定在tab上 // 是否固定在tab上

View File

@@ -41,7 +41,7 @@ const setting = {
//显示图标 //显示图标
showIcon: false, showIcon: false,
}, },
//菜单权限模式 ROLE 前端固定角色 BACK 动态获取 //菜单权限模式 FIXED 前端固定路由 BACK 动态获取
permissionMode: 'ROLE', permissionMode: 'FIXED',
}; };
export default setting; export default setting;

View File

@@ -88,12 +88,12 @@ export const useAsyncRouteStore = defineStore({
}, },
async generateRoutes(data) { async generateRoutes(data) {
let accessedRouters; let accessedRouters;
const roleList = data.roles || []; const permissionsList = data.permissions || [];
const routeFilter = (route) => { const routeFilter = (route) => {
const { meta } = route; const { meta } = route;
const { permission } = meta || {}; const { permissions } = meta || {};
if (!permission) return true; if (!permissions) return true;
return roleList.some((role) => permission.includes(role.value)); return permissionsList.some((item) => permissions.includes(item.value));
}; };
const { getPermissionMode } = useProjectSetting(); const { getPermissionMode } = useProjectSetting();
const permissionMode = unref(getPermissionMode); const permissionMode = unref(getPermissionMode);

View File

@@ -13,7 +13,7 @@ export interface IUserState {
username: string; username: string;
welcome: string; welcome: string;
avatar: string; avatar: string;
roles: any[]; permissions: any[];
info: any; info: any;
} }
@@ -24,7 +24,7 @@ export const useUserStore = defineStore({
username: '', username: '',
welcome: '', welcome: '',
avatar: '', avatar: '',
roles: [], permissions: [],
info: Storage.get(CURRENT_USER, {}), info: Storage.get(CURRENT_USER, {}),
}), }),
getters: { getters: {
@@ -37,8 +37,8 @@ export const useUserStore = defineStore({
getNickname(): string { getNickname(): string {
return this.username; return this.username;
}, },
getRoles(): [any][] { getPermissions(): [any][] {
return this.roles; return this.permissions;
}, },
getUserInfo(): object { getUserInfo(): object {
return this.info; return this.info;
@@ -51,8 +51,8 @@ export const useUserStore = defineStore({
setAvatar(avatar: string) { setAvatar(avatar: string) {
this.avatar = avatar; this.avatar = avatar;
}, },
setRoles(roles) { setPermissions(permissions) {
this.roles = roles; this.permissions = permissions;
}, },
setUserInfo(info) { setUserInfo(info) {
this.info = info; this.info = info;
@@ -83,12 +83,12 @@ export const useUserStore = defineStore({
getUserInfo() getUserInfo()
.then((res) => { .then((res) => {
const result = res; const result = res;
if (result.roles && result.roles.length) { if (result.permissions && result.permissions.length) {
const roles = result.roles; const permissionsList = result.permissions;
that.setRoles(roles); that.setPermissions(permissionsList);
that.setUserInfo(result); that.setUserInfo(result);
} else { } else {
reject(new Error('getInfo: roles must be a non-null array !')); reject(new Error('getInfo: permissionsList must be a non-null array !'));
} }
that.setAvatar(result.avatar); that.setAvatar(result.avatar);
resolve(res); resolve(res);
@@ -101,7 +101,7 @@ export const useUserStore = defineStore({
// 登出 // 登出
async logout() { async logout() {
this.setRoles([]); this.setPermissions([]);
this.setUserInfo(''); this.setUserInfo('');
storage.remove(ACCESS_TOKEN); storage.remove(ACCESS_TOKEN);
storage.remove(CURRENT_USER); storage.remove(CURRENT_USER);

View File

@@ -1,7 +1,7 @@
<template> <template>
<div class="console"> <div class="console">
<!--数据卡片--> <!--数据卡片-->
<n-grid cols="1 s:2 m:3 l:4 xl:4 2xl:4" responsive="screen" :x-gap="12" :y-gap="8" :cols="4"> <n-grid cols="1 s:2 m:3 l:4 xl:4 2xl:4" responsive="screen" :x-gap="12" :y-gap="8">
<n-grid-item> <n-grid-item>
<NCard <NCard
title="访问量" title="访问量"
@@ -19,14 +19,14 @@
<div class="text-sn"> <div class="text-sn">
日同比 日同比
<CountTo :startVal="1" suffix="%" :endVal="visits.rise" /> <CountTo :startVal="1" suffix="%" :endVal="visits.rise" />
<n-icon size="12" style="color: #00ff6f"> <n-icon size="12" color="#00ff6f">
<component is="CaretUpOutlined" /> <component is="CaretUpOutlined" />
</n-icon> </n-icon>
</div> </div>
<div class="text-sn"> <div class="text-sn">
周同比 周同比
<CountTo :startVal="1" suffix="%" :endVal="visits.decline" /> <CountTo :startVal="1" suffix="%" :endVal="visits.decline" />
<n-icon size="12" style="color: #ffde66"> <n-icon size="12" color="#ffde66">
<component is="CaretDownOutlined" /> <component is="CaretDownOutlined" />
</n-icon> </n-icon>
</div> </div>
@@ -91,14 +91,14 @@
<div class="text-sn"> <div class="text-sn">
日同比 日同比
<CountTo :startVal="1" suffix="%" :endVal="orderLarge.rise" /> <CountTo :startVal="1" suffix="%" :endVal="orderLarge.rise" />
<n-icon size="12" style="color: #00ff6f"> <n-icon size="12" color="#00ff6f">
<component is="CaretUpOutlined" /> <component is="CaretUpOutlined" />
</n-icon> </n-icon>
</div> </div>
<div class="text-sn"> <div class="text-sn">
周同比 周同比
<CountTo :startVal="1" suffix="%" :endVal="orderLarge.rise" /> <CountTo :startVal="1" suffix="%" :endVal="orderLarge.rise" />
<n-icon size="12" style="color: #ffde66"> <n-icon size="12" color="#ffde66">
<component is="CaretDownOutlined" /> <component is="CaretDownOutlined" />
</n-icon> </n-icon>
</div> </div>
@@ -130,14 +130,14 @@
<div class="text-sn"> <div class="text-sn">
月同比 月同比
<CountTo :startVal="1" suffix="%" :endVal="volume.rise" /> <CountTo :startVal="1" suffix="%" :endVal="volume.rise" />
<n-icon size="12" style="color: #00ff6f"> <n-icon size="12" color="#00ff6f">
<component is="CaretUpOutlined" /> <component is="CaretUpOutlined" />
</n-icon> </n-icon>
</div> </div>
<div class="text-sn"> <div class="text-sn">
月同比 月同比
<CountTo :startVal="1" suffix="%" :endVal="volume.decline" /> <CountTo :startVal="1" suffix="%" :endVal="volume.decline" />
<n-icon size="12" style="color: #ffde66"> <n-icon size="12" color="#ffde66">
<component is="CaretDownOutlined" /> <component is="CaretDownOutlined" />
</n-icon> </n-icon>
</div> </div>
@@ -156,14 +156,14 @@
<!--导航卡片--> <!--导航卡片-->
<div class="mt-4"> <div class="mt-4">
<n-grid cols="1 s:2 m:3 l:8 xl:8 2xl:8" responsive="screen" :x-gap="16" :y-gap="8" :cols="8"> <n-grid cols="1 s:2 m:3 l:8 xl:8 2xl:8" responsive="screen" :x-gap="16" :y-gap="8">
<n-grid-item v-for="(item, index) in iconList" :key="index"> <n-grid-item v-for="(item, index) in iconList" :key="index">
<NCard content-style="padding-top: 0;" size="small" :bordered="false"> <NCard content-style="padding-top: 0;" size="small" :bordered="false">
<template #footer> <template #footer>
<div class="cursor-pointer"> <div class="cursor-pointer">
<p class="flex justify-center"> <p class="flex justify-center">
<span> <span>
<n-icon :size="item.size" class="flex-1" :style="{ color: `${item.color}` }"> <n-icon :size="item.size" class="flex-1" :color="item.color">
<component :is="item.icon" v-on="item.eventObject || {}" /> <component :is="item.icon" v-on="item.eventObject || {}" />
</n-icon> </n-icon>
</span> </span>

View File

@@ -29,9 +29,9 @@
<style lang="less" scoped> <style lang="less" scoped>
.page-container { .page-container {
width: 100%; width: 100%;
background-color: white;
border-radius: 4px; border-radius: 4px;
padding: 50px 0; padding: 50px 0;
height: 100vh;
.text-center { .text-center {
h1 { h1 {

View File

@@ -29,9 +29,9 @@
<style lang="less" scoped> <style lang="less" scoped>
.page-container { .page-container {
width: 100%; width: 100%;
background-color: white;
border-radius: 4px; border-radius: 4px;
padding: 50px 0; padding: 50px 0;
height: 100vh;
.text-center { .text-center {
h1 { h1 {

View File

@@ -29,9 +29,9 @@
<style lang="less" scoped> <style lang="less" scoped>
.page-container { .page-container {
width: 100%; width: 100%;
background-color: white;
border-radius: 4px; border-radius: 4px;
padding: 50px 0; padding: 50px 0;
height: 100vh;
.text-center { .text-center {
h1 { h1 {

View File

@@ -13,7 +13,7 @@
ref="actionRef" ref="actionRef"
:actionColumn="actionColumn" :actionColumn="actionColumn"
@update:checked-row-keys="onCheckedRow" @update:checked-row-keys="onCheckedRow"
:scroll-x="1010" :scroll-x="1090"
> >
<template #tableTitle> <template #tableTitle>
<n-button type="primary" @click="addTable"> <n-button type="primary" @click="addTable">

View File

@@ -2696,11 +2696,6 @@ esbuild@0.11.3:
resolved "https://registry.nlark.com/esbuild/download/esbuild-0.11.3.tgz#b57165b907be4ffba651f6450538ce8d8c1d5eb0" resolved "https://registry.nlark.com/esbuild/download/esbuild-0.11.3.tgz#b57165b907be4ffba651f6450538ce8d8c1d5eb0"
integrity sha1-tXFluQe+T/umUfZFBTjOjYwdXrA= integrity sha1-tXFluQe+T/umUfZFBTjOjYwdXrA=
esbuild@^0.12.5:
version "0.12.15"
resolved "https://registry.nlark.com/esbuild/download/esbuild-0.12.15.tgz?cache=0&sync_timestamp=1625545660518&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fesbuild%2Fdownload%2Fesbuild-0.12.15.tgz#9d99cf39aeb2188265c5983e983e236829f08af0"
integrity sha1-nZnPOa6yGIJlxZg+mD4jaCnwivA=
esbuild@^0.12.6, esbuild@^0.12.8: esbuild@^0.12.6, esbuild@^0.12.8:
version "0.12.14" version "0.12.14"
resolved "https://registry.nlark.com/esbuild/download/esbuild-0.12.14.tgz?cache=0&sync_timestamp=1625183314696&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fesbuild%2Fdownload%2Fesbuild-0.12.14.tgz#43157dbd0b36d939247d4eb4909a4886ac40f82e" resolved "https://registry.nlark.com/esbuild/download/esbuild-0.12.14.tgz?cache=0&sync_timestamp=1625183314696&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fesbuild%2Fdownload%2Fesbuild-0.12.14.tgz#43157dbd0b36d939247d4eb4909a4886ac40f82e"
@@ -5569,7 +5564,7 @@ postcss@^8.1.10:
nanoid "^3.1.23" nanoid "^3.1.23"
source-map-js "^0.6.2" source-map-js "^0.6.2"
postcss@^8.1.6, postcss@^8.2.1, postcss@^8.2.10, postcss@^8.3.5: postcss@^8.1.6, postcss@^8.2.1, postcss@^8.3.5:
version "8.3.5" version "8.3.5"
resolved "https://registry.nlark.com/postcss/download/postcss-8.3.5.tgz#982216b113412bc20a86289e91eb994952a5b709" resolved "https://registry.nlark.com/postcss/download/postcss-8.3.5.tgz#982216b113412bc20a86289e91eb994952a5b709"
integrity sha1-mCIWsRNBK8IKhiiekeuZSVKltwk= integrity sha1-mCIWsRNBK8IKhiiekeuZSVKltwk=
@@ -5578,6 +5573,15 @@ postcss@^8.1.6, postcss@^8.2.1, postcss@^8.2.10, postcss@^8.3.5:
nanoid "^3.1.23" nanoid "^3.1.23"
source-map-js "^0.6.2" source-map-js "^0.6.2"
postcss@^8.3.6:
version "8.3.6"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.3.6.tgz#2730dd76a97969f37f53b9a6096197be311cc4ea"
integrity sha512-wG1cc/JhRgdqB6WHEuyLTedf3KIRuD0hG6ldkFEZNCjRxiC+3i6kkWUUbiJQayP28iwG35cEmAbe98585BYV0A==
dependencies:
colorette "^1.2.2"
nanoid "^3.1.23"
source-map-js "^0.6.2"
prelude-ls@^1.2.1: prelude-ls@^1.2.1:
version "1.2.1" version "1.2.1"
resolved "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" resolved "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396"
@@ -6858,17 +6862,17 @@ vite-plugin-style-import@^1.0.1:
es-module-lexer "^0.6.0" es-module-lexer "^0.6.0"
magic-string "^0.25.7" magic-string "^0.25.7"
vite@2.3.6: vite@2.4.4:
version "2.3.6" version "2.4.4"
resolved "https://registry.nlark.com/vite/download/vite-2.3.6.tgz#1f7cfde88a51a802d69000c7bac85d481c2e871c" resolved "https://registry.yarnpkg.com/vite/-/vite-2.4.4.tgz#8c402a07ad45f168f6eb5428bead38f3e4363e47"
integrity sha1-H3z96IpRqALWkADHushdSBwuhxw= integrity sha512-m1wK6pFJKmaYA6AeZIUXyiAgUAAJzVXhIMYCdZUpCaFMGps0v0IlNJtbmPvkUhVEyautalajmnW5X6NboUPsnw==
dependencies: dependencies:
esbuild "^0.12.5" esbuild "^0.12.8"
postcss "^8.2.10" postcss "^8.3.6"
resolve "^1.19.0" resolve "^1.20.0"
rollup "^2.38.5" rollup "^2.38.5"
optionalDependencies: optionalDependencies:
fsevents "~2.3.1" fsevents "~2.3.2"
vooks@^0.2.4, vooks@^0.2.6: vooks@^0.2.4, vooks@^0.2.6:
version "0.2.6" version "0.2.6"