-
+
+
+
+
+
+
+
-
+
diff --git a/src/main.ts b/src/main.ts
index 3a397c2..b003f11 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -1,11 +1,12 @@
-import { createApp } from "vue";
-import App from "./App.vue";
-import router, { setupRouter } from "@/router";
-import { setupStore } from "@/store";
-import { setupNaive, setupNaiveDiscreteApi, setupDirectives } from "@/plugins";
-
+import './styles/tailwind.css';
+import { createApp } from 'vue';
+import App from './App.vue';
+import router, { setupRouter } from '@/router';
+import { setupStore } from '@/store';
+import { setupNaive, setupNaiveDiscreteApi, setupDirectives } from '@/plugins';
+import SvgIcon from '@/components/SvgIcon/index.vue';
// 样式
-import "@/styles/index.scss";
+import '@/styles/index.scss';
async function appInit() {
const app = createApp(App);
@@ -24,15 +25,17 @@ async function appInit() {
// 挂载路由
setupRouter(app);
+ app.component('SvgIcon', SvgIcon);
+
// 路由准备就绪后挂载APP实例
await router.isReady();
- const meta = document.createElement("meta");
- meta.name = "naive-ui-style";
+ const meta = document.createElement('meta');
+ meta.name = 'naive-ui-style';
document.head.appendChild(meta);
// 挂载到页面
- app.mount("#app", true);
+ app.mount('#app', true);
}
void appInit();
diff --git a/src/plugins/assets.ts b/src/plugins/assets.ts
new file mode 100644
index 0000000..1aa8a00
--- /dev/null
+++ b/src/plugins/assets.ts
@@ -0,0 +1 @@
+import 'virtual:svg-icons-register';
diff --git a/src/plugins/directives.ts b/src/plugins/directives.ts
index 29b09d0..d90782c 100644
--- a/src/plugins/directives.ts
+++ b/src/plugins/directives.ts
@@ -1,11 +1,11 @@
-import { App } from "vue";
+import { App } from 'vue';
-import draggable from "@/directives/draggable";
+import draggable from '@/directives/draggable';
/**
* 注册全局自定义指令
* @param app
*/
export function setupDirectives(app: App) {
// 拖拽指令
- app.directive("draggable", draggable);
+ app.directive('draggable', draggable);
}
diff --git a/src/plugins/icon.ts b/src/plugins/icon.ts
index c22f54e..179951c 100644
--- a/src/plugins/icon.ts
+++ b/src/plugins/icon.ts
@@ -7,7 +7,7 @@ import {
InformationCircleOutline as InformationCircleIcon,
BagOutline as BagOutlineIcon,
BagCheckOutline as BagCheckOutlineIcon,
-} from "@vicons/ionicons5";
+} from '@vicons/ionicons5';
const ionicons5 = {
AlbumsOutlineIcon,
diff --git a/src/plugins/index.ts b/src/plugins/index.ts
index 7df428d..c1e709c 100644
--- a/src/plugins/index.ts
+++ b/src/plugins/index.ts
@@ -1,4 +1,5 @@
-export { setupNaive } from "@/plugins/naive";
-export { icons } from "@/plugins/icon";
-export { setupNaiveDiscreteApi } from "@/plugins/naiveDiscreteApi";
-export { setupDirectives } from "@/plugins/directives";
+export { setupNaive } from '@/plugins/naive';
+export { icons } from '@/plugins/icon';
+export { setupNaiveDiscreteApi } from '@/plugins/naiveDiscreteApi';
+export { setupDirectives } from '@/plugins/directives';
+export * from '@/plugins/assets';
diff --git a/src/plugins/naive.ts b/src/plugins/naive.ts
index 80acf06..80e1cff 100644
--- a/src/plugins/naive.ts
+++ b/src/plugins/naive.ts
@@ -1,4 +1,4 @@
-import type { App } from "vue";
+import type { App } from 'vue';
import {
create,
NA,
@@ -100,7 +100,7 @@ import {
NWatermark,
NEmpty,
NCollapseTransition,
-} from "naive-ui";
+} from 'naive-ui';
const naive = create({
components: [
diff --git a/src/plugins/naiveDiscreteApi.ts b/src/plugins/naiveDiscreteApi.ts
index 7f64cbc..5844469 100644
--- a/src/plugins/naiveDiscreteApi.ts
+++ b/src/plugins/naiveDiscreteApi.ts
@@ -1,5 +1,7 @@
-import * as NaiveUI from "naive-ui";
-import { computed } from "vue";
+import * as NaiveUI from 'naive-ui';
+import { computed } from 'vue';
+import { useDesignSetting } from '@/store/modules/designSetting';
+import { lighten } from '@/utils';
/**
* 挂载 Naive-ui 脱离上下文的 API
@@ -8,29 +10,30 @@ import { computed } from "vue";
*/
export function setupNaiveDiscreteApi() {
+ const designStore = useDesignSetting();
const configProviderPropsRef = computed(() => ({
- // theme: NaiveUI.darkTheme,
- // themeOverrides: {
- // common: {
- // primaryColor: designStore.appTheme,
- // primaryColorHover: designStore.appTheme,
- // primaryColorPressed: designStore.appTheme,
- // },
- // LoadingBar: {
- // colorLoading: designStore.appTheme,
- // },
- // },
+ theme: designStore.darkTheme ? NaiveUI.darkTheme : undefined,
+ themeOverrides: {
+ common: {
+ primaryColor: designStore.appTheme,
+ primaryColorHover: lighten(designStore.appTheme, 6),
+ primaryColorPressed: lighten(designStore.appTheme, 6),
+ },
+ LoadingBar: {
+ colorLoading: designStore.appTheme,
+ },
+ },
}));
const { message, dialog, notification, loadingBar } =
NaiveUI.createDiscreteApi(
- ["message", "dialog", "notification", "loadingBar"],
+ ['message', 'dialog', 'notification', 'loadingBar'],
{
configProviderProps: configProviderPropsRef,
}
);
- (window as any)["$message"] = message;
- (window as any)["$dialog"] = dialog;
- (window as any)["$notification"] = notification;
- (window as any)["$loading"] = loadingBar;
+ (window as any)['$message'] = message;
+ (window as any)['$dialog'] = dialog;
+ (window as any)['$notification'] = notification;
+ (window as any)['$loading'] = loadingBar;
}
diff --git a/src/router/base.ts b/src/router/base.ts
index d65ff9e..728ac94 100644
--- a/src/router/base.ts
+++ b/src/router/base.ts
@@ -1,21 +1,21 @@
-import { RouteRecordRaw } from "vue-router";
-import type { AppRouteRecordRaw } from "@/router/types";
-import { ErrorPage404, ErrorPage403, Layout } from "@/router/constant";
-import { PageEnum } from "@/enums/pageEnum";
+import { RouteRecordRaw } from 'vue-router';
+import type { AppRouteRecordRaw } from '@/router/types';
+import { ErrorPage404, ErrorPage403, Layout } from '@/router/constant';
+import { PageEnum } from '@/enums/pageEnum';
// import { GoReload } from "@/components/GoReload";
export const LoginRoute: RouteRecordRaw = {
path: PageEnum.BASE_LOGIN,
name: PageEnum.BASE_LOGIN_NAME,
- component: () => import("@/views/login/index.vue"),
+ component: () => import('@/views/login/index.vue'),
meta: {
- title: "登录",
+ title: '登录',
},
};
export const HttpErrorPage: RouteRecordRaw[] = [
{
- path: "/error/404",
+ path: '/error/404',
name: PageEnum.ERROR_PAGE_NAME_404,
component: ErrorPage404,
meta: {
@@ -23,7 +23,7 @@ export const HttpErrorPage: RouteRecordRaw[] = [
},
},
{
- path: "/error/403",
+ path: '/error/403',
name: PageEnum.ERROR_PAGE_NAME_403,
component: ErrorPage403,
meta: {
@@ -41,20 +41,20 @@ export const HttpErrorPage: RouteRecordRaw[] = [
];
export const ErrorPageRoute: RouteRecordRaw = {
- path: "/:path(.*)*",
- name: "ErrorPage",
+ path: '/:path(.*)*',
+ name: 'ErrorPage',
component: Layout,
meta: {
- title: "ErrorPage",
+ title: 'ErrorPage',
// hideBreadcrumb: true,
},
children: [
{
- path: "/:path(.*)*",
- name: "ErrorPageSon",
+ path: '/:path(.*)*',
+ name: 'ErrorPageSon',
component: ErrorPage404,
meta: {
- title: "ErrorPage",
+ title: 'ErrorPage',
// hideBreadcrumb: true,
},
},
@@ -80,9 +80,9 @@ export const RedirectRoute: AppRouteRecordRaw = {
},
children: [
{
- path: "/redirect/:path(.*)",
+ path: '/redirect/:path(.*)',
name: PageEnum.REDIRECT_NAME,
- component: () => import("@/views/redirect/index.vue"),
+ component: () => import('@/views/redirect/index.vue'),
meta: {
title: PageEnum.REDIRECT_NAME,
hideBreadcrumb: true,
diff --git a/src/router/constant.ts b/src/router/constant.ts
index f5a93a5..2738937 100644
--- a/src/router/constant.ts
+++ b/src/router/constant.ts
@@ -1,7 +1,9 @@
-export const ErrorPage404 = () => import("@/views/exception/404.vue");
+export const RedirectName = 'Redirect';
-export const ErrorPage403 = () => import("@/views/exception/403.vue");
+export const ErrorPage404 = () => import('@/views/exception/404.vue');
-export const Layout = () => import("@/layout/index.vue");
+export const ErrorPage403 = () => import('@/views/exception/403.vue');
-export const ParentLayout = () => import("@/layout/parentLayout.vue");
+export const Layout = () => import('@/layout/index.vue');
+
+export const ParentLayout = () => import('@/layout/parentLayout.vue');
diff --git a/src/router/generator.ts b/src/router/generator.ts
index 0502daa..08c9ea5 100644
--- a/src/router/generator.ts
+++ b/src/router/generator.ts
@@ -1,16 +1,16 @@
-import { getMenuTree } from "@/api/system/menu";
-import { constantRouterIcon } from "./icons";
-import { RouteRecordRaw } from "vue-router";
-import { Layout, ParentLayout } from "@/router/constant";
+import { getMenuTree } from '@/api/system/menu';
+import { constantRouterIcon } from './icons';
+import { RouteRecordRaw } from 'vue-router';
+import { Layout, ParentLayout } from '@/router/constant';
// import { storage } from "@/utils";
// import { StorageEnum } from "@/enums/storageEnum";
-import type { AppRouteRecordRaw } from "@/router/types";
+import type { AppRouteRecordRaw } from '@/router/types';
-const Iframe = () => import("@/views/iframe/index.vue");
-const LayoutMap = new Map
Promise>();
+const Iframe = () => import('@/views/iframe/index.vue');
+const LayoutMap = new Map Promise>();
-LayoutMap.set("Layout", Layout);
-LayoutMap.set("iframe", Iframe);
+LayoutMap.set('Layout', Layout);
+LayoutMap.set('iframe', Iframe);
/**
* 格式化 后端 结构信息并递归生成层级路由表
@@ -22,10 +22,10 @@ export const generateRoutes = (routerMap, parent?): any[] => {
return routerMap.map((item) => {
const currentRoute: any = {
// 路由地址 动态拼接生成如 /dashboard/workplace
- path: `${(parent && parent.path) ?? ""}/${item.path}`,
+ path: `${(parent && parent.path) ?? ''}/${item.path}`,
// 路由名称,建议唯一
- name: item.name ?? "",
- key: item.name ?? "",
+ name: item.name ?? '',
+ key: item.name ?? '',
// 该路由对应页面的 组件
component: item.component,
// meta: 页面标题, 菜单图标, 页面权限(供指令权限用,可去掉)
@@ -38,7 +38,7 @@ export const generateRoutes = (routerMap, parent?): any[] => {
};
// 为了防止出现后端返回结果不规范,处理有可能出现拼接出两个 反斜杠
- currentRoute.path = currentRoute.path.replace("//", "/");
+ currentRoute.path = currentRoute.path.replace('//', '/');
// 重定向
item.redirect && (currentRoute.redirect = item.redirect);
// 是否有子菜单,并递归处理
@@ -47,7 +47,7 @@ export const generateRoutes = (routerMap, parent?): any[] => {
!item.redirect &&
(currentRoute.redirect = `${item.path}/${item.children[0].path}`);
// Recursion
- console.log(currentRoute.redirect, "currentRoute.redirect");
+ console.log(currentRoute.redirect, 'currentRoute.redirect');
currentRoute.children = generateRoutes(item.children, currentRoute);
}
@@ -73,12 +73,12 @@ let viewsModules: Record Promise>;
export const asyncImportRoute = (
routes: AppRouteRecordRaw[] | undefined
): void => {
- viewsModules = viewsModules || import.meta.glob("../views/**/*.{vue,tsx}");
+ viewsModules = viewsModules || import.meta.glob('../views/**/*.{vue,tsx}');
if (!routes) return;
routes.forEach((item) => {
if (!item.component && item.meta?.frameSrc) {
- item.component = "iframe";
+ item.component = 'iframe';
}
const { component, name } = item;
@@ -109,9 +109,9 @@ export const dynamicImport = (
) => {
const keys = Object.keys(viewsModules);
const matchKeys = keys.filter((key) => {
- let k = key.replace("../views", "");
+ let k = key.replace('../views', '');
- const lastIndex = k.lastIndexOf(".");
+ const lastIndex = k.lastIndexOf('.');
k = k.substring(0, lastIndex);
return k === component;
});
@@ -121,7 +121,7 @@ export const dynamicImport = (
}
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"
+ '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;
}
diff --git a/src/router/icons.ts b/src/router/icons.ts
index 60dfafd..a7369ee 100644
--- a/src/router/icons.ts
+++ b/src/router/icons.ts
@@ -1,5 +1,5 @@
-import { renderIcon } from "@/utils/index";
-import { DashboardOutlined } from "@vicons/antd";
+import { renderIcon } from '@/utils/index';
+import { DashboardOutlined } from '@vicons/antd';
//前端路由图标映射表
export const constantRouterIcon = {
diff --git a/src/router/index.ts b/src/router/index.ts
index 577b15c..07bb109 100644
--- a/src/router/index.ts
+++ b/src/router/index.ts
@@ -1,7 +1,7 @@
-import type { App } from "vue";
-import { createRouter, createWebHashHistory, RouteRecordRaw } from "vue-router";
-import { createRouterGuards } from "./router-guards";
-import { LoginRoute, RedirectRoute } from "@/router/base";
+import type { App } from 'vue';
+import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router';
+import { createRouterGuards } from './router-guards';
+import { LoginRoute, RedirectRoute } from '@/router/base';
// import type { IModuleType } from "./types";
// const modules = import.meta.glob("./modules/**/*router.ts", {
@@ -23,14 +23,14 @@ import { LoginRoute, RedirectRoute } from "@/router/base";
// routeModuleList.sort(sortRoute);
export const RootRoute: RouteRecordRaw = {
- path: "/",
- name: "Root",
- redirect: "/system/menu",
+ path: '/',
+ name: 'Root',
+ redirect: '/system/menu',
// async beforeEnter(to, from, next) {
// next(await reqRedirectUrl());
// },
meta: {
- title: "Root",
+ title: 'Root',
},
};
diff --git a/src/router/router-guards.ts b/src/router/router-guards.ts
index 4c91779..90c7160 100644
--- a/src/router/router-guards.ts
+++ b/src/router/router-guards.ts
@@ -1,10 +1,11 @@
-import { Router, isNavigationFailure } from "vue-router";
-import type { RouteRecordRaw } from "vue-router";
-import { loginCheck } from "@/utils/router";
-import { useUser } from "@/store/modules/user";
-import { useAsyncRoute } from "@/store/modules/asyncRoute";
-import { PageEnum } from "@/enums/pageEnum";
-import { ErrorPageRoute } from "@/router/base";
+import { Router, isNavigationFailure } from 'vue-router';
+import type { RouteRecordRaw } from 'vue-router';
+import { loginCheck } from '@/utils/router';
+import { useUser } from '@/store/modules/user';
+import { useAsyncRoute } from '@/store/modules/asyncRoute';
+import { PageEnum } from '@/enums/pageEnum';
+import { ErrorPageRoute } from '@/router/base';
+import { useProjectSetting } from '@/store/modules/projectSetting';
const LOGIN_PATH = PageEnum.BASE_LOGIN;
const whitePathList = [LOGIN_PATH]; // 没有重定向白名单
@@ -12,12 +13,13 @@ const whitePathList = [LOGIN_PATH]; // 没有重定向白名单
export async function createRouterGuards(router: Router) {
const userStore = useUser();
const asyncRouteStore = useAsyncRoute();
+ const useProjectSettingStore = useProjectSetting();
// 前置;
router.beforeEach(async (to, from, next) => {
- const Loading = window["$loading"] || null;
+ const Loading = window['$loading'] || null;
Loading && Loading.start();
- if (from.path === LOGIN_PATH && to.name === "errorPage") {
+ if (from.path === LOGIN_PATH && to.name === 'errorPage') {
next(PageEnum.BASE_HOME);
return;
}
@@ -61,6 +63,7 @@ export async function createRouterGuards(router: Router) {
try {
await userStore.getInfo();
+ await useProjectSettingStore.setProjectSetting();
const routes = await asyncRouteStore.generateRoutes();
// 动态添加可访问路由表
routes.forEach((item: unknown) => {
@@ -92,7 +95,7 @@ export async function createRouterGuards(router: Router) {
router.afterEach((to, _, failure) => {
document.title = (to?.meta?.title as string) || document.title;
if (isNavigationFailure(failure)) {
- console.log("导航失败", failure);
+ console.log('导航失败', failure);
}
const asyncRouteStore = useAsyncRoute();
// 在这里设置需要缓存的组件名称
@@ -107,7 +110,7 @@ export async function createRouterGuards(router: Router) {
) {
// 需要缓存的组件
keepAliveComponents.push(currentComName);
- } else if (!to.meta?.keepAlive || to.name == "Redirect") {
+ } else if (!to.meta?.keepAlive || to.name == 'Redirect') {
// 不需要缓存的组件
const index = asyncRouteStore.keepAliveComponents.findIndex(
(name) => name == currentComName
@@ -117,11 +120,11 @@ export async function createRouterGuards(router: Router) {
}
}
asyncRouteStore.setKeepAliveComponents(keepAliveComponents);
- const Loading = window["$loading"] || null;
+ const Loading = window['$loading'] || null;
Loading && Loading.finish();
});
router.onError((error) => {
- console.log(error, "路由错误");
+ console.log(error, '路由错误');
});
}
diff --git a/src/router/types.ts b/src/router/types.ts
index fd0277f..555a1d0 100644
--- a/src/router/types.ts
+++ b/src/router/types.ts
@@ -1,13 +1,13 @@
-import type { RouteRecordRaw, RouteMeta } from "vue-router";
-import { defineComponent } from "vue";
+import type { RouteRecordRaw, RouteMeta } from 'vue-router';
+import { defineComponent } from 'vue';
export type Component =
| ReturnType
- | (() => Promise)
+ | (() => Promise)
| (() => Promise);
export interface AppRouteRecordRaw
- extends Omit {
+ extends Omit {
name: string;
meta: RouteMeta;
component?: Component | string;
diff --git a/src/settings.ts b/src/settings.ts
index 2e7ca33..faed772 100644
--- a/src/settings.ts
+++ b/src/settings.ts
@@ -1,16 +1,16 @@
const defaultSettings: AppSettings = {
- title: "vue3-element-admin",
- version: "v2.7.0",
+ title: 'vue3-element-admin',
+ version: 'v2.7.0',
showSettings: true,
tagsView: true,
fixedHeader: false,
sidebarLogo: true,
- layout: "left",
- theme: "light",
- size: "small",
- language: "zh-cn",
- themeColor: "#409EFF",
- watermark: { enabled: false, content: "有来技术" },
+ layout: 'left',
+ theme: 'light',
+ size: 'small',
+ language: 'zh-cn',
+ themeColor: '#409EFF',
+ watermark: { enabled: false, content: '有来技术' },
};
export default defaultSettings;
diff --git a/src/settings/animateSetting.ts b/src/settings/animateSetting.ts
new file mode 100644
index 0000000..05dc0be
--- /dev/null
+++ b/src/settings/animateSetting.ts
@@ -0,0 +1,8 @@
+export const animates = [
+ { value: 'zoom-fade', label: '渐变' },
+ { value: 'zoom-out', label: '闪现' },
+ { value: 'fade-slide', label: '滑动' },
+ { value: 'fade', label: '消退' },
+ { value: 'fade-bottom', label: '底部消退' },
+ { value: 'fade-scale', label: '缩放消退' },
+];
diff --git a/src/settings/componentSetting.ts b/src/settings/componentSetting.ts
new file mode 100644
index 0000000..0c5150c
--- /dev/null
+++ b/src/settings/componentSetting.ts
@@ -0,0 +1,39 @@
+export default {
+ table: {
+ apiSetting: {
+ // 当前页的字段名
+ pageField: 'page',
+ // 每页数量字段名
+ sizeField: 'pageSize',
+ // 接口返回的数据字段名
+ listField: 'list',
+ // 接口返回总页数字段名
+ totalField: 'pageCount',
+ //总数字段名
+ countField: 'itemCount',
+ },
+ //默认分页数量
+ defaultPageSize: 10,
+ //可切换每页数量集合
+ pageSizes: [10, 20, 30, 40, 50],
+ },
+ upload: {
+ //考虑接口规范不同
+ apiSetting: {
+ // 集合字段名
+ infoField: 'data',
+ // 图片地址字段名
+ imgField: 'photo',
+ },
+ //最大上传图片大小
+ maxSize: 2,
+ //图片上传类型
+ fileType: [
+ 'image/png',
+ 'image/jpg',
+ 'image/jpeg',
+ 'image/gif',
+ 'image/svg+xml',
+ ],
+ },
+};
diff --git a/src/settings/designSetting.ts b/src/settings/designSetting.ts
new file mode 100644
index 0000000..d4485f3
--- /dev/null
+++ b/src/settings/designSetting.ts
@@ -0,0 +1,33 @@
+// app theme preset color
+export const appThemeList: string[] = [
+ '#6366F1',
+ '#2d8cf0',
+ '#0960bd',
+ '#0084f4',
+ '#009688',
+ '#536dfe',
+ '#ff5c93',
+ '#ee4f12',
+ '#0096c7',
+ '#9c27b0',
+ '#ff9800',
+ '#FF3D68',
+ '#00C1D4',
+ '#71EFA3',
+ '#171010',
+ '#78DEC7',
+ '#1768AC',
+ '#FB9300',
+ '#FC5404',
+];
+
+const setting = {
+ //深色主题
+ darkTheme: false,
+ //系统主题色
+ appTheme: '#2d8cf0',
+ //系统内置主题色列表
+ appThemeList,
+};
+
+export default setting;
diff --git a/src/settings/projectSetting.ts b/src/settings/projectSetting.ts
new file mode 100644
index 0000000..2d208ab
--- /dev/null
+++ b/src/settings/projectSetting.ts
@@ -0,0 +1,57 @@
+const setting = {
+ //导航模式 vertical 左侧菜单模式 horizontal 顶部菜单模式
+ navMode: 'vertical',
+ //导航风格 dark 暗色侧边栏 light 白色侧边栏 header-dark 暗色顶栏
+ navTheme: 'light',
+ // 是否处于移动端模式
+ isMobile: false,
+ //顶部
+ headerSetting: {
+ //背景色
+ bgColor: '#fff',
+ //固定顶部
+ fixed: true,
+ //显示重载按钮
+ isReload: true,
+ },
+ //页脚
+ showFooter: true,
+ //多标签
+ multiTabsSetting: {
+ //背景色
+ bgColor: '#fff',
+ //是否显示
+ show: true,
+ //固定多标签
+ fixed: true,
+ },
+ //菜单
+ menuSetting: {
+ //最小宽度
+ minMenuWidth: 64,
+ //菜单宽度
+ menuWidth: 200,
+ //固定菜单
+ fixed: true,
+ //分割菜单
+ mixMenu: false,
+ //触发移动端侧边栏的宽度
+ mobileWidth: 800,
+ // 折叠菜单
+ collapsed: false,
+ },
+ //面包屑
+ crumbsSetting: {
+ //是否显示
+ show: true,
+ //显示图标
+ showIcon: false,
+ },
+ //菜单权限模式 FIXED 前端固定路由 BACK 动态获取
+ // permissionMode: "FIXED",
+ //是否开启路由动画
+ isPageAnimate: true,
+ //路由动画类型
+ pageAnimateType: 'zoom-fade',
+};
+export default setting;
diff --git a/src/store/index.ts b/src/store/index.ts
index e22b67c..ceb4219 100644
--- a/src/store/index.ts
+++ b/src/store/index.ts
@@ -1,5 +1,5 @@
-import type { App } from "vue";
-import { createPinia } from "pinia";
+import type { App } from 'vue';
+import { createPinia } from 'pinia';
const store = createPinia();
diff --git a/src/store/modules/app.ts b/src/store/modules/app.ts
index 5b7edc4..b56227a 100644
--- a/src/store/modules/app.ts
+++ b/src/store/modules/app.ts
@@ -1,42 +1,42 @@
-import { defineStore } from "pinia";
-import { useStorage } from "@vueuse/core";
-import defaultSettings from "@/settings";
+import { defineStore } from 'pinia';
+import { useStorage } from '@vueuse/core';
+import defaultSettings from '@/settings';
// setup
-export const useAppStore = defineStore("app", () => {
+export const useAppStore = defineStore('app', () => {
// state
- const device = useStorage("device", "desktop");
- const size = useStorage("size", defaultSettings.size);
+ const device = useStorage('device', 'desktop');
+ const size = useStorage('size', defaultSettings.size);
- const sidebarStatus = useStorage("sidebarStatus", "closed");
+ const sidebarStatus = useStorage('sidebarStatus', 'closed');
const sidebar = reactive({
- opened: sidebarStatus.value !== "closed",
+ opened: sidebarStatus.value !== 'closed',
withoutAnimation: false,
});
- const activeTopMenu = useStorage("activeTop", "");
+ const activeTopMenu = useStorage('activeTop', '');
// actions
function toggleSidebar() {
sidebar.opened = !sidebar.opened;
sidebar.withoutAnimation = false;
if (sidebar.opened) {
- sidebarStatus.value = "opened";
+ sidebarStatus.value = 'opened';
} else {
- sidebarStatus.value = "closed";
+ sidebarStatus.value = 'closed';
}
}
function closeSideBar(withoutAnimation: boolean) {
sidebar.opened = false;
sidebar.withoutAnimation = withoutAnimation;
- sidebarStatus.value = "closed";
+ sidebarStatus.value = 'closed';
}
function openSideBar(withoutAnimation: boolean) {
sidebar.opened = true;
sidebar.withoutAnimation = withoutAnimation;
- sidebarStatus.value = "opened";
+ sidebarStatus.value = 'opened';
}
function toggleDevice(val: string) {
diff --git a/src/store/modules/asyncRoute.ts b/src/store/modules/asyncRoute.ts
index b9d1740..5c51bc5 100644
--- a/src/store/modules/asyncRoute.ts
+++ b/src/store/modules/asyncRoute.ts
@@ -1,8 +1,8 @@
-import { defineStore } from "pinia";
-import { RouteRecordRaw } from "vue-router";
-import { store } from "@/store";
-import { constantRouter } from "@/router/index";
-import { generateDynamicRoutes } from "@/router/generator";
+import { defineStore } from 'pinia';
+import { RouteRecordRaw } from 'vue-router';
+import { store } from '@/store';
+import { constantRouter } from '@/router/index';
+import { generateDynamicRoutes } from '@/router/generator';
export interface IAsyncRouteState {
menus: RouteRecordRaw[];
@@ -13,7 +13,7 @@ export interface IAsyncRouteState {
}
export const useAsyncRouteStore = defineStore({
- id: "app-async-route",
+ id: 'app-async-route',
state: (): IAsyncRouteState => ({
menus: [],
routers: constantRouter,
diff --git a/src/store/modules/designSetting.ts b/src/store/modules/designSetting.ts
new file mode 100644
index 0000000..14ad7fe
--- /dev/null
+++ b/src/store/modules/designSetting.ts
@@ -0,0 +1,44 @@
+import { defineStore } from 'pinia';
+import { store } from '@/store';
+import designSetting from '@/settings/designSetting';
+
+const { darkTheme, appTheme, appThemeList } = designSetting;
+
+interface DesignSettingState {
+ //深色主题
+ darkTheme: boolean;
+ //系统风格
+ appTheme: string;
+ //系统内置风格
+ appThemeList: string[];
+}
+
+export const useDesignSettingStore = defineStore({
+ id: 'app-design-setting',
+ state: (): DesignSettingState => ({
+ darkTheme,
+ appTheme,
+ appThemeList,
+ }),
+ getters: {
+ getDarkTheme(): boolean {
+ return this.darkTheme;
+ },
+ getAppTheme(): string {
+ return this.appTheme;
+ },
+ getAppThemeList(): string[] {
+ return this.appThemeList;
+ },
+ },
+ actions: {
+ setAppTheme(color: string) {
+ this.appTheme = color;
+ },
+ },
+});
+
+// Need to be used outside the setup
+export function useDesignSetting() {
+ return useDesignSettingStore(store);
+}
diff --git a/src/store/modules/projectSetting.ts b/src/store/modules/projectSetting.ts
new file mode 100644
index 0000000..a67336b
--- /dev/null
+++ b/src/store/modules/projectSetting.ts
@@ -0,0 +1,116 @@
+import { store } from '@/store';
+import { defineStore } from 'pinia';
+import projectSetting from '@/settings/projectSetting';
+import type {
+ IHeaderSetting,
+ IMenuSetting,
+ IMultiTabsSetting,
+ ICrumbsSetting,
+} from '/#/config';
+import { getUserStyleSetting } from '@/api/system/user';
+import { useDesignSetting } from './designSetting';
+const {
+ navMode,
+ navTheme,
+ isMobile,
+ headerSetting,
+ showFooter,
+ menuSetting,
+ multiTabsSetting,
+ crumbsSetting,
+ // permissionMode,
+ isPageAnimate,
+ pageAnimateType,
+} = projectSetting;
+
+interface ProjectSettingState {
+ navMode: string; //导航模式
+ navTheme: string; //导航风格
+ headerSetting: IHeaderSetting; //顶部设置
+ showFooter: boolean; //页脚
+ menuSetting: IMenuSetting; //多标签
+ multiTabsSetting: IMultiTabsSetting; //多标签
+ crumbsSetting: ICrumbsSetting; //面包屑
+ // permissionMode: string; //权限模式
+ isPageAnimate: boolean; //是否开启路由动画
+ pageAnimateType: string; //路由动画类型
+ isMobile: boolean; // 是否处于移动端模式
+}
+
+export const useProjectSettingStore = defineStore({
+ id: 'app-project-setting',
+ state: (): ProjectSettingState => ({
+ navMode: navMode,
+ navTheme,
+ isMobile,
+ headerSetting,
+ showFooter,
+ menuSetting,
+ multiTabsSetting,
+ crumbsSetting,
+ // permissionMode,
+ isPageAnimate,
+ pageAnimateType,
+ }),
+ getters: {
+ getNavMode(): string {
+ return this.navMode;
+ },
+ getNavTheme(): string {
+ return this.navTheme;
+ },
+ getIsMobile(): boolean {
+ return this.isMobile;
+ },
+ getHeaderSetting(): object {
+ return this.headerSetting;
+ },
+ getShowFooter(): boolean {
+ return this.showFooter;
+ },
+ getMenuSetting(): object {
+ return this.menuSetting;
+ },
+ getMultiTabsSetting(): object {
+ return this.multiTabsSetting;
+ },
+ getCrumbsSetting(): object {
+ return this.crumbsSetting;
+ },
+ // getPermissionMode(): string {
+ // return this.permissionMode;
+ // },
+ getIsPageAnimate(): boolean {
+ return this.isPageAnimate;
+ },
+ getPageAnimateType(): string {
+ return this.pageAnimateType;
+ },
+ },
+ actions: {
+ async setProjectSetting() {
+ const useDesignSettingStore = useDesignSetting();
+ const { data } = await getUserStyleSetting();
+ this.navMode = data.navMode;
+ this.navTheme = data.navTheme;
+ this.isMobile = data.isMobile;
+ this.headerSetting = data.headerSetting;
+ this.showFooter = data.showFooter;
+ this.menuSetting = data.menuSetting;
+ this.multiTabsSetting = data.multiTabsSetting;
+ this.crumbsSetting = data.crumbsSetting;
+ this.isPageAnimate = data.isPageAnimate;
+ this.pageAnimateType = data.pageAnimateType;
+
+ useDesignSettingStore.setAppTheme(data.appTheme);
+ },
+
+ setIsMobile(value: boolean): void {
+ this.isMobile = value;
+ },
+ },
+});
+
+export function useProjectSetting() {
+ return useProjectSettingStore(store);
+}
diff --git a/src/store/modules/screenLock.ts b/src/store/modules/screenLock.ts
new file mode 100644
index 0000000..06aa9ca
--- /dev/null
+++ b/src/store/modules/screenLock.ts
@@ -0,0 +1,31 @@
+import { defineStore } from 'pinia';
+import { StorageEnum } from '@/enums/storageEnum';
+import { storage } from '@/utils';
+
+// 长时间不操作默认锁屏时间
+const initTime = 60 * 60;
+
+const isLocked = storage.get(StorageEnum.IS_SCREENLOCKED, false);
+
+export type IScreenLockState = {
+ isLocked: boolean; // 是否锁屏
+ lockTime: number;
+};
+
+export const useScreenLockStore = defineStore({
+ id: 'app-screen-lock',
+ state: (): IScreenLockState => ({
+ isLocked: isLocked === true, // 是否锁屏
+ lockTime: isLocked == 'true' ? initTime : 0,
+ }),
+ getters: {},
+ actions: {
+ setLock(payload: boolean) {
+ this.isLocked = payload;
+ storage.set(StorageEnum.IS_SCREENLOCKED, this.isLocked);
+ },
+ setLockTime(payload = initTime) {
+ this.lockTime = payload;
+ },
+ },
+});
diff --git a/src/store/modules/settings.ts b/src/store/modules/settings.ts
deleted file mode 100644
index 5db3d7f..0000000
--- a/src/store/modules/settings.ts
+++ /dev/null
@@ -1,62 +0,0 @@
-import { defineStore } from "pinia";
-import defaultSettings from "@/settings";
-
-export const useSettingsStore = defineStore("setting", () => {
- const title = defaultSettings.title;
- const version = defaultSettings.version;
-
- const tagsView = useStorage("tagsView", defaultSettings.tagsView);
-
- const showSettings = ref(defaultSettings.showSettings);
- const sidebarLogo = ref(defaultSettings.sidebarLogo);
- const fixedHeader = useStorage(
- "fixedHeader",
- defaultSettings.fixedHeader
- );
- const layout = useStorage("layout", defaultSettings.layout);
- const themeColor = useStorage(
- "themeColor",
- defaultSettings.themeColor
- );
- const theme = useStorage("theme", defaultSettings.theme);
-
- // Whether to enable watermark
- const watermark = useStorage("watermark", defaultSettings.watermark);
-
- const settingsMap: Record> = {
- showSettings,
- fixedHeader,
- tagsView,
- sidebarLogo,
- layout,
- themeColor,
- theme,
- watermark: watermark.value,
- };
-
- function changeSetting({ key, value }: { key: string; value: any }) {
- const setting = settingsMap[key];
- if (setting !== undefined) {
- setting.value = value;
- if (key === "theme" && value === "dark") {
- document.documentElement.classList.add("dark");
- } else {
- document.documentElement.classList.remove("dark");
- }
- }
- }
-
- return {
- title,
- version,
- showSettings,
- tagsView,
- fixedHeader,
- sidebarLogo,
- layout,
- themeColor,
- changeSetting,
- theme,
- watermark,
- };
-});
diff --git a/src/store/modules/tabsView.ts b/src/store/modules/tabsView.ts
new file mode 100644
index 0000000..0ee6950
--- /dev/null
+++ b/src/store/modules/tabsView.ts
@@ -0,0 +1,85 @@
+import { defineStore } from 'pinia';
+import { RouteLocationNormalized } from 'vue-router';
+
+// 不需要出现在标签页中的路由
+const whiteList = ['Redirect', 'login'];
+
+export type RouteItem = Partial & {
+ fullPath: string;
+ path: string;
+ name: string;
+ hash: string;
+ meta: object;
+ params: object;
+ query: object;
+};
+
+export type ITabsViewState = {
+ tabsList: RouteItem[]; // 标签页
+};
+
+//保留固定路由
+function retainAffixRoute(list: any[]) {
+ return list.filter((item) => item?.meta?.affix ?? false);
+}
+
+export const useTabsViewStore = defineStore({
+ id: 'app-tabs-view',
+ state: (): ITabsViewState => ({
+ tabsList: [],
+ }),
+ getters: {},
+ actions: {
+ initTabs(routes: RouteItem[]) {
+ // 初始化标签页
+ this.tabsList = routes;
+ },
+ addTab(route: RouteItem): boolean {
+ // 添加标签页
+ if (whiteList.includes(route.name)) return false;
+ const isExists = this.tabsList.some(
+ (item) => item.fullPath == route.fullPath
+ );
+ if (!isExists) {
+ this.tabsList.push(route);
+ }
+ return true;
+ },
+ closeLeftTabs(route: RouteItem) {
+ // 关闭左侧
+ const index = this.tabsList.findIndex(
+ (item) => item.fullPath == route.fullPath
+ );
+ this.tabsList = this.tabsList.filter(
+ (item, i) => i >= index || (item?.meta?.affix ?? false)
+ );
+ },
+ closeRightTabs(route: RouteItem) {
+ // 关闭右侧
+ const index = this.tabsList.findIndex(
+ (item) => item.fullPath == route.fullPath
+ );
+ this.tabsList = this.tabsList.filter(
+ (item, i) => i <= index || (item?.meta?.affix ?? false)
+ );
+ },
+ closeOtherTabs(route: RouteItem) {
+ // 关闭其他
+ this.tabsList = this.tabsList.filter(
+ (item) =>
+ item.fullPath == route.fullPath || (item?.meta?.affix ?? false)
+ );
+ },
+ closeCurrentTab(route: RouteItem) {
+ // 关闭当前页
+ const index = this.tabsList.findIndex(
+ (item) => item.fullPath == route.fullPath
+ );
+ this.tabsList.splice(index, 1);
+ },
+ closeAllTabs() {
+ // 关闭全部
+ this.tabsList = retainAffixRoute(this.tabsList);
+ },
+ },
+});
diff --git a/src/store/modules/user.ts b/src/store/modules/user.ts
index 2a97cfc..421fc3c 100644
--- a/src/store/modules/user.ts
+++ b/src/store/modules/user.ts
@@ -1,10 +1,10 @@
-import { defineStore } from "pinia";
-import { store } from "@/store";
-import { StorageEnum } from "@/enums/storageEnum";
-import { ResultEnum } from "@/enums/httpEnum";
-import { storage, routerTurnByName } from "@/utils";
-import { login, getUserInfo } from "@/api/system/user";
-import { PageEnum } from "@/enums/pageEnum";
+import { defineStore } from 'pinia';
+import { store } from '@/store';
+import { StorageEnum } from '@/enums/storageEnum';
+import { ResultEnum } from '@/enums/httpEnum';
+import { storage, routerTurnByName } from '@/utils';
+import { login, getUserInfo } from '@/api/system/user';
+import { PageEnum } from '@/enums/pageEnum';
export type UserInfoType = {
// TODO: add your own data
name: string;
@@ -21,12 +21,12 @@ export interface IUserState {
}
export const useUserStore = defineStore({
- id: "app-user",
+ id: 'app-user',
state: (): IUserState => ({
- token: storage.get(StorageEnum.ZS_ACCESS_TOKEN, ""),
- username: "",
- welcome: "",
- avatar: "",
+ token: storage.get(StorageEnum.ZS_ACCESS_TOKEN, ''),
+ username: '',
+ welcome: '',
+ avatar: '',
permissions: [],
info: storage.get(StorageEnum.ZS_CURRENT_USER, {}),
}),
@@ -76,7 +76,7 @@ export const useUserStore = defineStore({
// 获取用户信息
async getInfo() {
const { data } = await getUserInfo();
- console.log(data, "用户信息");
+ console.log(data, '用户信息');
// if (result.permissions && result.permissions.length) {
// const permissionsList = result.permissions;
@@ -92,7 +92,7 @@ export const useUserStore = defineStore({
// 登出
async logout() {
this.setPermissions([]);
- this.setUserInfo({ name: "", email: "" });
+ this.setUserInfo({ name: '', email: '' });
storage.remove(StorageEnum.ZS_ACCESS_TOKEN);
storage.remove(StorageEnum.ZS_CURRENT_USER);
routerTurnByName(PageEnum.BASE_LOGIN_NAME);
diff --git a/src/styles/index.scss b/src/styles/index.scss
index 94c86e0..a70ab42 100644
--- a/src/styles/index.scss
+++ b/src/styles/index.scss
@@ -1,48 +1 @@
-@import "./transition";
-@import "./reset";
-
-.app-container {
- width: 100%;
- height: calc(100vh - 70px);
- background: #fff;
- border-radius: 5px;
-
- &-title {
- position: relative;
- margin-left: 34px;
- font-family: SourceHanSansCN, sans-serif;
- font-size: 20px;
- font-weight: 600;
-
- &::before {
- position: absolute;
- top: 5px;
- left: -30px;
- display: block;
- width: 10px;
- height: 22px;
- content: "";
- background: linear-gradient(90deg,
- #3db8ff 0%,
- rgb(53 138 225 / 77%) 52%,
- #3db8ff 100%);
- }
- }
-}
-
-/* 设置滚动条的样式 */
-::-webkit-scrollbar {
- width: 8px;
-}
-
-/* 滚动槽 */
-::-webkit-scrollbar-track {
- border-radius: 2px;
- box-shadow: inset 0 0 6px rgb(0 0 0 / 0%);
-}
-
-/* 滚动条滑块 */
-::-webkit-scrollbar-thumb {
- background: rgb(123 157 213 / 50%);
- border-radius: 4px;
-}
\ No newline at end of file
+@import 'transition/index';
\ No newline at end of file
diff --git a/src/styles/reset.scss b/src/styles/reset.scss
deleted file mode 100644
index fa0142c..0000000
--- a/src/styles/reset.scss
+++ /dev/null
@@ -1,75 +0,0 @@
-*,
-::before,
-::after {
- box-sizing: border-box;
- border-color: currentcolor;
- border-style: solid;
- border-width: 0;
-}
-
-#app {
- width: 100%;
- height: 100%;
-}
-
-html {
- box-sizing: border-box;
- width: 100%;
- height: 100%;
- line-height: 1.5;
- tab-size: 4;
- text-size-adjust: 100%;
-}
-
-body {
- width: 100%;
- height: 100%;
- margin: 0;
- font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB",
- "Microsoft YaHei", "微软雅黑", YouSheBiaoTiHei, Arial, sans-serif;
- line-height: inherit;
- -moz-osx-font-smoothing: grayscale;
- -webkit-font-smoothing: antialiased;
- text-rendering: optimizelegibility;
-}
-
-a {
- color: inherit;
- text-decoration: inherit;
-}
-
-img,
-svg {
- display: inline-block;
-}
-
-svg {
- vertical-align: -0.15em; //因icon大小被设置为和字体大小一致,而span等标签的下边缘会和字体的基线对齐,故需设置一个往下的偏移比例,来纠正视觉上的未对齐效果
-}
-
-ul,
-li {
- padding: 0;
- margin: 0;
- list-style: none;
-}
-
-*,
-*::before,
-*::after {
- box-sizing: inherit;
-}
-
-a,
-a:focus,
-a:hover {
- color: inherit;
- text-decoration: none;
- cursor: pointer;
-}
-
-a:focus,
-a:active,
-div:focus {
- outline: none;
-}
diff --git a/src/styles/tailwind.css b/src/styles/tailwind.css
new file mode 100644
index 0000000..bd6213e
--- /dev/null
+++ b/src/styles/tailwind.css
@@ -0,0 +1,3 @@
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
\ No newline at end of file
diff --git a/src/styles/transition.scss b/src/styles/transition.scss
deleted file mode 100644
index 7ba5363..0000000
--- a/src/styles/transition.scss
+++ /dev/null
@@ -1,85 +0,0 @@
-// global transition css
-
-/* fade */
-.fade-enter-active,
-.fade-leave-active {
- transition: opacity 0.28s;
-}
-
-.fade-enter,
-.fade-leave-active {
- opacity: 0;
-}
-
-/* fade-transform */
-.fade-transform-leave-active,
-.fade-transform-enter-active {
- transition: all 0.5s;
-}
-
-.fade-transform-enter {
- opacity: 0;
- transform: translateX(-30px);
-}
-
-.fade-transform-leave-to {
- opacity: 0;
- transform: translateX(30px);
-}
-
-/* breadcrumb transition */
-.breadcrumb-enter-active,
-.breadcrumb-leave-active {
- transition: all 0.5s;
-}
-
-.breadcrumb-enter,
-.breadcrumb-leave-active {
- opacity: 0;
- transform: translateX(20px);
-}
-
-.breadcrumb-move {
- transition: all 0.5s;
-}
-
-.breadcrumb-leave-active {
- position: absolute;
-}
-
-/* 缩放过渡 */
-.fade-scale-enter-active,
-.fade-scale-leave-active {
- transition: transform 0.3s ease-in-out;
-}
-
-.fade-scale-enter-from,
-.fade-scale-leave-to {
- transform: scale(0);
-}
-
-.fade-slide-leave-active,
-.fade-slide-enter-active {
- transition: opacity 0.3s, transform 0.3s;
-}
-
-.fade-slide-enter-from {
- opacity: 0;
- transform: translateX(-30px);
-}
-
-.fade-slide-leave-to {
- opacity: 0;
- transform: translateX(30px);
-}
-
-/* 旋转过渡 */
-.fade-rotate-enter-active,
-.fade-rotate-leave-active {
- transition: transform 0.3s ease-in-out;
-}
-
-.fade-rotate-enter-from,
-.fade-rotate-leave-to {
- transform: rotate(90deg);
-}
diff --git a/src/styles/transition/base.scss b/src/styles/transition/base.scss
new file mode 100644
index 0000000..5837d76
--- /dev/null
+++ b/src/styles/transition/base.scss
@@ -0,0 +1,18 @@
+@mixin transition-default {
+ &-enter-active,
+ &-leave-active {
+ transition: 0.3s cubic-bezier(0.25, 0.8, 0.5, 1) !important;
+ }
+
+ &-move {
+ transition: transform 0.4s;
+ }
+}
+
+.expand-transition {
+ @include transition-default;
+}
+
+.expand-x-transition {
+ @include transition-default;
+}
\ No newline at end of file
diff --git a/src/styles/transition/fade.scss b/src/styles/transition/fade.scss
new file mode 100644
index 0000000..f9ed05a
--- /dev/null
+++ b/src/styles/transition/fade.scss
@@ -0,0 +1,81 @@
+.fade-enter-active,
+.fade-leave-active {
+ transition: opacity 0.2s ease-in-out;
+}
+
+.fade-enter-from,
+.fade-leave-to {
+ opacity: 0;
+}
+
+/* fade-slide */
+.fade-slide-leave-active,
+.fade-slide-enter-active {
+ transition: all 0.3s;
+}
+
+.fade-slide-enter-from {
+ opacity: 0;
+ transform: translateX(-30px);
+}
+
+.fade-slide-leave-to {
+ opacity: 0;
+ transform: translateX(30px);
+}
+
+// ///////////////////////////////////////////////
+// Fade Bottom
+// ///////////////////////////////////////////////
+
+// Speed: 1x
+.fade-bottom-enter-active,
+.fade-bottom-leave-active {
+ transition: opacity 0.25s, transform 0.3s;
+}
+
+.fade-bottom-enter-from {
+ opacity: 0;
+ transform: translateY(-10%);
+}
+
+.fade-bottom-leave-to {
+ opacity: 0;
+ transform: translateY(10%);
+}
+
+// fade-scale
+.fade-scale-leave-active,
+.fade-scale-enter-active {
+ transition: all 0.28s;
+}
+
+.fade-scale-enter-from {
+ opacity: 0;
+ transform: scale(1.2);
+}
+
+.fade-scale-leave-to {
+ opacity: 0;
+ transform: scale(0.8);
+}
+
+// ///////////////////////////////////////////////
+// Fade Top
+// ///////////////////////////////////////////////
+
+// Speed: 1x
+.fade-top-enter-active,
+.fade-top-leave-active {
+ transition: opacity 0.2s, transform 0.25s;
+}
+
+.fade-top-enter-from {
+ opacity: 0;
+ transform: translateY(8%);
+}
+
+.fade-top-leave-to {
+ opacity: 0;
+ transform: translateY(-8%);
+}
\ No newline at end of file
diff --git a/src/styles/transition/index.scss b/src/styles/transition/index.scss
new file mode 100644
index 0000000..a97eb47
--- /dev/null
+++ b/src/styles/transition/index.scss
@@ -0,0 +1,10 @@
+@import './base';
+@import './fade';
+@import './scale';
+@import './slide';
+@import './scroll';
+@import './zoom';
+
+.collapse-transition {
+ transition: 0.2s height ease-in-out, 0.2s padding-top ease-in-out, 0.2s padding-bottom ease-in-out;
+}
\ No newline at end of file
diff --git a/src/styles/transition/scale.scss b/src/styles/transition/scale.scss
new file mode 100644
index 0000000..db18095
--- /dev/null
+++ b/src/styles/transition/scale.scss
@@ -0,0 +1,21 @@
+.scale-transition {
+ @include transition-default;
+
+ &-enter-from,
+ &-leave,
+ &-leave-to {
+ opacity: 0;
+ transform: scale(0);
+ }
+}
+
+.scale-rotate-transition {
+ @include transition-default;
+
+ &-enter-from,
+ &-leave,
+ &-leave-to {
+ opacity: 0;
+ transform: scale(0) rotate(-45deg);
+ }
+}
\ No newline at end of file
diff --git a/src/styles/transition/scroll.scss b/src/styles/transition/scroll.scss
new file mode 100644
index 0000000..c04da26
--- /dev/null
+++ b/src/styles/transition/scroll.scss
@@ -0,0 +1,67 @@
+.scroll-y-transition {
+ @include transition-default;
+
+ &-enter-from,
+ &-leave-to {
+ opacity: 0;
+ }
+
+ &-enter-from {
+ transform: translateY(-15px);
+ }
+
+ &-leave-to {
+ transform: translateY(15px);
+ }
+}
+
+.scroll-y-reverse-transition {
+ @include transition-default;
+
+ &-enter-from,
+ &-leave-to {
+ opacity: 0;
+ }
+
+ &-enter-from {
+ transform: translateY(15px);
+ }
+
+ &-leave-to {
+ transform: translateY(-15px);
+ }
+}
+
+.scroll-x-transition {
+ @include transition-default;
+
+ &-enter-from,
+ &-leave-to {
+ opacity: 0;
+ }
+
+ &-enter-from {
+ transform: translateX(-15px);
+ }
+
+ &-leave-to {
+ transform: translateX(15px);
+ }
+}
+
+.scroll-x-reverse-transition {
+ @include transition-default;
+
+ &-enter-from,
+ &-leave-to {
+ opacity: 0;
+ }
+
+ &-enter-from {
+ transform: translateX(15px);
+ }
+
+ &-leave-to {
+ transform: translateX(-15px);
+ }
+}
\ No newline at end of file
diff --git a/src/styles/transition/slide.scss b/src/styles/transition/slide.scss
new file mode 100644
index 0000000..fb1fc85
--- /dev/null
+++ b/src/styles/transition/slide.scss
@@ -0,0 +1,39 @@
+.slide-y-transition {
+ @include transition-default;
+
+ &-enter-from,
+ &-leave-to {
+ opacity: 0;
+ transform: translateY(-15px);
+ }
+}
+
+.slide-y-reverse-transition {
+ @include transition-default;
+
+ &-enter-from,
+ &-leave-to {
+ opacity: 0;
+ transform: translateY(15px);
+ }
+}
+
+.slide-x-transition {
+ @include transition-default;
+
+ &-enter-from,
+ &-leave-to {
+ opacity: 0;
+ transform: translateX(-15px);
+ }
+}
+
+.slide-x-reverse-transition {
+ @include transition-default;
+
+ &-enter-from,
+ &-leave-to {
+ opacity: 0;
+ transform: translateX(15px);
+ }
+}
\ No newline at end of file
diff --git a/src/styles/transition/zoom.scss b/src/styles/transition/zoom.scss
new file mode 100644
index 0000000..41682b9
--- /dev/null
+++ b/src/styles/transition/zoom.scss
@@ -0,0 +1,27 @@
+// zoom-out
+.zoom-out-enter-active,
+.zoom-out-leave-active {
+ transition: opacity 0.1 ease-in-out, transform 0.15s ease-out;
+}
+
+.zoom-out-enter-from,
+.zoom-out-leave-to {
+ opacity: 0;
+ transform: scale(0);
+}
+
+// zoom-fade
+.zoom-fade-enter-active,
+.zoom-fade-leave-active {
+ transition: transform 0.2s, opacity 0.3s ease-out;
+}
+
+.zoom-fade-enter-from {
+ opacity: 0;
+ transform: scale(0.92);
+}
+
+.zoom-fade-leave-to {
+ opacity: 0;
+ transform: scale(1.06);
+}
\ No newline at end of file
diff --git a/src/styles/variables.module.scss b/src/styles/variables.module.scss
deleted file mode 100644
index 7feccc4..0000000
--- a/src/styles/variables.module.scss
+++ /dev/null
@@ -1,6 +0,0 @@
-// 导出 variables.module.scss 变量提供给TypeScript使用
-:export {
- menuBg: $menuBg;
- menuText: $menuText;
- menuActiveText: $menuActiveText;
-}
diff --git a/src/styles/variables.scss b/src/styles/variables.scss
deleted file mode 100644
index 909dbb4..0000000
--- a/src/styles/variables.scss
+++ /dev/null
@@ -1,24 +0,0 @@
-// 全局SCSS变量
-
-:root {
- --menuBg: rgb(15 27 37 / 80%);
- --menuText: #acc9e0;
- --menuActiveText: #fff;
- --menuHover: rgb(15 27 37 / 80%);
- --subMenuBg: rgb(15 27 37 / 80%);
- --subMenuActiveText: #f4f4f5;
- --subMenuHover: #001528;
-}
-
-$menuBg: var(--menuBg);
-$menuText: var(--menuText);
-$menuActiveText: var(--menuActiveText);
-$menuHover: var(--menuHover);
-
-$subMenuBg: var(--subMenuBg);
-$subMenuActiveText: var(--subMenuActiveText);
-$subMenuHover: var(--subMenuHover);
-
-$sideBarWidth: 168px;
-$headerHeight: 89.5px;
-$footerHeight: 22px;
diff --git a/src/utils/color.ts b/src/utils/color.ts
new file mode 100644
index 0000000..8b2ebb6
--- /dev/null
+++ b/src/utils/color.ts
@@ -0,0 +1,26 @@
+/**
+ * Sums the passed percentage to the R, G or B of a HEX color
+ * @param {string} color The color to change
+ * @param {number} amount The amount to change the color by
+ * @returns {string} The processed part of the color
+ */
+function addLight(color: string, amount: number) {
+ const cc = parseInt(color, 16) + amount;
+ const c = cc > 255 ? 255 : cc;
+ return c.toString(16).length > 1 ? c.toString(16) : `0${c.toString(16)}`;
+}
+
+/**
+ * Lightens a 6 char HEX color according to the passed percentage
+ * @param {string} color The color to change
+ * @param {number} amount The amount to change the color by
+ * @returns {string} The processed color represented as HEX
+ */
+export function lighten(color: string, amount: number) {
+ color = color.indexOf('#') >= 0 ? color.substring(1, color.length) : color;
+ amount = Math.trunc((255 * amount) / 100);
+ return `#${addLight(color.substring(0, 2), amount)}${addLight(
+ color.substring(2, 4),
+ amount
+ )}${addLight(color.substring(4, 6), amount)}`;
+}
diff --git a/src/utils/crypto.ts b/src/utils/crypto.ts
index ad8147f..55991b7 100644
--- a/src/utils/crypto.ts
+++ b/src/utils/crypto.ts
@@ -1,7 +1,7 @@
-import CryptoJS from "crypto-js";
-import { isString } from "./is";
+import CryptoJS from 'crypto-js';
+import { isString } from './is';
-const KEY = "zs";
+const KEY = 'zs';
/**
* * 加密
@@ -9,7 +9,7 @@ const KEY = "zs";
* @returns
*/
export const cryptoEncode = (data: string): string => {
- if (!isString(data)) return "";
+ if (!isString(data)) return '';
// 加密
const encryptedData = CryptoJS.AES.encrypt(data, KEY, {
mode: CryptoJS.mode.ECB,
@@ -24,7 +24,7 @@ export const cryptoEncode = (data: string): string => {
* @returns
*/
export const cryptoDecode = (data: string): string => {
- if (!isString(data)) return "";
+ if (!isString(data)) return '';
// 解密
const decryptedData = CryptoJS.AES.decrypt(data, KEY, {
mode: CryptoJS.mode.ECB,
diff --git a/src/utils/domUtils.ts b/src/utils/domUtils.ts
index a74cd47..bd7b0f3 100644
--- a/src/utils/domUtils.ts
+++ b/src/utils/domUtils.ts
@@ -1,4 +1,4 @@
-import { upperFirst } from "lodash-es";
+import { upperFirst } from 'lodash-es';
export interface ViewportOffsetResult {
left: number;
@@ -17,18 +17,18 @@ export function getBoundingClientRect(element: Element): DOMRect | number {
}
function trim(string: string) {
- return (string || "").replace(/^[\s\uFEFF]+|[\s\uFEFF]+$/g, "");
+ return (string || '').replace(/^[\s\uFEFF]+|[\s\uFEFF]+$/g, '');
}
/* istanbul ignore next */
export function hasClass(el: Element, cls: string) {
if (!el || !cls) return false;
- if (cls.indexOf(" ") !== -1)
- throw new Error("className should not contain space.");
+ if (cls.indexOf(' ') !== -1)
+ throw new Error('className should not contain space.');
if (el.classList) {
return el.classList.contains(cls);
} else {
- return (" " + el.className + " ").indexOf(" " + cls + " ") > -1;
+ return (' ' + el.className + ' ').indexOf(' ' + cls + ' ') > -1;
}
}
@@ -36,7 +36,7 @@ export function hasClass(el: Element, cls: string) {
export function addClass(el: Element, cls: string) {
if (!el) return;
let curClass = el.className;
- const classes = (cls || "").split(" ");
+ const classes = (cls || '').split(' ');
for (let i = 0, j = classes.length; i < j; i++) {
const clsName = classes[i];
@@ -45,7 +45,7 @@ export function addClass(el: Element, cls: string) {
if (el.classList) {
el.classList.add(clsName);
} else if (!hasClass(el, clsName)) {
- curClass += " " + clsName;
+ curClass += ' ' + clsName;
}
}
if (!el.classList) {
@@ -56,8 +56,8 @@ export function addClass(el: Element, cls: string) {
/* istanbul ignore next */
export function removeClass(el: Element, cls: string) {
if (!el || !cls) return;
- const classes = cls.split(" ");
- let curClass = " " + el.className + " ";
+ const classes = cls.split(' ');
+ let curClass = ' ' + el.className + ' ';
for (let i = 0, j = classes.length; i < j; i++) {
const clsName = classes[i];
@@ -66,7 +66,7 @@ export function removeClass(el: Element, cls: string) {
if (el.classList) {
el.classList.remove(clsName);
} else if (hasClass(el, clsName)) {
- curClass = curClass.replace(" " + clsName + " ", " ");
+ curClass = curClass.replace(' ' + clsName + ' ', ' ');
}
}
if (!el.classList) {
@@ -125,7 +125,7 @@ export function getViewportOffset(element: Element): ViewportOffsetResult {
}
export function hackCss(attr: string, value: string) {
- const prefix: string[] = ["webkit", "Moz", "ms", "OT"];
+ const prefix: string[] = ['webkit', 'Moz', 'ms', 'OT'];
const styleObj: any = {};
prefix.forEach((item) => {
diff --git a/src/utils/env.ts b/src/utils/env.ts
index 2360040..a5634e6 100644
--- a/src/utils/env.ts
+++ b/src/utils/env.ts
@@ -1,12 +1,12 @@
/**
* @description: Development model
*/
-export const devMode = "development";
+export const devMode = 'development';
/**
* @description: Production mode
*/
-export const prodMode = "production";
+export const prodMode = 'production';
/**
* @description: Get environment variables
diff --git a/src/utils/index.ts b/src/utils/index.ts
index e6e41b1..660ff6f 100644
--- a/src/utils/index.ts
+++ b/src/utils/index.ts
@@ -1,10 +1,11 @@
-export * from "@/utils/utils";
-export * from "@/utils/router";
-export * from "@/utils/is";
-export * from "@/utils/domUtils";
-export * from "@/utils/plugin";
-export * from "@/utils/menu";
-export * from "@/utils/storage";
-export * from "@/utils/crypto";
-export * from "@/utils/props";
-export * from "@/utils/tree";
+export * from '@/utils/utils';
+export * from '@/utils/router';
+export * from '@/utils/is';
+export * from '@/utils/domUtils';
+export * from '@/utils/plugin';
+export * from '@/utils/menu';
+export * from '@/utils/storage';
+export * from '@/utils/crypto';
+export * from '@/utils/props';
+export * from '@/utils/tree';
+export * from '@/utils/color';
diff --git a/src/utils/is.ts b/src/utils/is.ts
index 985c678..76cfac7 100644
--- a/src/utils/is.ts
+++ b/src/utils/is.ts
@@ -11,14 +11,14 @@ export function is(val: unknown, type: string) {
* @description: 是否为函数
*/
export function isFunction(val: unknown): val is T {
- return is(val, "Function") || is(val, "AsyncFunction");
+ return is(val, 'Function') || is(val, 'AsyncFunction');
}
/**
* @description: 是否已定义
*/
export const isDef = (val?: T): val is T => {
- return typeof val !== "undefined";
+ return typeof val !== 'undefined';
};
export const isUnDef = (val?: T): val is T => {
@@ -28,21 +28,21 @@ export const isUnDef = (val?: T): val is T => {
* @description: 是否为对象
*/
export const isObject = (val: any): val is Record => {
- return val !== null && is(val, "Object");
+ return val !== null && is(val, 'Object');
};
/**
* @description: 是否为时间
*/
export function isDate(val: unknown): val is Date {
- return is(val, "Date");
+ return is(val, 'Date');
}
/**
* @description: 是否为数值
*/
export function isNumber(val: unknown): val is number {
- return is(val, "Number");
+ return is(val, 'Number');
}
/**
@@ -51,7 +51,7 @@ export function isNumber(val: unknown): val is number {
export function isAsyncFunction(
val: unknown
): val is () => Promise {
- return is(val, "AsyncFunction");
+ return is(val, 'AsyncFunction');
}
/**
@@ -59,7 +59,7 @@ export function isAsyncFunction(
*/
export function isPromise(val: unknown): val is Promise {
return (
- is(val, "Promise") &&
+ is(val, 'Promise') &&
isObject(val) &&
isFunction(val.then) &&
isFunction(val.catch)
@@ -70,14 +70,14 @@ export function isPromise(val: unknown): val is Promise {
* @description: 是否为字符串
*/
export function isString(val: unknown): val is string {
- return is(val, "String");
+ return is(val, 'String');
}
/**
* @description: 是否为boolean类型
*/
export function isBoolean(val: unknown): val is boolean {
- return is(val, "Boolean");
+ return is(val, 'Boolean');
}
/**
@@ -91,25 +91,25 @@ export function isArray(val: any): val is Array {
* @description: 是否客户端
*/
export const isClient = () => {
- return typeof window !== "undefined";
+ return typeof window !== 'undefined';
};
/**
* @description: 是否为浏览器
*/
export const isWindow = (val: any): val is Window => {
- return typeof window !== "undefined" && is(val, "Window");
+ return typeof window !== 'undefined' && is(val, 'Window');
};
export const isElement = (val: unknown): val is Element => {
return isObject(val) && !!val.tagName;
};
-export const isServer = typeof window === "undefined";
+export const isServer = typeof window === 'undefined';
// 是否为图片节点
export function isImageDom(o: Element) {
- return o && ["IMAGE", "IMG"].includes(o.tagName);
+ return o && ['IMAGE', 'IMG'].includes(o.tagName);
}
export function isNull(val: unknown): val is null {
diff --git a/src/utils/menu.ts b/src/utils/menu.ts
index 01889d9..f5db982 100644
--- a/src/utils/menu.ts
+++ b/src/utils/menu.ts
@@ -1,4 +1,5 @@
-import { PageEnum } from "@/enums/pageEnum";
+import { PageEnum } from '@/enums/pageEnum';
+import { cloneDeep } from 'lodash-es';
/**
* 判断根路由 Router
@@ -17,12 +18,65 @@ export function filterRouter(routerMap: Array) {
return routerMap.filter((item) => {
return (
(item.meta?.hidden || false) != true &&
- !["/:path(.*)*", "/", PageEnum.REDIRECT, PageEnum.BASE_LOGIN].includes(
+ !['/:path(.*)*', '/', PageEnum.REDIRECT, PageEnum.BASE_LOGIN].includes(
item.path
)
);
});
}
+/**
+ * 混合菜单
+ * */
+export function generatorMenuMix(
+ routerMap: Array,
+ routerName: string,
+ location: string
+) {
+ const cloneRouterMap = cloneDeep(routerMap);
+ const newRouter = filterRouter(cloneRouterMap);
+ if (location === 'header') {
+ const firstRouter: any[] = [];
+ newRouter.forEach((item) => {
+ const isRoot = isRootRouter(item);
+ const info = isRoot ? item.children[0] : item;
+ info.children = undefined;
+ const currentMenu = {
+ ...info,
+ ...info.meta,
+ label: info.meta?.title,
+ key: info.name,
+ };
+ firstRouter.push(currentMenu);
+ });
+ return firstRouter;
+ } else {
+ return getChildrenRouter(
+ newRouter.filter((item) => item.name === routerName)
+ );
+ }
+}
+
+/**
+ * 递归组装子菜单
+ * */
+export function getChildrenRouter(routerMap: Array) {
+ return filterRouter(routerMap).map((item) => {
+ const isRoot = isRootRouter(item);
+ const info = isRoot ? item.children[0] : item;
+ const currentMenu = {
+ ...info,
+ ...info.meta,
+ label: info.meta?.title,
+ key: info.name,
+ };
+ // 是否有子菜单,并递归处理
+ if (info.children && info.children.length > 0) {
+ // Recursion
+ currentMenu.children = getChildrenRouter(info.children);
+ }
+ return currentMenu;
+ });
+}
/**
* 递归组装菜单格式
diff --git a/src/utils/plugin.ts b/src/utils/plugin.ts
index 2e995fe..2c481e0 100644
--- a/src/utils/plugin.ts
+++ b/src/utils/plugin.ts
@@ -1,25 +1,25 @@
-import { icons } from "@/plugins";
-import { DialogEnum } from "@/enums/pluginEnum";
-import { DialogReactive } from "naive-ui";
+import { icons } from '@/plugins';
+import { DialogEnum } from '@/enums/pluginEnum';
+import { DialogReactive } from 'naive-ui';
const { InformationCircleIcon } = icons.ionicons5;
-import { renderIcon } from "@/utils";
+import { renderIcon } from '@/utils';
// * 开启加载
export const loadingStart = () => {
- window["$loading"].start();
+ window['$loading'].start();
};
// * 加载结束
export const loadingFinish = () => {
setTimeout(() => {
- window["$loading"].finish();
+ window['$loading'].finish();
});
};
// * 加载错误
export const loadingError = () => {
setTimeout(() => {
- window["$loading"].error();
+ window['$loading'].error();
});
};
@@ -28,11 +28,11 @@ export const loadingError = () => {
* @param { Object} params 配置参数, 详见 https://www.naiveui.com/zh-CN/light/components/dialog
* ```
* 最简易的 demo
- * goDialog({
+ * zsDialog({
* onPositiveCallback: () => {}
* })
* ```
- *goDialog({
+ *zsDialog({
message: "请选择方式:",
type: DialogEnum.DELETE,
promise: true,
@@ -51,7 +51,7 @@ export const loadingError = () => {
});
*/
-export const goDialog = (params: {
+export const zsDialog = (params: {
// 基本
type?: DialogEnum;
// 标题
@@ -93,32 +93,32 @@ export const goDialog = (params: {
const typeObj = {
// 自定义
[DialogEnum.DELETE]: {
- fn: window["$dialog"].warning,
- message: message || "是否删除此数据?",
+ fn: window['$dialog'].warning,
+ message: message || '是否删除此数据?',
},
// 原有
[DialogEnum.WARNING]: {
- fn: window["$dialog"].warning,
- message: message || "是否执行此操作?",
+ fn: window['$dialog'].warning,
+ message: message || '是否执行此操作?',
},
[DialogEnum.ERROR]: {
- fn: window["$dialog"].error,
- message: message || "是否执行此操作?",
+ fn: window['$dialog'].error,
+ message: message || '是否执行此操作?',
},
[DialogEnum.SUCCESS]: {
- fn: window["$dialog"].success,
- message: message || "是否执行此操作?",
+ fn: window['$dialog'].success,
+ message: message || '是否执行此操作?',
},
};
- const dialog: DialogReactive = typeObj[type || DialogEnum.WARNING]["fn"]({
+ const dialog: DialogReactive = typeObj[type || DialogEnum.WARNING]['fn']({
// 导入其余 NaiveUI 支持参数
...params,
- title: title || "提示",
- icon: renderIcon(InformationCircleIcon, { size: "20" }),
- content: typeObj[type || DialogEnum.WARNING]["message"],
- positiveText: positiveText || "确定",
- negativeText: closeNegativeText ? undefined : negativeText || "取消",
+ title: title || '提示',
+ icon: renderIcon(InformationCircleIcon, { size: '20' }),
+ content: typeObj[type || DialogEnum.WARNING]['message'],
+ positiveText: positiveText || '确定',
+ negativeText: closeNegativeText ? undefined : negativeText || '取消',
// 是否通过遮罩关闭
maskClosable: isMaskClosable || false,
onPositiveClick: async () => {
diff --git a/src/utils/propTypes.ts b/src/utils/propTypes.ts
index 7d692e4..f748cf7 100644
--- a/src/utils/propTypes.ts
+++ b/src/utils/propTypes.ts
@@ -1,5 +1,5 @@
-import { CSSProperties, VNodeChild } from "vue";
-import { createTypes, VueTypeValidableDef, VueTypesInterface } from "vue-types";
+import { CSSProperties, VNodeChild } from 'vue';
+import { createTypes, VueTypeValidableDef, VueTypesInterface } from 'vue-types';
export type VueNode = VNodeChild | JSX.Element;
@@ -19,13 +19,13 @@ const propTypes = createTypes({
propTypes.extend([
{
- name: "style",
+ name: 'style',
getter: true,
type: [String, Object],
default: undefined,
},
{
- name: "VNodeChild",
+ name: 'VNodeChild',
getter: true,
type: undefined,
},
diff --git a/src/utils/router.ts b/src/utils/router.ts
index ca60f34..dd9eb33 100644
--- a/src/utils/router.ts
+++ b/src/utils/router.ts
@@ -1,9 +1,9 @@
-import router from "@/router";
-import { ResultEnum } from "@/enums/httpEnum";
-import { ErrorPageNameMap } from "@/enums/pageEnum";
+import router from '@/router';
+import { ResultEnum } from '@/enums/httpEnum';
+import { ErrorPageNameMap } from '@/enums/pageEnum';
// import { cryptoDecode } from "./crypto";
-import { StorageEnum } from "@/enums/storageEnum";
-import { storage } from "@/utils/storage";
+import { StorageEnum } from '@/enums/storageEnum';
+import { storage } from '@/utils/storage';
/**
* * 根据名字跳转路由
@@ -17,7 +17,7 @@ export const routerTurnByName = (
windowOpen?: boolean
) => {
if (windowOpen) {
- const path = fetchPathByName(pageName, "href");
+ const path = fetchPathByName(pageName, 'href');
openNewWindow(path);
return;
}
@@ -56,7 +56,7 @@ export const fetchPathByName = (pageName: string, p?: string) => {
});
return p ? (pathData as any)[p] : pathData;
} catch (error) {
- console.log("查询路由信息失败,请联系管理员!");
+ console.log('查询路由信息失败,请联系管理员!');
}
};
@@ -65,7 +65,7 @@ export const fetchPathByName = (pageName: string, p?: string) => {
* @param url
*/
export const openNewWindow = (url: string) => {
- return window.open(url, "_blank");
+ return window.open(url, '_blank');
};
/**
diff --git a/src/utils/storage.ts b/src/utils/storage.ts
index 15f8170..2e3102f 100644
--- a/src/utils/storage.ts
+++ b/src/utils/storage.ts
@@ -11,7 +11,7 @@ export default class Storage {
private storage: globalThis.Storage;
private prefixKey?: string;
- constructor(prefixKey = "", storage = localStorage) {
+ constructor(prefixKey = '', storage = localStorage) {
this.storage = storage;
this.prefixKey = prefixKey;
}
@@ -94,14 +94,14 @@ export default class Storage {
* @param name
*/
getCookie(name: string): string {
- const cookieArr = document.cookie.split("; ");
+ const cookieArr = document.cookie.split('; ');
for (let i = 0, length = cookieArr.length; i < length; i++) {
- const kv = cookieArr[i].split("=");
+ const kv = cookieArr[i].split('=');
if (kv[0] === this.getKey(name)) {
return kv[1];
}
}
- return "";
+ return '';
}
/**
@@ -119,10 +119,10 @@ export default class Storage {
const keys = document.cookie.match(/[^ =;]+(?==)/g);
if (keys) {
for (let i = keys.length; i--; ) {
- document.cookie = keys[i] + "=0;expire=" + new Date(0).toUTCString();
+ document.cookie = keys[i] + '=0;expire=' + new Date(0).toUTCString();
}
}
}
}
-export const storage = new Storage("");
+export const storage = new Storage('');
diff --git a/src/utils/utils.ts b/src/utils/utils.ts
index 87d072e..9cfb4a6 100644
--- a/src/utils/utils.ts
+++ b/src/utils/utils.ts
@@ -1,5 +1,5 @@
-import { h } from "vue";
-import { NIcon } from "naive-ui";
+import { h } from 'vue';
+import { NIcon } from 'naive-ui';
/**
* 格式化时间戳为字符串
* @param {number} time - 待格式化的时间戳
@@ -15,23 +15,23 @@ export function formatDate(time: number): string {
let second: number | string = date.getSeconds();
if (month < 10) {
- month = "0" + month;
+ month = '0' + month;
}
if (day < 10) {
- day = "0" + day;
+ day = '0' + day;
}
if (hour < 10) {
- hour = "0" + hour;
+ hour = '0' + hour;
}
if (minute < 10) {
- minute = "0" + minute;
+ minute = '0' + minute;
}
if (second < 10) {
- second = "0" + second;
+ second = '0' + second;
}
/**
@@ -39,7 +39,7 @@ export function formatDate(time: number): string {
* @type {string}
*/
const formattedDate: string =
- year + "-" + month + "-" + day + " " + hour + ":" + minute + ":" + second;
+ year + '-' + month + '-' + day + ' ' + hour + ':' + minute + ':' + second;
return formattedDate;
}
diff --git a/src/views/exception/403.vue b/src/views/exception/403.vue
index 9b9d0bb..d71d7fe 100644
--- a/src/views/exception/403.vue
+++ b/src/views/exception/403.vue
@@ -11,9 +11,9 @@
diff --git a/src/views/exception/404.vue b/src/views/exception/404.vue
index cd2e301..cd1b238 100644
--- a/src/views/exception/404.vue
+++ b/src/views/exception/404.vue
@@ -11,9 +11,9 @@
diff --git a/src/views/iframe/index.vue b/src/views/iframe/index.vue
new file mode 100644
index 0000000..bf7b6c5
--- /dev/null
+++ b/src/views/iframe/index.vue
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/src/views/login/index.vue b/src/views/login/index.vue
index cffec91..104ae6b 100644
--- a/src/views/login/index.vue
+++ b/src/views/login/index.vue
@@ -61,18 +61,16 @@
-
-
diff --git a/src/views/system/user/columns.ts b/src/views/system/user/columns.ts
index 6aa099f..657f521 100644
--- a/src/views/system/user/columns.ts
+++ b/src/views/system/user/columns.ts
@@ -1,49 +1,49 @@
-import { h } from "vue";
-import { NTag } from "naive-ui";
+import { h } from 'vue';
+import { NTag } from 'naive-ui';
export const columns = [
{
- title: "用户名",
- key: "username",
+ title: '用户名',
+ key: 'username',
},
{
- title: "真实姓名",
- key: "realname",
+ title: '真实姓名',
+ key: 'realname',
},
{
- title: "手机号",
- key: "phone",
+ title: '手机号',
+ key: 'phone',
},
{
- title: "电子邮箱",
- key: "email",
+ title: '电子邮箱',
+ key: 'email',
},
{
- title: "用户类型",
- key: "userType",
+ title: '用户类型',
+ key: 'userType',
render(row) {
return h(
NTag,
{
- type: row.userType === 1 ? "success" : "info",
+ type: row.userType === 1 ? 'success' : 'info',
},
{
- default: () => (row.userType === 1 ? "员工" : "用户"),
+ default: () => (row.userType === 1 ? '员工' : '用户'),
}
);
},
},
{
- title: "性别",
- key: "sex",
+ title: '性别',
+ key: 'sex',
render(row) {
return h(
NTag,
{
- type: row.sex === 1 ? "success" : "info",
+ type: row.sex === 1 ? 'success' : 'info',
},
{
- default: () => (row.sex === 1 ? "男" : "女"),
+ default: () => (row.sex === 1 ? '男' : '女'),
}
);
},
diff --git a/src/views/system/user/components/UserModal.vue b/src/views/system/user/components/UserModal.vue
index af7d9d7..fdb66dd 100644
--- a/src/views/system/user/components/UserModal.vue
+++ b/src/views/system/user/components/UserModal.vue
@@ -1,5 +1,5 @@
-
-
diff --git a/.stylelintrc.cjs b/stylelint.config.js
similarity index 75%
rename from .stylelintrc.cjs
rename to stylelint.config.js
index 06ed90d..30a5832 100644
--- a/.stylelintrc.cjs
+++ b/stylelint.config.js
@@ -36,7 +36,24 @@ module.exports = {
"property-no-unknown": [
true,
{
- ignoreProperties: ["menuBg", "menuText", "menuActiveText"],
+ // ignoreProperties: ["menuBg", "menuText", "menuActiveText"],
+ },
+ ],
+ "at-rule-no-unknown": [
+ true,
+ {
+ ignoreAtRules: [
+ "tailwind",
+ "apply",
+ "variants",
+ "responsive",
+ "screen",
+ "function",
+ "if",
+ "each",
+ "include",
+ "mixin",
+ ],
},
],
},
diff --git a/tailwind.config.js b/tailwind.config.js
new file mode 100644
index 0000000..091fa7a
--- /dev/null
+++ b/tailwind.config.js
@@ -0,0 +1,7 @@
+module.exports = {
+ content: ["./index.html", "./src/**/*.{vue,ts,tsx}"],
+ important: true,
+ theme: {
+ extend: {},
+ },
+};
diff --git a/tsconfig.json b/tsconfig.json
index 448015d..3e240df 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -12,8 +12,10 @@
"lib": ["esnext", "dom"],
"baseUrl": ".",
"allowJs": true,
+ "typeRoots": ["./node_modules/@types/", "./types"],
"paths": {
- "@/*": ["src/*"]
+ "@/*": ["src/*"],
+ "/#/*": ["types/*"]
},
"types": ["vite/client"],
"skipLibCheck": true /* Skip type checking all .d.ts files. */,
diff --git a/types/components.d.ts b/types/components.d.ts
index b161fd7..e986bb8 100644
--- a/types/components.d.ts
+++ b/types/components.d.ts
@@ -9,6 +9,7 @@ export {}
declare module '@vue/runtime-core' {
export interface GlobalComponents {
+ Application: typeof import('./../src/components/Application/Application.vue')['default']
BasicForm: typeof import('./../src/components/Form/src/BasicForm.vue')['default']
BasicForm2: typeof import('./../src/components/Form/src/BasicForm2.vue')['default']
ColumnSetting: typeof import('./../src/components/Table/src/components/settings/ColumnSetting.vue')['default']
@@ -16,8 +17,12 @@ declare module '@vue/runtime-core' {
Header: typeof import('./../src/layout/components/Header/index.vue')['default']
Histogram: typeof import('./../src/components/Charts/Histogram.vue')['default']
Line: typeof import('./../src/components/Charts/Line.vue')['default']
+ Lockscreen: typeof import('./../src/components/Lockscreen/Lockscreen.vue')['default']
+ Logo: typeof import('./../src/layout/components/Logo/index.vue')['default']
Main: typeof import('./../src/layout/components/Main/index.vue')['default']
MenuModal: typeof import('./../src/views/system/role/components/MenuModal.vue')['default']
+ ProjectSetting: typeof import('./../src/layout/components/Header/ProjectSetting.vue')['default']
+ Recharge: typeof import('./../src/components/Lockscreen/Recharge.vue')['default']
Ring: typeof import('./../src/components/Charts/Ring.vue')['default']
RoleModal: typeof import('./../src/views/system/role/components/RoleModal.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink']
@@ -26,7 +31,7 @@ declare module '@vue/runtime-core' {
SvgIcon: typeof import('./../src/components/SvgIcon/index.vue')['default']
Table: typeof import('./../src/components/Table/src/Table.vue')['default']
TableAction: typeof import('./../src/components/Table/src/components/TableAction.vue')['default']
- TransitionMain: typeof import('./../src/layout/components/TransitionMain/index.vue')['default']
+ TagsView: typeof import('./../src/layout/components/TagsView/index.vue')['default']
UserModal: typeof import('./../src/views/system/user/components/UserModal.vue')['default']
}
}
diff --git a/types/config.d.ts b/types/config.d.ts
new file mode 100644
index 0000000..34f404f
--- /dev/null
+++ b/types/config.d.ts
@@ -0,0 +1,74 @@
+export interface ProjectSettingState {
+ //导航模式
+ navMode: string;
+ //导航风格
+ navTheme: string;
+ //顶部设置
+ headerSetting: object;
+ //页脚
+ showFooter: boolean;
+ //菜单设置
+ menuSetting: object;
+ //多标签
+ multiTabsSetting: object;
+ //面包屑
+ crumbsSetting: object;
+ //权限模式
+ permissionMode: string;
+}
+
+export interface IBodySetting {
+ fixed: boolean;
+}
+
+export interface IHeaderSetting {
+ bgColor: string;
+ fixed: boolean;
+ isReload: boolean;
+}
+
+export interface IMenuSetting {
+ minMenuWidth: number;
+ menuWidth: number;
+ fixed: boolean;
+ mixMenu: boolean;
+ collapsed: boolean;
+ mobileWidth: number;
+}
+
+export interface ICrumbsSetting {
+ show: boolean;
+ showIcon: boolean;
+}
+
+export interface IMultiTabsSetting {
+ bgColor: string;
+ fixed: boolean;
+ show: boolean;
+}
+export interface GlobConfig {
+ title: string;
+ apiUrl: string;
+ shortName: string;
+ urlPrefix?: string;
+ uploadUrl?: string;
+ prodMock: boolean;
+ imgUrl?: string;
+}
+
+export interface GlobEnvConfig {
+ // 标题
+ VITE_GLOB_APP_TITLE: string;
+ // 接口地址
+ VITE_GLOB_API_URL: string;
+ // 接口前缀
+ VITE_GLOB_API_URL_PREFIX?: string;
+ // Project abbreviation
+ VITE_GLOB_APP_SHORT_NAME: string;
+ // 图片上传地址
+ VITE_GLOB_UPLOAD_URL?: string;
+ //图片前缀地址
+ VITE_GLOB_IMG_URL?: string;
+ //生产环境开启mock
+ VITE_GLOB_PROD_MOCK: boolean;
+}
diff --git a/vite.config.ts b/vite.config.ts
index 26e13ec..99b5da4 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -24,15 +24,15 @@ export default defineConfig(({ mode }: ConfigEnv): UserConfig => {
},
css: {
// CSS 预处理器
- preprocessorOptions: {
- // 定义全局 SCSS 变量
- scss: {
- javascriptEnabled: true,
- additionalData: `
- @use "@/styles/variables.scss" as *;
- `,
- },
- },
+ // preprocessorOptions: {
+ // 定义全局 SCSS 变量
+ // scss: {
+ // javascriptEnabled: true,
+ // additionalData: `
+ // @use "@/styles/variables.scss" as *;
+ // `,
+ // },
+ // },
},
server: {
// 允许IP访问
@@ -89,7 +89,7 @@ export default defineConfig(({ mode }: ConfigEnv): UserConfig => {
}),
createSvgIconsPlugin({
// 指定需要缓存的图标文件夹
- iconDirs: [resolve(pathSrc, "assets/icons")],
+ iconDirs: [resolve(pathSrc, "assets/svgs")],
// 指定symbolId格式
symbolId: "icon-[dir]-[name]",
}),