feat: 修改代码规则,新增Layout动态布局,新增系统设置
@ -1,14 +1,14 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
// (x)=>{},单个参数箭头函数是否显示小括号。(always:始终显示;avoid:省略括号。默认:always)
|
// (x)=>{},单个参数箭头函数是否显示小括号。(always:始终显示;avoid:省略括号。默认:always)
|
||||||
arrowParens: "always",
|
arrowParens: 'always',
|
||||||
// 开始标签的右尖括号是否跟随在最后一行属性末尾,默认false
|
// 开始标签的右尖括号是否跟随在最后一行属性末尾,默认false
|
||||||
bracketSameLine: false,
|
bracketSameLine: false,
|
||||||
// 对象字面量的括号之间打印空格 (true - Example: { foo: bar } ; false - Example: {foo:bar})
|
// 对象字面量的括号之间打印空格 (true - Example: { foo: bar } ; false - Example: {foo:bar})
|
||||||
bracketSpacing: true,
|
bracketSpacing: true,
|
||||||
// 是否格式化一些文件中被嵌入的代码片段的风格(auto|off;默认auto)
|
// 是否格式化一些文件中被嵌入的代码片段的风格(auto|off;默认auto)
|
||||||
embeddedLanguageFormatting: "auto",
|
embeddedLanguageFormatting: 'auto',
|
||||||
// 指定 HTML 文件的空格敏感度 (css|strict|ignore;默认css)
|
// 指定 HTML 文件的空格敏感度 (css|strict|ignore;默认css)
|
||||||
htmlWhitespaceSensitivity: "css",
|
htmlWhitespaceSensitivity: 'css',
|
||||||
// 当文件已经被 Prettier 格式化之后,是否会在文件顶部插入一个特殊的 @format 标记,默认false
|
// 当文件已经被 Prettier 格式化之后,是否会在文件顶部插入一个特殊的 @format 标记,默认false
|
||||||
insertPragma: false,
|
insertPragma: false,
|
||||||
// 在 JSX 中使用单引号替代双引号,默认false
|
// 在 JSX 中使用单引号替代双引号,默认false
|
||||||
@ -16,9 +16,9 @@ module.exports = {
|
|||||||
// 每行最多字符数量,超出换行(默认80)
|
// 每行最多字符数量,超出换行(默认80)
|
||||||
printWidth: 80,
|
printWidth: 80,
|
||||||
// 超出打印宽度 (always | never | preserve )
|
// 超出打印宽度 (always | never | preserve )
|
||||||
proseWrap: "preserve",
|
proseWrap: 'preserve',
|
||||||
// 对象属性是否使用引号(as-needed | consistent | preserve;默认as-needed:对象的属性需要加引号才添加;)
|
// 对象属性是否使用引号(as-needed | consistent | preserve;默认as-needed:对象的属性需要加引号才添加;)
|
||||||
quoteProps: "as-needed",
|
quoteProps: 'as-needed',
|
||||||
// 是否只格式化在文件顶部包含特定注释(@prettier| @format)的文件,默认false
|
// 是否只格式化在文件顶部包含特定注释(@prettier| @format)的文件,默认false
|
||||||
requirePragma: false,
|
requirePragma: false,
|
||||||
// 结尾添加分号
|
// 结尾添加分号
|
||||||
@ -28,18 +28,18 @@ module.exports = {
|
|||||||
// 缩进空格数,默认2个空格
|
// 缩进空格数,默认2个空格
|
||||||
tabWidth: 2,
|
tabWidth: 2,
|
||||||
// 元素末尾是否加逗号,默认es5: ES5中的 objects, arrays 等会添加逗号,TypeScript 中的 type 后不加逗号
|
// 元素末尾是否加逗号,默认es5: ES5中的 objects, arrays 等会添加逗号,TypeScript 中的 type 后不加逗号
|
||||||
trailingComma: "es5",
|
trailingComma: 'es5',
|
||||||
// 指定缩进方式,空格或tab,默认false,即使用空格
|
// 指定缩进方式,空格或tab,默认false,即使用空格
|
||||||
useTabs: false,
|
useTabs: false,
|
||||||
// vue 文件中是否缩进 <style> 和 <script> 标签,默认 false
|
// vue 文件中是否缩进 <style> 和 <script> 标签,默认 false
|
||||||
vueIndentScriptAndStyle: false,
|
vueIndentScriptAndStyle: false,
|
||||||
|
|
||||||
endOfLine: "auto",
|
endOfLine: 'auto',
|
||||||
overrides: [
|
overrides: [
|
||||||
{
|
{
|
||||||
files: "*.html",
|
files: '*.html',
|
||||||
options: {
|
options: {
|
||||||
parser: "html",
|
parser: 'html',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
14
package.json
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"name": "iec104",
|
"name": "zs-naiveui-admin",
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@ -41,16 +41,18 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@types/crypto-js": "^4.1.1",
|
||||||
"@vicons/antd": "^0.12.0",
|
"@vicons/antd": "^0.12.0",
|
||||||
"@vicons/ionicons5": "^0.12.0",
|
"@vicons/ionicons5": "^0.12.0",
|
||||||
"@vitejs/plugin-vue": "^4.4.0",
|
"@vitejs/plugin-vue": "^4.4.0",
|
||||||
"@vueuse/core": "^10.5.0",
|
"@vueuse/core": "^10.5.0",
|
||||||
"@wangeditor/editor": "^5.1.23",
|
"@wangeditor/editor": "^5.1.23",
|
||||||
"@wangeditor/editor-for-vue": "5.1.10",
|
"@wangeditor/editor-for-vue": "5.1.10",
|
||||||
"@types/crypto-js": "^4.1.1",
|
|
||||||
"axios": "^1.6.0",
|
"axios": "^1.6.0",
|
||||||
"crypto-js": "^4.2.0",
|
"crypto-js": "^4.2.0",
|
||||||
"echarts": "^5.4.3",
|
"echarts": "^5.4.3",
|
||||||
|
"element-resize-detector": "^1.2.4",
|
||||||
|
"lodash": "^4.17.21",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"naive-ui": "^2.36.0",
|
"naive-ui": "^2.36.0",
|
||||||
"path-browserify": "^1.0.1",
|
"path-browserify": "^1.0.1",
|
||||||
@ -61,7 +63,7 @@
|
|||||||
"vue-router": "^4.2.5",
|
"vue-router": "^4.2.5",
|
||||||
"vue-types": "^5.1.1",
|
"vue-types": "^5.1.1",
|
||||||
"vue3-seamless-scroll": "^2.0.1",
|
"vue3-seamless-scroll": "^2.0.1",
|
||||||
"vuedraggable": "^2.24.3"
|
"vuedraggable": "^4.1.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@commitlint/cli": "^17.8.1",
|
"@commitlint/cli": "^17.8.1",
|
||||||
@ -84,9 +86,6 @@
|
|||||||
"husky": "^8.0.3",
|
"husky": "^8.0.3",
|
||||||
"lint-staged": "^13.3.0",
|
"lint-staged": "^13.3.0",
|
||||||
"postcss": "^8.4.31",
|
"postcss": "^8.4.31",
|
||||||
"postcss-html": "^1.5.0",
|
|
||||||
"postcss-px-to-viewport": "^1.1.1",
|
|
||||||
"postcss-scss": "^4.0.9",
|
|
||||||
"prettier": "^2.8.8",
|
"prettier": "^2.8.8",
|
||||||
"sass": "^1.69.5",
|
"sass": "^1.69.5",
|
||||||
"stylelint": "^15.11.0",
|
"stylelint": "^15.11.0",
|
||||||
@ -96,6 +95,7 @@
|
|||||||
"stylelint-config-recommended-vue": "^1.5.0",
|
"stylelint-config-recommended-vue": "^1.5.0",
|
||||||
"stylelint-config-standard": "^34.0.0",
|
"stylelint-config-standard": "^34.0.0",
|
||||||
"stylelint-config-standard-scss": "^11.1.0",
|
"stylelint-config-standard-scss": "^11.1.0",
|
||||||
|
"tailwindcss": "^3.3.3",
|
||||||
"typescript": "^5.3.2",
|
"typescript": "^5.3.2",
|
||||||
"unplugin-auto-import": "^0.15.3",
|
"unplugin-auto-import": "^0.15.3",
|
||||||
"unplugin-icons": "^0.16.6",
|
"unplugin-icons": "^0.16.6",
|
||||||
@ -107,4 +107,4 @@
|
|||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=16.0.0"
|
"node": ">=16.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,20 +1,27 @@
|
|||||||
|
// module.exports = {
|
||||||
|
// plugins: {
|
||||||
|
// autoprefixer: {}, //// 用来给不同的浏览器自动添加相应前缀,如-webkit-,-moz-等等
|
||||||
|
// "postcss-px-to-viewport": {
|
||||||
|
// unitToConvert: "px", // 要转化的单位
|
||||||
|
// viewportWidth: 1920, // UI设计稿的宽度
|
||||||
|
// viewportHeight: 1080,
|
||||||
|
// unitPrecision: 10, // 转换后的精度,即小数点位数
|
||||||
|
// propList: ["*"], // 指定转换的css属性的单位,*代表全部css属性的单位都进行转换
|
||||||
|
// viewportUnit: "vw", // 指定需要转换成的视窗单位,默认vw
|
||||||
|
// fontViewportUnit: "vw", // 指定字体需要转换成的视窗单位,默认vw
|
||||||
|
// selectorBlackList: [".ignore"], // 指定不转换为视窗单位的类名,
|
||||||
|
// minPixelValue: 1, // 默认值1,小于或等于1px则不进行转换
|
||||||
|
// mediaQuery: false, // 是否在媒体查询的css代码中也进行转换,默认false
|
||||||
|
// replace: false, // 是否转换后直接更换属性值
|
||||||
|
// exclude: /(\/|\\)(node_modules)(\/|\\)/, // 设置忽略文件,用正则做目录名匹配
|
||||||
|
// // landscape: true // 是否处理横屏情况,
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// };
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
plugins: {
|
plugins: {
|
||||||
autoprefixer: {}, //// 用来给不同的浏览器自动添加相应前缀,如-webkit-,-moz-等等
|
tailwindcss: {},
|
||||||
"postcss-px-to-viewport": {
|
autoprefixer: {},
|
||||||
unitToConvert: "px", // 要转化的单位
|
|
||||||
viewportWidth: 1920, // UI设计稿的宽度
|
|
||||||
viewportHeight: 1080,
|
|
||||||
unitPrecision: 6, // 转换后的精度,即小数点位数
|
|
||||||
propList: ["*"], // 指定转换的css属性的单位,*代表全部css属性的单位都进行转换
|
|
||||||
viewportUnit: "vw", // 指定需要转换成的视窗单位,默认vw
|
|
||||||
fontViewportUnit: "vw", // 指定字体需要转换成的视窗单位,默认vw
|
|
||||||
selectorBlackList: [".ignore"], // 指定不转换为视窗单位的类名,
|
|
||||||
minPixelValue: 1, // 默认值1,小于或等于1px则不进行转换
|
|
||||||
mediaQuery: false, // 是否在媒体查询的css代码中也进行转换,默认false
|
|
||||||
replace: true, // 是否转换后直接更换属性值
|
|
||||||
exclude: /(\/|\\)(node_modules)(\/|\\)/, // 设置忽略文件,用正则做目录名匹配
|
|
||||||
// landscape: true // 是否处理横屏情况,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
104
src/App.vue
@ -1,21 +1,89 @@
|
|||||||
<script setup lang="ts">
|
<template>
|
||||||
import { NConfigProvider } from "naive-ui";
|
<NConfigProvider
|
||||||
import { zhCN, dateZhCN } from "naive-ui";
|
v-if="!isLock"
|
||||||
const themeOverrides = {
|
:locale="zhCN"
|
||||||
common: {
|
:theme="getDarkTheme"
|
||||||
primaryColor: "#646CFF",
|
:theme-overrides="getThemeOverrides"
|
||||||
primaryColorHover: "rgba(100,108,255,.8)",
|
:date-locale="dateZhCN"
|
||||||
primaryColorPressed: "rgba(100,108,255,.8)",
|
>
|
||||||
},
|
<AppProvider>
|
||||||
|
<RouterView />
|
||||||
|
</AppProvider>
|
||||||
|
</NConfigProvider>
|
||||||
|
|
||||||
|
<transition v-if="isLock && $route.name !== 'login'" name="slide-up">
|
||||||
|
<LockScreen />
|
||||||
|
</transition>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { computed, onMounted, onUnmounted } from "vue";
|
||||||
|
import { zhCN, dateZhCN, darkTheme } from "naive-ui";
|
||||||
|
import { LockScreen } from "@/components/Lockscreen";
|
||||||
|
import { AppProvider } from "@/components/Application";
|
||||||
|
import { useScreenLockStore } from "@/store/modules/screenLock.js";
|
||||||
|
import { useRoute } from "vue-router";
|
||||||
|
import { useDesignSettingStore } from "@/store/modules/designSetting";
|
||||||
|
import { lighten } from "@/utils";
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
|
const useScreenLock = useScreenLockStore();
|
||||||
|
const designStore = useDesignSettingStore();
|
||||||
|
const isLock = computed(() => useScreenLock.isLocked);
|
||||||
|
const lockTime = computed(() => useScreenLock.lockTime);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type import('naive-ui').GlobalThemeOverrides
|
||||||
|
*/
|
||||||
|
const getThemeOverrides = computed(() => {
|
||||||
|
const appTheme = designStore.appTheme;
|
||||||
|
const lightenStr = lighten(designStore.appTheme, 8);
|
||||||
|
return {
|
||||||
|
common: {
|
||||||
|
primaryColor: appTheme,
|
||||||
|
primaryColorHover: lightenStr,
|
||||||
|
primaryColorPressed: lightenStr,
|
||||||
|
primaryColorSuppl: appTheme,
|
||||||
|
},
|
||||||
|
LoadingBar: {
|
||||||
|
colorLoading: appTheme,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const getDarkTheme = computed(() =>
|
||||||
|
designStore.darkTheme ? darkTheme : undefined
|
||||||
|
);
|
||||||
|
|
||||||
|
let timer: any;
|
||||||
|
|
||||||
|
const timekeeping = () => {
|
||||||
|
clearInterval(timer);
|
||||||
|
if (route.name == "login" || isLock.value) return;
|
||||||
|
// 设置不锁屏
|
||||||
|
useScreenLock.setLock(false);
|
||||||
|
// 重置锁屏时间
|
||||||
|
useScreenLock.setLockTime();
|
||||||
|
timer = setInterval(() => {
|
||||||
|
// 锁屏倒计时递减
|
||||||
|
useScreenLock.setLockTime(lockTime.value - 1);
|
||||||
|
if (lockTime.value <= 0) {
|
||||||
|
// 设置锁屏
|
||||||
|
useScreenLock.setLock(true);
|
||||||
|
return clearInterval(timer);
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
document.addEventListener("mousedown", timekeeping);
|
||||||
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
document.removeEventListener("mousedown", timekeeping);
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<style lang="scss">
|
||||||
<n-config-provider
|
@import "styles/index";
|
||||||
:locale="zhCN"
|
</style>
|
||||||
:date-locale="dateZhCN"
|
|
||||||
:theme-overrides="themeOverrides"
|
|
||||||
>
|
|
||||||
<router-view />
|
|
||||||
</n-config-provider>
|
|
||||||
</template>
|
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
import axios, { AxiosResponse, AxiosRequestConfig } from "axios";
|
import axios, { AxiosResponse, AxiosRequestConfig } from 'axios';
|
||||||
import { ResultEnum } from "@/enums/httpEnum";
|
import { ResultEnum } from '@/enums/httpEnum';
|
||||||
// import { ErrorPageNameMap } from "@/enums/pageEnum";
|
// import { ErrorPageNameMap } from "@/enums/pageEnum";
|
||||||
// import { redirectErrorPage } from "@/utils";
|
// import { redirectErrorPage } from "@/utils";
|
||||||
import { storage } from "@/utils";
|
import { storage } from '@/utils';
|
||||||
import { StorageEnum } from "@/enums/storageEnum";
|
import { StorageEnum } from '@/enums/storageEnum';
|
||||||
|
|
||||||
const axiosInstance = axios.create({
|
const axiosInstance = axios.create({
|
||||||
baseURL: import.meta.env.DEV
|
baseURL: import.meta.env.DEV
|
||||||
@ -15,7 +15,8 @@ const axiosInstance = axios.create({
|
|||||||
// 请求拦截器
|
// 请求拦截器
|
||||||
axiosInstance.interceptors.request.use(
|
axiosInstance.interceptors.request.use(
|
||||||
(config) => {
|
(config) => {
|
||||||
config.headers.Authorization = storage.get(StorageEnum.ZS_ACCESS_TOKEN);
|
const TOKEN = storage.get(StorageEnum.ZS_ACCESS_TOKEN);
|
||||||
|
config.headers.Authorization = TOKEN;
|
||||||
return config;
|
return config;
|
||||||
},
|
},
|
||||||
(error: AxiosRequestConfig) => {
|
(error: AxiosRequestConfig) => {
|
||||||
@ -29,17 +30,17 @@ axiosInstance.interceptors.response.use(
|
|||||||
const { message, success } = res.data as IResponse;
|
const { message, success } = res.data as IResponse;
|
||||||
|
|
||||||
// 如果是文件流,直接过
|
// 如果是文件流,直接过
|
||||||
if (res.config.responseType === "blob") return Promise.resolve(res.data);
|
if (res.config.responseType === 'blob') return Promise.resolve(res.data);
|
||||||
if (success) return Promise.resolve(res.data);
|
if (success) return Promise.resolve(res.data);
|
||||||
// 如果 success 为 false,显示服务器返回的错误消息
|
// 如果 success 为 false,显示服务器返回的错误消息
|
||||||
window["$message"].error(message || "系统错误");
|
window['$message'].error(message || '系统错误');
|
||||||
|
|
||||||
// 重定向
|
// 重定向
|
||||||
// if (ErrorPageNameMap.get(status)) redirectErrorPage(status);
|
// if (ErrorPageNameMap.get(status)) redirectErrorPage(status);
|
||||||
return Promise.resolve(res.data);
|
return Promise.resolve(res.data);
|
||||||
},
|
},
|
||||||
(err: AxiosResponse) => {
|
(err: AxiosResponse) => {
|
||||||
window["$message"].error("接口异常,请检查");
|
window['$message'].error('接口异常,请检查');
|
||||||
return Promise.reject(err);
|
return Promise.reject(err);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
import axiosInstance from "./axios";
|
import axiosInstance from './axios';
|
||||||
import type {
|
import type {
|
||||||
RequestGlobalConfigType,
|
RequestGlobalConfigType,
|
||||||
RequestConfigType,
|
RequestConfigType,
|
||||||
} from "@/enums/httpEnum";
|
} from '@/enums/httpEnum';
|
||||||
import {
|
import {
|
||||||
RequestHttpEnum,
|
RequestHttpEnum,
|
||||||
ContentTypeEnum,
|
ContentTypeEnum,
|
||||||
@ -10,7 +10,7 @@ import {
|
|||||||
RequestDataTypeEnum,
|
RequestDataTypeEnum,
|
||||||
RequestContentTypeEnum,
|
RequestContentTypeEnum,
|
||||||
RequestParamsObjType,
|
RequestParamsObjType,
|
||||||
} from "@/enums/httpEnum";
|
} from '@/enums/httpEnum';
|
||||||
|
|
||||||
export const get = (url: string, params?: object) => {
|
export const get = (url: string, params?: object) => {
|
||||||
return axiosInstance({
|
return axiosInstance({
|
||||||
@ -26,7 +26,7 @@ export const post = (url: string, data?: object, headersType?: string) => {
|
|||||||
method: RequestHttpEnum.POST,
|
method: RequestHttpEnum.POST,
|
||||||
data: data,
|
data: data,
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": headersType || ContentTypeEnum.JSON,
|
'Content-Type': headersType || ContentTypeEnum.JSON,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@ -37,7 +37,7 @@ export const patch = (url: string, data?: object, headersType?: string) => {
|
|||||||
method: RequestHttpEnum.PATCH,
|
method: RequestHttpEnum.PATCH,
|
||||||
data: data,
|
data: data,
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": headersType || ContentTypeEnum.JSON,
|
'Content-Type': headersType || ContentTypeEnum.JSON,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@ -52,7 +52,7 @@ export const put = (
|
|||||||
method: RequestHttpEnum.PUT,
|
method: RequestHttpEnum.PUT,
|
||||||
data: data,
|
data: data,
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": headersType || ContentTypeEnum.JSON,
|
'Content-Type': headersType || ContentTypeEnum.JSON,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@ -147,7 +147,7 @@ export const customizeHttp = (
|
|||||||
const params: RequestParamsObjType = targetRequestParams.Params;
|
const params: RequestParamsObjType = targetRequestParams.Params;
|
||||||
// form 类型处理
|
// form 类型处理
|
||||||
const formData: FormData = new FormData();
|
const formData: FormData = new FormData();
|
||||||
formData.set("default", "defaultData");
|
formData.set('default', 'defaultData');
|
||||||
// 类型处理
|
// 类型处理
|
||||||
|
|
||||||
switch (requestParamsBodyType) {
|
switch (requestParamsBodyType) {
|
||||||
@ -155,28 +155,28 @@ export const customizeHttp = (
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case RequestBodyEnum.JSON:
|
case RequestBodyEnum.JSON:
|
||||||
headers["Content-Type"] = ContentTypeEnum.JSON;
|
headers['Content-Type'] = ContentTypeEnum.JSON;
|
||||||
data = JSON.parse(targetRequestParams.Body["json"]);
|
data = JSON.parse(targetRequestParams.Body['json']);
|
||||||
// json 赋值给 data
|
// json 赋值给 data
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RequestBodyEnum.XML:
|
case RequestBodyEnum.XML:
|
||||||
headers["Content-Type"] = ContentTypeEnum.XML;
|
headers['Content-Type'] = ContentTypeEnum.XML;
|
||||||
// xml 字符串赋值给 data
|
// xml 字符串赋值给 data
|
||||||
data = targetRequestParams.Body["xml"];
|
data = targetRequestParams.Body['xml'];
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RequestBodyEnum.X_WWW_FORM_URLENCODED:
|
case RequestBodyEnum.X_WWW_FORM_URLENCODED:
|
||||||
headers["Content-Type"] = ContentTypeEnum.FORM_URLENCODED;
|
headers['Content-Type'] = ContentTypeEnum.FORM_URLENCODED;
|
||||||
const bodyFormData = targetRequestParams.Body["x-www-form-urlencoded"];
|
const bodyFormData = targetRequestParams.Body['x-www-form-urlencoded'];
|
||||||
for (const i in bodyFormData) formData.set(i, bodyFormData[i]);
|
for (const i in bodyFormData) formData.set(i, bodyFormData[i]);
|
||||||
// FormData 赋值给 data
|
// FormData 赋值给 data
|
||||||
data = formData;
|
data = formData;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RequestBodyEnum.FORM_DATA:
|
case RequestBodyEnum.FORM_DATA:
|
||||||
headers["Content-Type"] = ContentTypeEnum.FORM_DATA;
|
headers['Content-Type'] = ContentTypeEnum.FORM_DATA;
|
||||||
const bodyFormUrlencoded = targetRequestParams.Body["form-data"];
|
const bodyFormUrlencoded = targetRequestParams.Body['form-data'];
|
||||||
for (const i in bodyFormUrlencoded) {
|
for (const i in bodyFormUrlencoded) {
|
||||||
formData.set(i, bodyFormUrlencoded[i]);
|
formData.set(i, bodyFormUrlencoded[i]);
|
||||||
}
|
}
|
||||||
@ -187,7 +187,7 @@ export const customizeHttp = (
|
|||||||
|
|
||||||
// sql 处理
|
// sql 处理
|
||||||
if (requestContentType === RequestContentTypeEnum.SQL) {
|
if (requestContentType === RequestContentTypeEnum.SQL) {
|
||||||
headers["Content-Type"] = ContentTypeEnum.JSON;
|
headers['Content-Type'] = ContentTypeEnum.JSON;
|
||||||
data = requestSQLContent;
|
data = requestSQLContent;
|
||||||
}
|
}
|
||||||
// 如果定义了 requestInterval 且请求次数未达到某个上限,则使用 setInterval 实现轮询
|
// 如果定义了 requestInterval 且请求次数未达到某个上限,则使用 setInterval 实现轮询
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { get, post } from "@/api/http";
|
import { get, post } from '@/api/http';
|
||||||
|
|
||||||
const fix = "/menu";
|
const fix = '/menu';
|
||||||
|
|
||||||
const url = {
|
const url = {
|
||||||
insert: `${fix}/insert`, // 分页查询
|
insert: `${fix}/insert`, // 分页查询
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { get, post } from "@/api/http";
|
import { get, post } from '@/api/http';
|
||||||
|
|
||||||
const fix = "/role";
|
const fix = '/role';
|
||||||
|
|
||||||
const url = {
|
const url = {
|
||||||
insert: `${fix}/insert`,
|
insert: `${fix}/insert`,
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { get, post } from "@/api/http";
|
import { get, post } from '@/api/http';
|
||||||
|
|
||||||
const fix = "/roleMenu";
|
const fix = '/roleMenu';
|
||||||
|
|
||||||
const url = {
|
const url = {
|
||||||
queryMenuByRole: `${fix}/queryMenuByRole`, // 根据角色查询菜单
|
queryMenuByRole: `${fix}/queryMenuByRole`, // 根据角色查询菜单
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { get, post } from "@/api/http";
|
import { get, post } from '@/api/http';
|
||||||
|
|
||||||
const fix = "/user";
|
const fix = '/user';
|
||||||
|
|
||||||
const url = {
|
const url = {
|
||||||
login: `${fix}/login`,
|
login: `${fix}/login`,
|
||||||
@ -9,6 +9,8 @@ const url = {
|
|||||||
page: `${fix}/page`,
|
page: `${fix}/page`,
|
||||||
update: `${fix}/update`,
|
update: `${fix}/update`,
|
||||||
delete: `${fix}/delete`,
|
delete: `${fix}/delete`,
|
||||||
|
updateSystemSetting: `/userStyleSetting/update`,
|
||||||
|
getUserStyleSetting: `/userStyleSetting/get`,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const login = (params: Object) => {
|
export const login = (params: Object) => {
|
||||||
@ -34,3 +36,11 @@ export const updateUser = (params: Object) => {
|
|||||||
export const deleteUser = (params: Object) => {
|
export const deleteUser = (params: Object) => {
|
||||||
return post(url.delete, params);
|
return post(url.delete, params);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const setSystemSetting = (params: Object) => {
|
||||||
|
return post(url.updateSystemSetting, params);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getUserStyleSetting = () => {
|
||||||
|
return get(url.getUserStyleSetting);
|
||||||
|
};
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
// 用户角色关系
|
// 用户角色关系
|
||||||
import { get, post } from "@/api/http";
|
import { get, post } from '@/api/http';
|
||||||
const fix = "/userRole";
|
const fix = '/userRole';
|
||||||
const url = {
|
const url = {
|
||||||
save: `${fix}/save`,
|
save: `${fix}/save`,
|
||||||
queryMenuTree: `${fix}/queryMenuTree `,
|
queryMenuTree: `${fix}/queryMenuTree `,
|
||||||
|
|||||||
49
src/assets/images/header-theme-dark.svg
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg width="52px" height="45px" viewBox="0 0 52 45" version="1.1" xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
|
<!-- Generator: Sketch 50.2 (55047) - http://www.bohemiancoding.com/sketch -->
|
||||||
|
<title>Group 5 Copy 5</title>
|
||||||
|
<desc>Created with Sketch.</desc>
|
||||||
|
<defs>
|
||||||
|
<filter x="-9.4%" y="-6.2%" width="118.8%" height="122.5%" filterUnits="objectBoundingBox"
|
||||||
|
id="filter-1">
|
||||||
|
<feOffset dx="0" dy="1" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
|
||||||
|
<feGaussianBlur stdDeviation="1" in="shadowOffsetOuter1"
|
||||||
|
result="shadowBlurOuter1"></feGaussianBlur>
|
||||||
|
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.15 0" type="matrix"
|
||||||
|
in="shadowBlurOuter1" result="shadowMatrixOuter1"></feColorMatrix>
|
||||||
|
<feMerge>
|
||||||
|
<feMergeNode in="shadowMatrixOuter1"></feMergeNode>
|
||||||
|
<feMergeNode in="SourceGraphic"></feMergeNode>
|
||||||
|
</feMerge>
|
||||||
|
</filter>
|
||||||
|
<rect id="path-2" x="0" y="0" width="48" height="40" rx="4"></rect>
|
||||||
|
<filter x="-4.2%" y="-2.5%" width="108.3%" height="110.0%" filterUnits="objectBoundingBox"
|
||||||
|
id="filter-4">
|
||||||
|
<feOffset dx="0" dy="1" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
|
||||||
|
<feGaussianBlur stdDeviation="0.5" in="shadowOffsetOuter1"
|
||||||
|
result="shadowBlurOuter1"></feGaussianBlur>
|
||||||
|
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.1 0" type="matrix"
|
||||||
|
in="shadowBlurOuter1"></feColorMatrix>
|
||||||
|
</filter>
|
||||||
|
</defs>
|
||||||
|
<g id="配置面板" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||||
|
<g id="setting-copy-2" transform="translate(-1190.000000, -136.000000)">
|
||||||
|
<g id="Group-8" transform="translate(1167.000000, 0.000000)">
|
||||||
|
<g id="Group-5-Copy-5" filter="url(#filter-1)" transform="translate(25.000000, 137.000000)">
|
||||||
|
<mask id="mask-3" fill="white">
|
||||||
|
<use xlink:href="#path-2"></use>
|
||||||
|
</mask>
|
||||||
|
<g id="Rectangle-18">
|
||||||
|
<use fill="black" fill-opacity="1" filter="url(#filter-4)" xlink:href="#path-2"></use>
|
||||||
|
<use fill="#F0F2F5" fill-rule="evenodd" xlink:href="#path-2"></use>
|
||||||
|
</g>
|
||||||
|
<rect id="Rectangle-11" fill="#303648" mask="url(#mask-3)" x="-1" y="0" width="49"
|
||||||
|
height="10"></rect>
|
||||||
|
<rect id="Rectangle-18" fill="#303648" mask="url(#mask-3)" x="0" y="0" width="16"
|
||||||
|
height="44"></rect>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.8 KiB |
15
src/assets/images/nav-horizontal-mix.svg
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="52px" height="45px" viewBox="0 0 52 45" enable-background="new 0 0 52 45" xml:space="preserve"> <image id="image0" width="52" height="45" x="0" y="0"
|
||||||
|
href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADQAAAAtCAMAAADWf7iKAAAABGdBTUEAALGPC/xhBQAAACBjSFJN
|
||||||
|
AAB6JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAAAdVBMVEX///8AAABkbnc9RVY7
|
||||||
|
QE9fY3GIiJZjbHk9QFOCi5QAAAA9QlZfZHMAAAA4QFEAAADt7/Lf5OTt7/KChoYAAADu8PTf3+Nw
|
||||||
|
c3MAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAyNUkyOEn////w8vWhURXFAAAAI3RS
|
||||||
|
TlMAAE/2/uZJUP45AvfkBP4F9LrwPwP0vUkOAREsNjk0JwYHCLrjEiIAAAABYktHRACIBR1IAAAA
|
||||||
|
CXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH5QgGAhE5kB5L+gAAAIZJREFUSMft1rsSgjAQhWEW
|
||||||
|
FTUYIopKlPui7/+IZqFhbMxmhm7//qvPiaKgAOIN+rbdJQCE9qO3cR2OhFTKMYgn5ZDmGcy0Q4aJ
|
||||||
|
0AhaoPdPnz8JEiRIkKCV0DkE5YQ0D12uBQ01B93uj9LSJdDPV1V71rRlMf0Iq7p+8Kw3aj4fAJYR
|
||||||
|
TCigL0lMJ5P4y7LRAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDIxLTA4LTA2VDAyOjE3OjU2KzAwOjAw
|
||||||
|
Kbo8/wAAACV0RVh0ZGF0ZTptb2RpZnkAMjAyMS0wOC0wNlQwMjoxNzo1NiswMDowMFjnhEMAAAAA
|
||||||
|
SUVORK5CYII=" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.2 KiB |
26
src/assets/images/nav-horizontal.svg
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="52px" height="45px"
|
||||||
|
viewBox="0 0 52 45" enable-background="new 0 0 52 45" xml:space="preserve"> <image id="image0"
|
||||||
|
width="52"
|
||||||
|
height="45"
|
||||||
|
x="0" y="0"
|
||||||
|
href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADQAAAAtCAMAAADWf7iKAAAABGdBTUEAALGPC/xhBQAAACBjSFJN
|
||||||
|
AAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAkFBMVEX+/v78/Pz6+vr39/f0
|
||||||
|
9PTx8fHv7+/u7u729vbHyc1ESVsxNkgwNkg7QFFscH3T1NfGyMwxN0k9QlPa3N5vdID4+Pjz8/Pr
|
||||||
|
6+s6QVLw8PDm5ubk5OTy8vLj4+Pt7e3u8PPw8vXu8PTn5+fw8fXs7Oz5+fnp6ene3t7b29vc3Nzf
|
||||||
|
39/q6ur7+/vo6Oj9/f3///855aJlAAAAAWJLR0QvI9QgEQAAAAd0SU1FB+UHAxEtKCKzaD0AAAC8
|
||||||
|
SURBVEjH7dbLGoIgEIbhEUHI6JyYgmWWnTzd/92FTzcws2vBt38f2P0DELGYi0SiSgSPWQTeqEW6
|
||||||
|
1MhW643yiqntDkvm9gfFIBYpxWh9FBmYHP23X6dcQVHSjNYlB2mpyDqQFRVVDs7/ji41rYACCiig
|
||||||
|
gHBIWiqyxo/alYga7ufzRkR35YeaaGrX+pOgeFTPF673x3Yi7ueDxcluKDE1Qy5N1o8AY98qbgQm
|
||||||
|
Z+Yrx5sJxqhnrEXF2PzM9AV+UvDCWyYgmAAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAyMS0wNy0wM1Qw
|
||||||
|
OTo0NTo0MCswODowMOjZqbAAAAAldEVYdGRhdGU6bW9kaWZ5ADIwMjEtMDctMDNUMDk6NDU6NDAr
|
||||||
|
MDg6MDCZhBEMAAAAIHRFWHRzb2Z0d2FyZQBodHRwczovL2ltYWdlbWFnaWNrLm9yZ7zPHZ0AAAAY
|
||||||
|
dEVYdFRodW1iOjpEb2N1bWVudDo6UGFnZXMAMaf/uy8AAAAXdEVYdFRodW1iOjpJbWFnZTo6SGVp
|
||||||
|
Z2h0ADQ1+dH7kAAAABZ0RVh0VGh1bWI6OkltYWdlOjpXaWR0aAA1MoYBn/8AAAAZdEVYdFRodW1i
|
||||||
|
OjpNaW1ldHlwZQBpbWFnZS9wbmc/slZOAAAAF3RFWHRUaHVtYjo6TVRpbWUAMTYyNTI3Njc0MKmy
|
||||||
|
pv8AAAASdEVYdFRodW1iOjpTaXplADEzMzJCQoe7yB0AAABGdEVYdFRodW1iOjpVUkkAZmlsZTov
|
||||||
|
Ly9hcHAvdG1wL2ltYWdlbGMvaW1ndmlldzJfOV8xNjIzOTEyMDA2MDA1NDY4Ml8yMl9bMF2ZTW7W
|
||||||
|
AAAAAElFTkSuQmCC"></image>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.0 KiB |
49
src/assets/images/nav-theme-dark.svg
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg width="52px" height="45px" viewBox="0 0 52 45" version="1.1" xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
|
<!-- Generator: Sketch 50.2 (55047) - http://www.bohemiancoding.com/sketch -->
|
||||||
|
<title>Group 5 Copy 5</title>
|
||||||
|
<desc>Created with Sketch.</desc>
|
||||||
|
<defs>
|
||||||
|
<filter x="-9.4%" y="-6.2%" width="118.8%" height="122.5%" filterUnits="objectBoundingBox"
|
||||||
|
id="filter-1">
|
||||||
|
<feOffset dx="0" dy="1" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
|
||||||
|
<feGaussianBlur stdDeviation="1" in="shadowOffsetOuter1"
|
||||||
|
result="shadowBlurOuter1"></feGaussianBlur>
|
||||||
|
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.15 0" type="matrix"
|
||||||
|
in="shadowBlurOuter1" result="shadowMatrixOuter1"></feColorMatrix>
|
||||||
|
<feMerge>
|
||||||
|
<feMergeNode in="shadowMatrixOuter1"></feMergeNode>
|
||||||
|
<feMergeNode in="SourceGraphic"></feMergeNode>
|
||||||
|
</feMerge>
|
||||||
|
</filter>
|
||||||
|
<rect id="path-2" x="0" y="0" width="48" height="40" rx="4"></rect>
|
||||||
|
<filter x="-4.2%" y="-2.5%" width="108.3%" height="110.0%" filterUnits="objectBoundingBox"
|
||||||
|
id="filter-4">
|
||||||
|
<feOffset dx="0" dy="1" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
|
||||||
|
<feGaussianBlur stdDeviation="0.5" in="shadowOffsetOuter1"
|
||||||
|
result="shadowBlurOuter1"></feGaussianBlur>
|
||||||
|
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.1 0" type="matrix"
|
||||||
|
in="shadowBlurOuter1"></feColorMatrix>
|
||||||
|
</filter>
|
||||||
|
</defs>
|
||||||
|
<g id="配置面板" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||||
|
<g id="setting-copy-2" transform="translate(-1190.000000, -136.000000)">
|
||||||
|
<g id="Group-8" transform="translate(1167.000000, 0.000000)">
|
||||||
|
<g id="Group-5-Copy-5" filter="url(#filter-1)" transform="translate(25.000000, 137.000000)">
|
||||||
|
<mask id="mask-3" fill="white">
|
||||||
|
<use xlink:href="#path-2"></use>
|
||||||
|
</mask>
|
||||||
|
<g id="Rectangle-18">
|
||||||
|
<use fill="black" fill-opacity="1" filter="url(#filter-4)" xlink:href="#path-2"></use>
|
||||||
|
<use fill="#F0F2F5" fill-rule="evenodd" xlink:href="#path-2"></use>
|
||||||
|
</g>
|
||||||
|
<rect id="Rectangle-11" fill="#FFFFFF" mask="url(#mask-3)" x="-1" y="0" width="49"
|
||||||
|
height="10"></rect>
|
||||||
|
<rect id="Rectangle-18" fill="#303648" mask="url(#mask-3)" x="0" y="0" width="16"
|
||||||
|
height="44"></rect>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.8 KiB |
49
src/assets/images/nav-theme-light.svg
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg width="52px" height="45px" viewBox="0 0 52 45" version="1.1" xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
|
<!-- Generator: Sketch 50.2 (55047) - http://www.bohemiancoding.com/sketch -->
|
||||||
|
<title>Group 5</title>
|
||||||
|
<desc>Created with Sketch.</desc>
|
||||||
|
<defs>
|
||||||
|
<filter x="-9.4%" y="-6.2%" width="118.8%" height="122.5%" filterUnits="objectBoundingBox"
|
||||||
|
id="filter-1">
|
||||||
|
<feOffset dx="0" dy="1" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
|
||||||
|
<feGaussianBlur stdDeviation="1" in="shadowOffsetOuter1"
|
||||||
|
result="shadowBlurOuter1"></feGaussianBlur>
|
||||||
|
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.15 0" type="matrix"
|
||||||
|
in="shadowBlurOuter1" result="shadowMatrixOuter1"></feColorMatrix>
|
||||||
|
<feMerge>
|
||||||
|
<feMergeNode in="shadowMatrixOuter1"></feMergeNode>
|
||||||
|
<feMergeNode in="SourceGraphic"></feMergeNode>
|
||||||
|
</feMerge>
|
||||||
|
</filter>
|
||||||
|
<rect id="path-2" x="0" y="0" width="48" height="40" rx="4"></rect>
|
||||||
|
<filter x="-4.2%" y="-2.5%" width="108.3%" height="110.0%" filterUnits="objectBoundingBox"
|
||||||
|
id="filter-4">
|
||||||
|
<feOffset dx="0" dy="1" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
|
||||||
|
<feGaussianBlur stdDeviation="0.5" in="shadowOffsetOuter1"
|
||||||
|
result="shadowBlurOuter1"></feGaussianBlur>
|
||||||
|
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.1 0" type="matrix"
|
||||||
|
in="shadowBlurOuter1"></feColorMatrix>
|
||||||
|
</filter>
|
||||||
|
</defs>
|
||||||
|
<g id="配置面板" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||||
|
<g id="setting-copy-2" transform="translate(-1254.000000, -136.000000)">
|
||||||
|
<g id="Group-8" transform="translate(1167.000000, 0.000000)">
|
||||||
|
<g id="Group-5" filter="url(#filter-1)" transform="translate(89.000000, 137.000000)">
|
||||||
|
<mask id="mask-3" fill="white">
|
||||||
|
<use xlink:href="#path-2"></use>
|
||||||
|
</mask>
|
||||||
|
<g id="Rectangle-18">
|
||||||
|
<use fill="black" fill-opacity="1" filter="url(#filter-4)" xlink:href="#path-2"></use>
|
||||||
|
<use fill="#F0F2F5" fill-rule="evenodd" xlink:href="#path-2"></use>
|
||||||
|
</g>
|
||||||
|
<rect id="Rectangle-18" fill="#FFFFFF" mask="url(#mask-3)" x="0" y="0" width="16"
|
||||||
|
height="44"></rect>
|
||||||
|
<rect id="Rectangle-11" fill="#FFFFFF" mask="url(#mask-3)" x="-1" y="0" width="49"
|
||||||
|
height="10"></rect>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.8 KiB |
2915
src/assets/svgs/logo-text.svg
Normal file
|
After Width: | Height: | Size: 212 KiB |
2887
src/assets/svgs/logo.svg
Normal file
|
After Width: | Height: | Size: 211 KiB |
30
src/components/Application/Application.vue
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
<template>
|
||||||
|
<n-dialog-provider>
|
||||||
|
<n-notification-provider>
|
||||||
|
<n-message-provider>
|
||||||
|
<slot name="default"></slot>
|
||||||
|
</n-message-provider>
|
||||||
|
</n-notification-provider>
|
||||||
|
</n-dialog-provider>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent } from 'vue';
|
||||||
|
import {
|
||||||
|
NDialogProvider,
|
||||||
|
NNotificationProvider,
|
||||||
|
NMessageProvider,
|
||||||
|
} from 'naive-ui';
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'Application',
|
||||||
|
components: {
|
||||||
|
NDialogProvider,
|
||||||
|
NNotificationProvider,
|
||||||
|
NMessageProvider,
|
||||||
|
},
|
||||||
|
setup() {
|
||||||
|
return {};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
3
src/components/Application/index.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import AppProvider from './Application.vue';
|
||||||
|
|
||||||
|
export { AppProvider };
|
||||||
@ -1,29 +1,29 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import * as echarts from "echarts";
|
import * as echarts from 'echarts';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
id: {
|
id: {
|
||||||
type: String,
|
type: String,
|
||||||
default: "histogram",
|
default: 'histogram',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
let xLabel = ["09/01", "09/02", "09/03", "09/04", "09/05"];
|
let xLabel = ['09/01', '09/02', '09/03', '09/04', '09/05'];
|
||||||
|
|
||||||
let yLabel = [20, 80, 100, 40, 34, 90, 60];
|
let yLabel = [20, 80, 100, 40, 34, 90, 60];
|
||||||
|
|
||||||
const options = {
|
const options = {
|
||||||
// animation: false,
|
// animation: false,
|
||||||
grid: {
|
grid: {
|
||||||
top: "18%",
|
top: '18%',
|
||||||
bottom: "20%", //也可设置left和right设置距离来控制图表的大小
|
bottom: '20%', //也可设置left和right设置距离来控制图表的大小
|
||||||
},
|
},
|
||||||
xAxis: {
|
xAxis: {
|
||||||
data: xLabel,
|
data: xLabel,
|
||||||
axisLine: {
|
axisLine: {
|
||||||
show: true, //隐藏X轴轴线
|
show: true, //隐藏X轴轴线
|
||||||
lineStyle: {
|
lineStyle: {
|
||||||
color: "#11417a",
|
color: '#11417a',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
axisTick: {
|
axisTick: {
|
||||||
@ -34,13 +34,13 @@ const options = {
|
|||||||
// margin: 14,
|
// margin: 14,
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
textStyle: {
|
textStyle: {
|
||||||
color: "#fff", //X轴文字颜色
|
color: '#fff', //X轴文字颜色
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
yAxis: [
|
yAxis: [
|
||||||
{
|
{
|
||||||
type: "value",
|
type: 'value',
|
||||||
gridIndex: 0,
|
gridIndex: 0,
|
||||||
min: 0,
|
min: 0,
|
||||||
max: 100,
|
max: 100,
|
||||||
@ -49,8 +49,8 @@ const options = {
|
|||||||
splitLine: {
|
splitLine: {
|
||||||
show: true,
|
show: true,
|
||||||
lineStyle: {
|
lineStyle: {
|
||||||
color: "rgba(255, 255, 255, .2)",
|
color: 'rgba(255, 255, 255, .2)',
|
||||||
type: "dashed",
|
type: 'dashed',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
axisTick: {
|
axisTick: {
|
||||||
@ -59,7 +59,7 @@ const options = {
|
|||||||
axisLine: {
|
axisLine: {
|
||||||
show: true,
|
show: true,
|
||||||
lineStyle: {
|
lineStyle: {
|
||||||
color: "#63273242",
|
color: '#63273242',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
axisLabel: {
|
axisLabel: {
|
||||||
@ -67,26 +67,26 @@ const options = {
|
|||||||
// margin: 14,
|
// margin: 14,
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
textStyle: {
|
textStyle: {
|
||||||
color: "#fff", //X轴文字颜色
|
color: '#fff', //X轴文字颜色
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
series: [
|
series: [
|
||||||
{
|
{
|
||||||
name: "",
|
name: '',
|
||||||
type: "bar",
|
type: 'bar',
|
||||||
barWidth: 20,
|
barWidth: 20,
|
||||||
itemStyle: {
|
itemStyle: {
|
||||||
normal: {
|
normal: {
|
||||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||||
{
|
{
|
||||||
offset: 1,
|
offset: 1,
|
||||||
color: "rgba(79, 203, 255, 1)",
|
color: 'rgba(79, 203, 255, 1)',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
offset: 0,
|
offset: 0,
|
||||||
color: "rgba(140, 225, 251,1)",
|
color: 'rgba(140, 225, 251,1)',
|
||||||
},
|
},
|
||||||
]),
|
]),
|
||||||
},
|
},
|
||||||
@ -96,27 +96,27 @@ const options = {
|
|||||||
zlevel: 0,
|
zlevel: 0,
|
||||||
label: {
|
label: {
|
||||||
show: true,
|
show: true,
|
||||||
position: "top",
|
position: 'top',
|
||||||
// distance: 10,
|
// distance: 10,
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
color: "#fff",
|
color: '#fff',
|
||||||
// backgroundColor: "#0f375f",
|
// backgroundColor: "#0f375f",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// 分隔
|
// 分隔
|
||||||
type: "pictorialBar",
|
type: 'pictorialBar',
|
||||||
itemStyle: {
|
itemStyle: {
|
||||||
normal: {
|
normal: {
|
||||||
color: "#0F375F",
|
color: '#0F375F',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
symbolRepeat: "fixed",
|
symbolRepeat: 'fixed',
|
||||||
symbolMargin: 6,
|
symbolMargin: 6,
|
||||||
symbol: "rect",
|
symbol: 'rect',
|
||||||
symbolClip: true,
|
symbolClip: true,
|
||||||
symbolSize: [20, 2],
|
symbolSize: [20, 2],
|
||||||
symbolPosition: "start",
|
symbolPosition: 'start',
|
||||||
symbolOffset: [0, -1],
|
symbolOffset: [0, -1],
|
||||||
// symbolBoundingData: this.total,
|
// symbolBoundingData: this.total,
|
||||||
data: yLabel,
|
data: yLabel,
|
||||||
@ -126,7 +126,7 @@ const options = {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
const chart = ref<any>("");
|
const chart = ref<any>('');
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
// 图表初始化
|
// 图表初始化
|
||||||
@ -137,7 +137,7 @@ onMounted(() => {
|
|||||||
chart.value.setOption(options);
|
chart.value.setOption(options);
|
||||||
|
|
||||||
// 大小自适应
|
// 大小自适应
|
||||||
window.addEventListener("resize", () => {
|
window.addEventListener('resize', () => {
|
||||||
chart.value.resize();
|
chart.value.resize();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,56 +1,56 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import * as echarts from "echarts";
|
import * as echarts from 'echarts';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
id: {
|
id: {
|
||||||
type: String,
|
type: String,
|
||||||
default: "lineChart",
|
default: 'lineChart',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const colors = "RGBA(30, 214, 255,";
|
const colors = 'RGBA(30, 214, 255,';
|
||||||
|
|
||||||
const options = {
|
const options = {
|
||||||
color: [colors + "1)"],
|
color: [colors + '1)'],
|
||||||
tooltip: {
|
tooltip: {
|
||||||
trigger: "axis",
|
trigger: 'axis',
|
||||||
backgroundColor: "#4B4F52",
|
backgroundColor: '#4B4F52',
|
||||||
borderColor: "#4B4F52",
|
borderColor: '#4B4F52',
|
||||||
padding: 8,
|
padding: 8,
|
||||||
textStyle: {
|
textStyle: {
|
||||||
color: "#fff",
|
color: '#fff',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
grid: {
|
grid: {
|
||||||
left: "3%",
|
left: '3%',
|
||||||
right: "4%",
|
right: '4%',
|
||||||
bottom: "10%",
|
bottom: '10%',
|
||||||
top: "10%",
|
top: '10%',
|
||||||
containLabel: true,
|
containLabel: true,
|
||||||
},
|
},
|
||||||
xAxis: {
|
xAxis: {
|
||||||
type: "category",
|
type: 'category',
|
||||||
boundaryGap: false,
|
boundaryGap: false,
|
||||||
axisLabel: {
|
axisLabel: {
|
||||||
textStyle: {
|
textStyle: {
|
||||||
color: "#ffffff", //更改坐标轴文字颜色
|
color: '#ffffff', //更改坐标轴文字颜色
|
||||||
fontSize: 12, //更改坐标轴文字大小
|
fontSize: 12, //更改坐标轴文字大小
|
||||||
// fontFamily: "SourceHanSansCN",
|
// fontFamily: "SourceHanSansCN",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
data: ["00:00", "01:00", "02:00", "03:00"],
|
data: ['00:00', '01:00', '02:00', '03:00'],
|
||||||
},
|
},
|
||||||
yAxis: {
|
yAxis: {
|
||||||
splitLine: {
|
splitLine: {
|
||||||
show: true,
|
show: true,
|
||||||
lineStyle: {
|
lineStyle: {
|
||||||
color: "rgba(255, 255, 255, .2)",
|
color: 'rgba(255, 255, 255, .2)',
|
||||||
type: "dashed",
|
type: 'dashed',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
axisLabel: {
|
axisLabel: {
|
||||||
textStyle: {
|
textStyle: {
|
||||||
color: "#ffffff", //更改坐标轴文字颜色
|
color: '#ffffff', //更改坐标轴文字颜色
|
||||||
fontSize: 12, //更改坐标轴文字大小
|
fontSize: 12, //更改坐标轴文字大小
|
||||||
// fontFamily: "SourceHanSansCN",
|
// fontFamily: "SourceHanSansCN",
|
||||||
},
|
},
|
||||||
@ -58,19 +58,19 @@ const options = {
|
|||||||
},
|
},
|
||||||
series: [
|
series: [
|
||||||
{
|
{
|
||||||
name: "",
|
name: '',
|
||||||
type: "line",
|
type: 'line',
|
||||||
showSymbol: false,
|
showSymbol: false,
|
||||||
|
|
||||||
areaStyle: {
|
areaStyle: {
|
||||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||||
{
|
{
|
||||||
offset: 0,
|
offset: 0,
|
||||||
color: colors + "0.6)",
|
color: colors + '0.6)',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
offset: 1,
|
offset: 1,
|
||||||
color: colors + "0)",
|
color: colors + '0)',
|
||||||
},
|
},
|
||||||
]),
|
]),
|
||||||
},
|
},
|
||||||
@ -79,7 +79,7 @@ const options = {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
const chart = ref<any>("");
|
const chart = ref<any>('');
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
// 图表初始化
|
// 图表初始化
|
||||||
@ -90,7 +90,7 @@ onMounted(() => {
|
|||||||
chart.value.setOption(options);
|
chart.value.setOption(options);
|
||||||
|
|
||||||
// 大小自适应
|
// 大小自适应
|
||||||
window.addEventListener("resize", () => {
|
window.addEventListener('resize', () => {
|
||||||
chart.value.resize();
|
chart.value.resize();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import * as echarts from "echarts";
|
import * as echarts from 'echarts';
|
||||||
|
|
||||||
interface ChartDataType {
|
interface ChartDataType {
|
||||||
name: string;
|
name: string;
|
||||||
@ -11,14 +11,14 @@ const chartObj = reactive({
|
|||||||
online: 0,
|
online: 0,
|
||||||
offline: 0,
|
offline: 0,
|
||||||
rate: 0,
|
rate: 0,
|
||||||
onlineIcon: "",
|
onlineIcon: '',
|
||||||
offlineIcon: "",
|
offlineIcon: '',
|
||||||
});
|
});
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
id: {
|
id: {
|
||||||
type: String,
|
type: String,
|
||||||
default: "barChart",
|
default: 'barChart',
|
||||||
},
|
},
|
||||||
|
|
||||||
chartData: {
|
chartData: {
|
||||||
@ -44,29 +44,29 @@ watch(
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const isColor = ["rgba(79, 203, 255, 1)", "rgba(140, 225, 251,1)"];
|
const isColor = ['rgba(79, 203, 255, 1)', 'rgba(140, 225, 251,1)'];
|
||||||
|
|
||||||
const options = {
|
const options = {
|
||||||
legend: {
|
legend: {
|
||||||
show: false,
|
show: false,
|
||||||
},
|
},
|
||||||
title: {
|
title: {
|
||||||
text: "{a|" + chartObj.rate + "}{c|%}",
|
text: '{a|' + chartObj.rate + '}{c|%}',
|
||||||
x: "40.5%",
|
x: '40.5%',
|
||||||
y: "40%",
|
y: '40%',
|
||||||
textStyle: {
|
textStyle: {
|
||||||
rich: {
|
rich: {
|
||||||
a: {
|
a: {
|
||||||
fontSize: 29, //环内数据字体大小
|
fontSize: 29, //环内数据字体大小
|
||||||
color: "#dbe2ea",
|
color: '#dbe2ea',
|
||||||
fontWeight: "bold",
|
fontWeight: 'bold',
|
||||||
fontFamily: "SourceHanSansCN",
|
fontFamily: 'SourceHanSansCN',
|
||||||
},
|
},
|
||||||
c: {
|
c: {
|
||||||
fontSize: 15,
|
fontSize: 15,
|
||||||
color: "#dbe2ea",
|
color: '#dbe2ea',
|
||||||
fontWeight: "500",
|
fontWeight: '500',
|
||||||
fontFamily: "SourceHanSansCN",
|
fontFamily: 'SourceHanSansCN',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -74,10 +74,10 @@ const options = {
|
|||||||
series: [
|
series: [
|
||||||
{
|
{
|
||||||
legendHoverLink: false, //只需要将这个给 false 即可
|
legendHoverLink: false, //只需要将这个给 false 即可
|
||||||
center: ["50.1%", "50%"],
|
center: ['50.1%', '50%'],
|
||||||
name: "",
|
name: '',
|
||||||
type: "pie",
|
type: 'pie',
|
||||||
radius: ["85%", "75%"],
|
radius: ['85%', '75%'],
|
||||||
silent: true,
|
silent: true,
|
||||||
clockwise: true,
|
clockwise: true,
|
||||||
startAngle: 90,
|
startAngle: 90,
|
||||||
@ -85,13 +85,13 @@ const options = {
|
|||||||
zlevel: 0,
|
zlevel: 0,
|
||||||
label: {
|
label: {
|
||||||
normal: {
|
normal: {
|
||||||
position: "center",
|
position: 'center',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
data: [
|
data: [
|
||||||
{
|
{
|
||||||
value: chartObj.online,
|
value: chartObj.online,
|
||||||
name: "在线",
|
name: '在线',
|
||||||
label: {
|
label: {
|
||||||
normal: {
|
normal: {
|
||||||
show: false,
|
show: false,
|
||||||
@ -117,7 +117,7 @@ const options = {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: chartObj.offline,
|
value: chartObj.offline,
|
||||||
name: "离线",
|
name: '离线',
|
||||||
label: {
|
label: {
|
||||||
normal: {
|
normal: {
|
||||||
show: false,
|
show: false,
|
||||||
@ -125,7 +125,7 @@ const options = {
|
|||||||
},
|
},
|
||||||
itemStyle: {
|
itemStyle: {
|
||||||
normal: {
|
normal: {
|
||||||
color: "rgba(86, 118, 139, 1)",
|
color: 'rgba(86, 118, 139, 1)',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -133,7 +133,7 @@ const options = {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
const chart = ref<any>("");
|
const chart = ref<any>('');
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
// 图表初始化
|
// 图表初始化
|
||||||
@ -144,7 +144,7 @@ onMounted(() => {
|
|||||||
chart.value.setOption(options);
|
chart.value.setOption(options);
|
||||||
|
|
||||||
// 大小自适应
|
// 大小自适应
|
||||||
window.addEventListener("resize", () => {
|
window.addEventListener('resize', () => {
|
||||||
chart.value.resize();
|
chart.value.resize();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
export { default as BasicForm } from "./src/BasicForm2.vue";
|
export { default as BasicForm } from './src/BasicForm2.vue';
|
||||||
export { useForm } from "./src/hooks/useForm";
|
export { useForm } from './src/hooks/useForm';
|
||||||
export * from "./src/types/form";
|
export * from './src/types/form';
|
||||||
export * from "./src/types/index";
|
export * from './src/types/index';
|
||||||
|
|||||||
@ -117,7 +117,7 @@
|
|||||||
<UpOutlined />
|
<UpOutlined />
|
||||||
</n-icon>
|
</n-icon>
|
||||||
</template>
|
</template>
|
||||||
{{ overflow ? "展开" : "收起" }}
|
{{ overflow ? '展开' : '收起' }}
|
||||||
</n-button>
|
</n-button>
|
||||||
</n-space>
|
</n-space>
|
||||||
</n-gi>
|
</n-gi>
|
||||||
@ -134,27 +134,27 @@ import {
|
|||||||
unref,
|
unref,
|
||||||
onMounted,
|
onMounted,
|
||||||
watch,
|
watch,
|
||||||
} from "vue";
|
} from 'vue';
|
||||||
import { createPlaceholderMessage } from "./helper";
|
import { createPlaceholderMessage } from './helper';
|
||||||
import { useFormEvents } from "./hooks/useFormEvents";
|
import { useFormEvents } from './hooks/useFormEvents';
|
||||||
import { useFormValues } from "./hooks/useFormValues";
|
import { useFormValues } from './hooks/useFormValues';
|
||||||
|
|
||||||
import { basicProps } from "./props";
|
import { basicProps } from './props';
|
||||||
import { DownOutlined, UpOutlined, QuestionCircleOutlined } from "@vicons/antd";
|
import { DownOutlined, UpOutlined, QuestionCircleOutlined } from '@vicons/antd';
|
||||||
|
|
||||||
import type { Ref } from "vue";
|
import type { Ref } from 'vue';
|
||||||
import type { GridProps } from "naive-ui/lib/grid";
|
import type { GridProps } from 'naive-ui/lib/grid';
|
||||||
import type { FormSchema, FormProps, FormActionType } from "./types/form";
|
import type { FormSchema, FormProps, FormActionType } from './types/form';
|
||||||
|
|
||||||
import { isArray, deepMerge } from "@/utils";
|
import { isArray, deepMerge } from '@/utils';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: "BasicForm",
|
name: 'BasicForm',
|
||||||
components: { DownOutlined, UpOutlined, QuestionCircleOutlined },
|
components: { DownOutlined, UpOutlined, QuestionCircleOutlined },
|
||||||
props: {
|
props: {
|
||||||
...basicProps,
|
...basicProps,
|
||||||
},
|
},
|
||||||
emits: ["reset", "submit", "register"],
|
emits: ['reset', 'submit', 'register'],
|
||||||
setup(props, { emit, attrs }) {
|
setup(props, { emit, attrs }) {
|
||||||
const defaultFormModel = ref<any>({});
|
const defaultFormModel = ref<any>({});
|
||||||
const formModel = reactive<any>({});
|
const formModel = reactive<any>({});
|
||||||
@ -169,7 +169,7 @@ export default defineComponent({
|
|||||||
return Object.assign(
|
return Object.assign(
|
||||||
{
|
{
|
||||||
size: props.size,
|
size: props.size,
|
||||||
type: "primary",
|
type: 'primary',
|
||||||
},
|
},
|
||||||
props.submitButtonOptions
|
props.submitButtonOptions
|
||||||
);
|
);
|
||||||
@ -179,7 +179,7 @@ export default defineComponent({
|
|||||||
return Object.assign(
|
return Object.assign(
|
||||||
{
|
{
|
||||||
size: props.size,
|
size: props.size,
|
||||||
type: "default",
|
type: 'default',
|
||||||
},
|
},
|
||||||
props.resetButtonOptions
|
props.resetButtonOptions
|
||||||
);
|
);
|
||||||
@ -211,7 +211,7 @@ export default defineComponent({
|
|||||||
|
|
||||||
const isInline = computed(() => {
|
const isInline = computed(() => {
|
||||||
const { layout } = unref(getProps);
|
const { layout } = unref(getProps);
|
||||||
return layout === "inline";
|
return layout === 'inline';
|
||||||
});
|
});
|
||||||
|
|
||||||
const getGrid = computed((): GridProps => {
|
const getGrid = computed((): GridProps => {
|
||||||
@ -219,7 +219,7 @@ export default defineComponent({
|
|||||||
return {
|
return {
|
||||||
...gridProps,
|
...gridProps,
|
||||||
collapsed: isInline.value ? gridCollapsed.value : false,
|
collapsed: isInline.value ? gridCollapsed.value : false,
|
||||||
responsive: "screen",
|
responsive: 'screen',
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -298,7 +298,7 @@ export default defineComponent({
|
|||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
initDefault();
|
initDefault();
|
||||||
emit("register", formActionType);
|
emit('register', formActionType);
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@ -1,13 +1,13 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { createPlaceholderMessage } from "./helper";
|
import { createPlaceholderMessage } from './helper';
|
||||||
import { useFormEvents } from "./hooks/useFormEvents";
|
import { useFormEvents } from './hooks/useFormEvents';
|
||||||
import { useFormValues } from "./hooks/useFormValues";
|
import { useFormValues } from './hooks/useFormValues';
|
||||||
import { basicProps } from "./props";
|
import { basicProps } from './props';
|
||||||
import { DownOutlined, UpOutlined, QuestionCircleOutlined } from "@vicons/antd";
|
import { DownOutlined, UpOutlined, QuestionCircleOutlined } from '@vicons/antd';
|
||||||
import { isArray, deepMerge } from "@/utils";
|
import { isArray, deepMerge } from '@/utils';
|
||||||
import type { FormSchema, FormProps, FormActionType } from "./types/form";
|
import type { FormSchema, FormProps, FormActionType } from './types/form';
|
||||||
const props = defineProps(basicProps);
|
const props = defineProps(basicProps);
|
||||||
const emit = defineEmits(["reset", "submit", "register"]);
|
const emit = defineEmits(['reset', 'submit', 'register']);
|
||||||
|
|
||||||
const defaultFormModel = ref({});
|
const defaultFormModel = ref({});
|
||||||
const formModel = reactive({});
|
const formModel = reactive({});
|
||||||
@ -22,7 +22,7 @@ const getSubmitBtnOptions = computed(() => {
|
|||||||
return Object.assign(
|
return Object.assign(
|
||||||
{
|
{
|
||||||
size: props.size,
|
size: props.size,
|
||||||
type: "primary",
|
type: 'primary',
|
||||||
},
|
},
|
||||||
props.submitButtonOptions
|
props.submitButtonOptions
|
||||||
);
|
);
|
||||||
@ -32,7 +32,7 @@ const getResetBtnOptions = computed(() => {
|
|||||||
return Object.assign(
|
return Object.assign(
|
||||||
{
|
{
|
||||||
size: props.size,
|
size: props.size,
|
||||||
type: "default",
|
type: 'default',
|
||||||
},
|
},
|
||||||
props.resetButtonOptions
|
props.resetButtonOptions
|
||||||
);
|
);
|
||||||
@ -64,7 +64,7 @@ const getProps = computed(() => {
|
|||||||
|
|
||||||
const isInline = computed(() => {
|
const isInline = computed(() => {
|
||||||
const { layout } = unref(getProps);
|
const { layout } = unref(getProps);
|
||||||
return layout === "inline";
|
return layout === 'inline';
|
||||||
});
|
});
|
||||||
|
|
||||||
const getGrid = computed(() => {
|
const getGrid = computed(() => {
|
||||||
@ -72,7 +72,7 @@ const getGrid = computed(() => {
|
|||||||
return {
|
return {
|
||||||
...gridProps,
|
...gridProps,
|
||||||
collapsed: isInline.value ? gridCollapsed.value : false,
|
collapsed: isInline.value ? gridCollapsed.value : false,
|
||||||
responsive: "screen",
|
responsive: 'screen',
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -149,7 +149,7 @@ watch(
|
|||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
initDefault();
|
initDefault();
|
||||||
emit("register", formActionType);
|
emit('register', formActionType);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -279,7 +279,7 @@ onMounted(() => {
|
|||||||
<UpOutlined />
|
<UpOutlined />
|
||||||
</n-icon>
|
</n-icon>
|
||||||
</template>
|
</template>
|
||||||
{{ overflow ? "展开" : "收起" }}
|
{{ overflow ? '展开' : '收起' }}
|
||||||
</n-button>
|
</n-button>
|
||||||
</n-space>
|
</n-space>
|
||||||
</n-gi>
|
</n-gi>
|
||||||
|
|||||||
@ -1,29 +1,29 @@
|
|||||||
import { ComponentType } from "./types/index";
|
import { ComponentType } from './types/index';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description: 生成placeholder
|
* @description: 生成placeholder
|
||||||
*/
|
*/
|
||||||
export function createPlaceholderMessage(component: ComponentType) {
|
export function createPlaceholderMessage(component: ComponentType) {
|
||||||
if (component === "NInput") return "请输入";
|
if (component === 'NInput') return '请输入';
|
||||||
if (
|
if (
|
||||||
[
|
[
|
||||||
"NPicker",
|
'NPicker',
|
||||||
"NSelect",
|
'NSelect',
|
||||||
"NCheckbox",
|
'NCheckbox',
|
||||||
"NRadio",
|
'NRadio',
|
||||||
"NSwitch",
|
'NSwitch',
|
||||||
"NDatePicker",
|
'NDatePicker',
|
||||||
"NTimePicker",
|
'NTimePicker',
|
||||||
].includes(component)
|
].includes(component)
|
||||||
)
|
)
|
||||||
return "请选择";
|
return '请选择';
|
||||||
return "";
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
const DATE_TYPE = ["DatePicker", "MonthPicker", "WeekPicker", "TimePicker"];
|
const DATE_TYPE = ['DatePicker', 'MonthPicker', 'WeekPicker', 'TimePicker'];
|
||||||
|
|
||||||
function genType() {
|
function genType() {
|
||||||
return [...DATE_TYPE, "RangePicker"];
|
return [...DATE_TYPE, 'RangePicker'];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -32,17 +32,17 @@ function genType() {
|
|||||||
export const dateItemType = genType();
|
export const dateItemType = genType();
|
||||||
|
|
||||||
export function defaultType(component) {
|
export function defaultType(component) {
|
||||||
if (component === "NInput") return "";
|
if (component === 'NInput') return '';
|
||||||
if (component === "NInputNumber") return null;
|
if (component === 'NInputNumber') return null;
|
||||||
return [
|
return [
|
||||||
"NPicker",
|
'NPicker',
|
||||||
"NSelect",
|
'NSelect',
|
||||||
"NCheckbox",
|
'NCheckbox',
|
||||||
"NRadio",
|
'NRadio',
|
||||||
"NSwitch",
|
'NSwitch',
|
||||||
"NDatePicker",
|
'NDatePicker',
|
||||||
"NTimePicker",
|
'NTimePicker',
|
||||||
].includes(component)
|
].includes(component)
|
||||||
? ""
|
? ''
|
||||||
: undefined;
|
: undefined;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,11 +2,11 @@ import type {
|
|||||||
FormProps,
|
FormProps,
|
||||||
FormActionType,
|
FormActionType,
|
||||||
UseFormReturnType,
|
UseFormReturnType,
|
||||||
} from "../types/form";
|
} from '../types/form';
|
||||||
import type { ComputedRef, Ref } from "vue";
|
import type { ComputedRef, Ref } from 'vue';
|
||||||
import { ref, onUnmounted, unref, nextTick, watch } from "vue";
|
import { ref, onUnmounted, unref, nextTick, watch } from 'vue';
|
||||||
import { isProdMode } from "@/utils/env";
|
import { isProdMode } from '@/utils/env';
|
||||||
import { getDynamicProps } from "@/utils";
|
import { getDynamicProps } from '@/utils';
|
||||||
|
|
||||||
type DynamicProps<T> = {
|
type DynamicProps<T> = {
|
||||||
[P in keyof T]: Ref<T[P]> | T[P] | ComputedRef<T[P]>;
|
[P in keyof T]: Ref<T[P]> | T[P] | ComputedRef<T[P]>;
|
||||||
@ -22,7 +22,7 @@ export function useForm(props?: Props): UseFormReturnType {
|
|||||||
const form = unref(formRef);
|
const form = unref(formRef);
|
||||||
if (!form) {
|
if (!form) {
|
||||||
console.error(
|
console.error(
|
||||||
"The form instance has not been obtained, please make sure that the form has been rendered when performing the form operation!"
|
'The form instance has not been obtained, please make sure that the form has been rendered when performing the form operation!'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
await nextTick();
|
await nextTick();
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { provide, inject } from "vue";
|
import { provide, inject } from 'vue';
|
||||||
|
|
||||||
const key = Symbol("formElRef");
|
const key = Symbol('formElRef');
|
||||||
|
|
||||||
export function createFormContext(instance) {
|
export function createFormContext(instance) {
|
||||||
provide(key, instance);
|
provide(key, instance);
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import type { ComputedRef, Ref } from "vue";
|
import type { ComputedRef, Ref } from 'vue';
|
||||||
import type { FormProps, FormSchema, FormActionType } from "../types/form";
|
import type { FormProps, FormSchema, FormActionType } from '../types/form';
|
||||||
import { unref, toRaw } from "vue";
|
import { unref, toRaw } from 'vue';
|
||||||
import { isFunction } from "@/utils";
|
import { isFunction } from '@/utils';
|
||||||
|
|
||||||
declare type EmitType = (event: string, ...args: any[]) => void;
|
declare type EmitType = (event: string, ...args: any[]) => void;
|
||||||
|
|
||||||
@ -47,10 +47,10 @@ export function useFormEvents({
|
|||||||
await validate();
|
await validate();
|
||||||
const values = getFieldsValue();
|
const values = getFieldsValue();
|
||||||
loadingSub.value = false;
|
loadingSub.value = false;
|
||||||
emit("submit", values);
|
emit('submit', values);
|
||||||
return values;
|
return values;
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
emit("submit", false);
|
emit('submit', false);
|
||||||
loadingSub.value = false;
|
loadingSub.value = false;
|
||||||
console.error(error);
|
console.error(error);
|
||||||
return false;
|
return false;
|
||||||
@ -75,7 +75,7 @@ export function useFormEvents({
|
|||||||
});
|
});
|
||||||
await clearValidate();
|
await clearValidate();
|
||||||
const fromValues = handleFormValues(toRaw(unref(formModel)));
|
const fromValues = handleFormValues(toRaw(unref(formModel)));
|
||||||
emit("reset", fromValues);
|
emit('reset', fromValues);
|
||||||
submitOnReset && (await handleSubmit());
|
submitOnReset && (await handleSubmit());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -4,11 +4,11 @@ import {
|
|||||||
isObject,
|
isObject,
|
||||||
isString,
|
isString,
|
||||||
isNullOrUnDef,
|
isNullOrUnDef,
|
||||||
} from "@/utils/is";
|
} from '@/utils/is';
|
||||||
import { unref } from "vue";
|
import { unref } from 'vue';
|
||||||
import type { Ref, ComputedRef } from "vue";
|
import type { Ref, ComputedRef } from 'vue';
|
||||||
import type { FormSchema } from "../types/form";
|
import type { FormSchema } from '../types/form';
|
||||||
import { set } from "lodash-es";
|
import { set } from 'lodash-es';
|
||||||
|
|
||||||
interface UseFormValuesContext {
|
interface UseFormValuesContext {
|
||||||
defaultFormModel: Ref<any>;
|
defaultFormModel: Ref<any>;
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
import type { CSSProperties, PropType } from "vue";
|
import type { CSSProperties, PropType } from 'vue';
|
||||||
import { FormSchema } from "./types/form";
|
import { FormSchema } from './types/form';
|
||||||
import type { GridProps, GridItemProps } from "naive-ui/lib/grid";
|
import type { GridProps, GridItemProps } from 'naive-ui/lib/grid';
|
||||||
import type { ButtonProps } from "naive-ui/lib/button";
|
import type { ButtonProps } from 'naive-ui/lib/button';
|
||||||
import { propTypes } from "@/utils/propTypes";
|
import { propTypes } from '@/utils/propTypes';
|
||||||
export const basicProps = {
|
export const basicProps = {
|
||||||
// 标签宽度 固定宽度
|
// 标签宽度 固定宽度
|
||||||
labelWidth: {
|
labelWidth: {
|
||||||
@ -17,7 +17,7 @@ export const basicProps = {
|
|||||||
//布局方式
|
//布局方式
|
||||||
layout: {
|
layout: {
|
||||||
type: String,
|
type: String,
|
||||||
default: "inline",
|
default: 'inline',
|
||||||
},
|
},
|
||||||
//是否展示为行内表单
|
//是否展示为行内表单
|
||||||
inline: {
|
inline: {
|
||||||
@ -27,12 +27,12 @@ export const basicProps = {
|
|||||||
//大小
|
//大小
|
||||||
size: {
|
size: {
|
||||||
type: String,
|
type: String,
|
||||||
default: "medium",
|
default: 'medium',
|
||||||
},
|
},
|
||||||
//标签位置
|
//标签位置
|
||||||
labelPlacement: {
|
labelPlacement: {
|
||||||
type: String,
|
type: String,
|
||||||
default: "left",
|
default: 'left',
|
||||||
},
|
},
|
||||||
//组件是否width 100%
|
//组件是否width 100%
|
||||||
isFull: {
|
isFull: {
|
||||||
@ -54,12 +54,12 @@ export const basicProps = {
|
|||||||
// 确认按钮文字
|
// 确认按钮文字
|
||||||
submitButtonText: {
|
submitButtonText: {
|
||||||
type: String,
|
type: String,
|
||||||
default: "查询",
|
default: '查询',
|
||||||
},
|
},
|
||||||
//重置按钮文字
|
//重置按钮文字
|
||||||
resetButtonText: {
|
resetButtonText: {
|
||||||
type: String,
|
type: String,
|
||||||
default: "重置",
|
default: '重置',
|
||||||
},
|
},
|
||||||
//grid 配置
|
//grid 配置
|
||||||
gridProps: Object as PropType<GridProps>,
|
gridProps: Object as PropType<GridProps>,
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { ComponentType } from "./index";
|
import { ComponentType } from './index';
|
||||||
import type { CSSProperties } from "vue";
|
import type { CSSProperties } from 'vue';
|
||||||
import type { GridProps, GridItemProps } from "naive-ui/lib/grid";
|
import type { GridProps, GridItemProps } from 'naive-ui/lib/grid';
|
||||||
import type { ButtonProps } from "naive-ui/lib/button";
|
import type { ButtonProps } from 'naive-ui/lib/button';
|
||||||
|
|
||||||
export interface FormSchema {
|
export interface FormSchema {
|
||||||
field: string;
|
field: string;
|
||||||
|
|||||||
@ -1,28 +1,28 @@
|
|||||||
export type ComponentType =
|
export type ComponentType =
|
||||||
| "NInput"
|
| 'NInput'
|
||||||
| "NInputGroup"
|
| 'NInputGroup'
|
||||||
| "NInputPassword"
|
| 'NInputPassword'
|
||||||
| "NInputSearch"
|
| 'NInputSearch'
|
||||||
| "NInputTextArea"
|
| 'NInputTextArea'
|
||||||
| "NInputNumber"
|
| 'NInputNumber'
|
||||||
| "NInputCountDown"
|
| 'NInputCountDown'
|
||||||
| "NSelect"
|
| 'NSelect'
|
||||||
| "NTreeSelect"
|
| 'NTreeSelect'
|
||||||
| "NRadioButtonGroup"
|
| 'NRadioButtonGroup'
|
||||||
| "NRadioGroup"
|
| 'NRadioGroup'
|
||||||
| "NCheckbox"
|
| 'NCheckbox'
|
||||||
| "NCheckboxGroup"
|
| 'NCheckboxGroup'
|
||||||
| "NAutoComplete"
|
| 'NAutoComplete'
|
||||||
| "NCascader"
|
| 'NCascader'
|
||||||
| "NDatePicker"
|
| 'NDatePicker'
|
||||||
| "NMonthPicker"
|
| 'NMonthPicker'
|
||||||
| "NRangePicker"
|
| 'NRangePicker'
|
||||||
| "NWeekPicker"
|
| 'NWeekPicker'
|
||||||
| "NTimePicker"
|
| 'NTimePicker'
|
||||||
| "NSwitch"
|
| 'NSwitch'
|
||||||
| "NStrengthMeter"
|
| 'NStrengthMeter'
|
||||||
| "NUpload"
|
| 'NUpload'
|
||||||
| "NIconPicker"
|
| 'NIconPicker'
|
||||||
| "NRender"
|
| 'NRender'
|
||||||
| "NSlider"
|
| 'NSlider'
|
||||||
| "NRate";
|
| 'NRate';
|
||||||
|
|||||||
303
src/components/Lockscreen/Lockscreen.vue
Normal file
@ -0,0 +1,303 @@
|
|||||||
|
<template>
|
||||||
|
<div
|
||||||
|
:class="{ onLockLogin: showLogin }"
|
||||||
|
class="lockscreen"
|
||||||
|
@keyup="onLockLogin(true)"
|
||||||
|
@mousedown.stop
|
||||||
|
@contextmenu.prevent
|
||||||
|
>
|
||||||
|
<template v-if="!showLogin">
|
||||||
|
<div class="lock-box">
|
||||||
|
<div class="lock">
|
||||||
|
<span class="lock-icon" title="解锁屏幕" @click="onLockLogin(true)">
|
||||||
|
<n-icon>
|
||||||
|
<lock-outlined />
|
||||||
|
</n-icon>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!--充电-->
|
||||||
|
<recharge
|
||||||
|
:battery="battery"
|
||||||
|
:battery-status="batteryStatus"
|
||||||
|
:calc-discharging-time="calcDischargingTime"
|
||||||
|
:calc-charging-time="calcChargingTime"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div class="local-time">
|
||||||
|
<div class="time">{{ hour }}:{{ minute }}</div>
|
||||||
|
<div class="date">{{ month }}月{{ day }}号,星期{{ week }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="computer-status">
|
||||||
|
<span :class="{ offline: !online }" class="network">
|
||||||
|
<wifi-outlined class="network" />
|
||||||
|
</span>
|
||||||
|
<api-outlined />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!--登录-->
|
||||||
|
<template v-if="showLogin">
|
||||||
|
<div class="login-box">
|
||||||
|
<n-avatar :size="128">
|
||||||
|
<n-icon>
|
||||||
|
<user-outlined />
|
||||||
|
</n-icon>
|
||||||
|
</n-avatar>
|
||||||
|
<div class="username">{{ loginParams.username }}</div>
|
||||||
|
<n-input
|
||||||
|
type="password"
|
||||||
|
autofocus
|
||||||
|
v-model:value="loginParams.password"
|
||||||
|
@keyup.enter="onLogin"
|
||||||
|
placeholder="请输入登录密码"
|
||||||
|
>
|
||||||
|
<template #suffix>
|
||||||
|
<n-icon @click="onLogin" style="cursor: pointer">
|
||||||
|
<LoadingOutlined v-if="loginLoading" />
|
||||||
|
<arrow-right-outlined v-else />
|
||||||
|
</n-icon>
|
||||||
|
</template>
|
||||||
|
</n-input>
|
||||||
|
|
||||||
|
<div class="flex w-full" v-if="isLoginError">
|
||||||
|
<span class="text-red-500">{{ errorMsg }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex justify-around w-full mt-1">
|
||||||
|
<div><a @click="showLogin = false">返回</a></div>
|
||||||
|
<div><a @click="goLogin">重新登录</a></div>
|
||||||
|
<div><a @click="onLogin">进入系统</a></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent, reactive, toRefs } from 'vue';
|
||||||
|
import { ResultEnum } from '@/enums/httpEnum';
|
||||||
|
import recharge from './Recharge.vue';
|
||||||
|
import {
|
||||||
|
LockOutlined,
|
||||||
|
LoadingOutlined,
|
||||||
|
UserOutlined,
|
||||||
|
ApiOutlined,
|
||||||
|
ArrowRightOutlined,
|
||||||
|
WifiOutlined,
|
||||||
|
} from '@vicons/antd';
|
||||||
|
|
||||||
|
import { useRouter, useRoute } from 'vue-router';
|
||||||
|
import { useOnline } from '@/hooks/useOnline';
|
||||||
|
import { useTime } from '@/hooks/useTime';
|
||||||
|
import { useBattery } from '@/hooks/useBattery';
|
||||||
|
import { useScreenLockStore } from '@/store/modules/screenLock';
|
||||||
|
import { UserInfoType, useUserStore } from '@/store/modules/user';
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'ScreenLock',
|
||||||
|
components: {
|
||||||
|
LockOutlined,
|
||||||
|
LoadingOutlined,
|
||||||
|
UserOutlined,
|
||||||
|
ArrowRightOutlined,
|
||||||
|
ApiOutlined,
|
||||||
|
WifiOutlined,
|
||||||
|
recharge,
|
||||||
|
},
|
||||||
|
setup() {
|
||||||
|
const useScreenLock = useScreenLockStore();
|
||||||
|
const userStore = useUserStore();
|
||||||
|
|
||||||
|
// 获取时间
|
||||||
|
const { month, day, hour, minute, second, week } = useTime();
|
||||||
|
const { online } = useOnline();
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
const route = useRoute();
|
||||||
|
|
||||||
|
const { battery, batteryStatus, calcDischargingTime, calcChargingTime } =
|
||||||
|
useBattery();
|
||||||
|
const userInfo: UserInfoType = userStore.getUserInfo || {};
|
||||||
|
const username = userInfo['username'] || '';
|
||||||
|
const state = reactive({
|
||||||
|
showLogin: false,
|
||||||
|
loginLoading: false, // 正在登录
|
||||||
|
isLoginError: false, //密码错误
|
||||||
|
errorMsg: '密码错误',
|
||||||
|
loginParams: {
|
||||||
|
username: username || '',
|
||||||
|
password: '',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// 解锁登录
|
||||||
|
const onLockLogin = (value: boolean) => (state.showLogin = value);
|
||||||
|
|
||||||
|
// 登录
|
||||||
|
const onLogin = async () => {
|
||||||
|
if (!state.loginParams.password.trim()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const params = {
|
||||||
|
isLock: true,
|
||||||
|
...state.loginParams,
|
||||||
|
};
|
||||||
|
state.loginLoading = true;
|
||||||
|
const { status } = await userStore.login(params);
|
||||||
|
|
||||||
|
if (status === ResultEnum.STATUS) {
|
||||||
|
onLockLogin(false);
|
||||||
|
useScreenLock.setLock(false);
|
||||||
|
} else {
|
||||||
|
state.errorMsg = '密码错误';
|
||||||
|
state.isLoginError = true;
|
||||||
|
}
|
||||||
|
state.loginLoading = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
//重新登录
|
||||||
|
const goLogin = () => {
|
||||||
|
onLockLogin(false);
|
||||||
|
useScreenLock.setLock(false);
|
||||||
|
router.replace({
|
||||||
|
path: '/login',
|
||||||
|
query: {
|
||||||
|
redirect: route.fullPath,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
...toRefs(state),
|
||||||
|
online,
|
||||||
|
month,
|
||||||
|
day,
|
||||||
|
hour,
|
||||||
|
minute,
|
||||||
|
second,
|
||||||
|
week,
|
||||||
|
battery,
|
||||||
|
batteryStatus,
|
||||||
|
calcDischargingTime,
|
||||||
|
calcChargingTime,
|
||||||
|
onLockLogin,
|
||||||
|
onLogin,
|
||||||
|
goLogin,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.lockscreen {
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
z-index: 9999;
|
||||||
|
display: flex;
|
||||||
|
overflow: hidden;
|
||||||
|
color: white;
|
||||||
|
background: #000;
|
||||||
|
|
||||||
|
&.onLockLogin {
|
||||||
|
background-color: rgb(25 28 34 / 88%);
|
||||||
|
backdrop-filter: blur(7px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-box {
|
||||||
|
position: absolute;
|
||||||
|
top: 45%;
|
||||||
|
left: 50%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
|
||||||
|
> * {
|
||||||
|
margin-bottom: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.username {
|
||||||
|
font-size: 30px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.lock-box {
|
||||||
|
position: absolute;
|
||||||
|
top: 20px;
|
||||||
|
left: 50%;
|
||||||
|
z-index: 100;
|
||||||
|
font-size: 34px;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
|
||||||
|
.tips {
|
||||||
|
color: white;
|
||||||
|
cursor: text;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lock {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
.lock-icon {
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
.anticon-unlock {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover .anticon-unlock {
|
||||||
|
display: initial;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover .anticon-lock {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.local-time {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 60px;
|
||||||
|
left: 60px;
|
||||||
|
font-family: helvetica;
|
||||||
|
|
||||||
|
.time {
|
||||||
|
font-size: 70px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.date {
|
||||||
|
font-size: 40px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.computer-status {
|
||||||
|
position: absolute;
|
||||||
|
right: 60px;
|
||||||
|
bottom: 60px;
|
||||||
|
font-size: 24px;
|
||||||
|
|
||||||
|
> * {
|
||||||
|
margin-left: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.network {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&.offline::before {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
z-index: 10;
|
||||||
|
width: 2px;
|
||||||
|
height: 28px;
|
||||||
|
content: '';
|
||||||
|
background-color: red;
|
||||||
|
transform: translate(-50%, -50%) rotate(45deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
174
src/components/Lockscreen/Recharge.vue
Normal file
@ -0,0 +1,174 @@
|
|||||||
|
<template>
|
||||||
|
<div class="container">
|
||||||
|
<div class="number">{{ battery.level }}%</div>
|
||||||
|
<div class="contrast">
|
||||||
|
<div class="circle"></div>
|
||||||
|
<ul class="bubbles">
|
||||||
|
<li v-for="i in 15" :key="i"></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="charging">
|
||||||
|
<div>{{ batteryStatus }}</div>
|
||||||
|
<div
|
||||||
|
v-show="
|
||||||
|
Number.isFinite(battery.dischargingTime) &&
|
||||||
|
battery.dischargingTime != 0
|
||||||
|
"
|
||||||
|
>
|
||||||
|
剩余可使用时间:{{ calcDischargingTime }}
|
||||||
|
</div>
|
||||||
|
<span
|
||||||
|
v-show="
|
||||||
|
Number.isFinite(battery.chargingTime) && battery.chargingTime != 0
|
||||||
|
"
|
||||||
|
>
|
||||||
|
距离电池充满需要:{{ calcChargingTime }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent } from 'vue';
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'HuaweiCharge',
|
||||||
|
// props: ['batteryStatus', 'battery', 'calcDischargingTime'],
|
||||||
|
props: {
|
||||||
|
battery: {
|
||||||
|
// 电池对象
|
||||||
|
type: Object,
|
||||||
|
default: () => ({}),
|
||||||
|
},
|
||||||
|
calcDischargingTime: {
|
||||||
|
// 电池剩余时间可用时间
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
calcChargingTime: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
batteryStatus: {
|
||||||
|
// 电池状态
|
||||||
|
type: String,
|
||||||
|
validator: (val: string) =>
|
||||||
|
['充电中', '已充满', '已断开电源'].includes(val),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.container {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 20vh;
|
||||||
|
left: 50vw;
|
||||||
|
width: 300px;
|
||||||
|
height: 500px;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
|
||||||
|
.number {
|
||||||
|
position: absolute;
|
||||||
|
top: 20%;
|
||||||
|
z-index: 10;
|
||||||
|
width: 300px;
|
||||||
|
font-size: 32px;
|
||||||
|
color: #fff;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contrast {
|
||||||
|
width: 300px;
|
||||||
|
height: 400px;
|
||||||
|
overflow: hidden;
|
||||||
|
background-color: #000;
|
||||||
|
filter: contrast(15) hue-rotate(0);
|
||||||
|
animation: hueRotate 10s infinite linear;
|
||||||
|
|
||||||
|
.circle {
|
||||||
|
position: relative;
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: 300px;
|
||||||
|
height: 300px;
|
||||||
|
filter: blur(8px);
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
position: absolute;
|
||||||
|
top: 40%;
|
||||||
|
left: 50%;
|
||||||
|
width: 200px;
|
||||||
|
height: 200px;
|
||||||
|
content: '';
|
||||||
|
background-color: #00ff6f;
|
||||||
|
border-radius: 42% 38% 62% 49% / 45%;
|
||||||
|
transform: translate(-50%, -50%) rotate(0);
|
||||||
|
animation: rotate 10s infinite linear;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
position: absolute;
|
||||||
|
top: 40%;
|
||||||
|
left: 50%;
|
||||||
|
z-index: 10;
|
||||||
|
width: 176px;
|
||||||
|
height: 176px;
|
||||||
|
content: '';
|
||||||
|
background-color: #000;
|
||||||
|
border-radius: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.bubbles {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 50%;
|
||||||
|
width: 100px;
|
||||||
|
height: 40px;
|
||||||
|
background-color: #00ff6f;
|
||||||
|
filter: blur(5px);
|
||||||
|
border-radius: 100px 100px 0 0;
|
||||||
|
transform: translate(-50%, 0);
|
||||||
|
|
||||||
|
li {
|
||||||
|
position: absolute;
|
||||||
|
background: #00ff6f;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.charging {
|
||||||
|
font-size: 20px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes rotate {
|
||||||
|
50% {
|
||||||
|
border-radius: 45% / 42% 38% 58% 49%;
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
transform: translate(-50%, -50%) rotate(720deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes moveToTop {
|
||||||
|
90% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
opacity: 0.1;
|
||||||
|
transform: translate(-50%, -180px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes hueRotate {
|
||||||
|
100% {
|
||||||
|
filter: contrast(15) hue-rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
3
src/components/Lockscreen/index.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import LockScreen from './Lockscreen.vue';
|
||||||
|
|
||||||
|
export { LockScreen };
|
||||||
@ -1,45 +1,41 @@
|
|||||||
<template>
|
<template>
|
||||||
<svg
|
<svg aria-hidden="true" class="svg-icon">
|
||||||
aria-hidden="true"
|
|
||||||
class="svg-icon"
|
|
||||||
:style="'width:' + size + ';height:' + size"
|
|
||||||
>
|
|
||||||
<use :xlink:href="symbolId" :fill="color" />
|
<use :xlink:href="symbolId" :fill="color" />
|
||||||
</svg>
|
</svg>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script>
|
||||||
const props = defineProps({
|
import { defineComponent, computed } from 'vue';
|
||||||
prefix: {
|
|
||||||
type: String,
|
export default defineComponent({
|
||||||
default: "icon",
|
name: 'SvgIcon',
|
||||||
|
props: {
|
||||||
|
// 使用的svg图标名称,也就是svg文件名
|
||||||
|
name: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
prefix: {
|
||||||
|
type: String,
|
||||||
|
default: 'icon',
|
||||||
|
},
|
||||||
|
color: {
|
||||||
|
type: String,
|
||||||
|
default: '#333',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
iconClass: {
|
setup(props) {
|
||||||
type: String,
|
const symbolId = computed(() => `#${props.prefix}-${props.name}`);
|
||||||
required: false,
|
return { symbolId };
|
||||||
default: "",
|
|
||||||
},
|
|
||||||
color: {
|
|
||||||
type: String,
|
|
||||||
default: "",
|
|
||||||
},
|
|
||||||
size: {
|
|
||||||
type: String,
|
|
||||||
default: "1em",
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const symbolId = computed(() => `#${props.prefix}-${props.iconClass}`);
|
|
||||||
</script>
|
</script>
|
||||||
|
<style scope>
|
||||||
<style scoped>
|
|
||||||
.svg-icon {
|
.svg-icon {
|
||||||
display: inline-block;
|
width: 16px;
|
||||||
width: 1em;
|
height: 16px;
|
||||||
height: 1em;
|
|
||||||
overflow: hidden;
|
/* color: #333; */
|
||||||
vertical-align: -0.15em; /* 因icon大小被设置为和字体大小一致,而span等标签的下边缘会和字体的基线对齐,故需设置一个往下的偏移比例,来纠正视觉上的未对齐效果 */
|
fill: currentcolor;
|
||||||
outline: none;
|
|
||||||
fill: currentcolor; /* 定义元素的颜色,currentColor是一个变量,这个变量的值就表示当前元素的color值,如果当前元素未设置color值,则从父元素继承 */
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
export { default as BasicTable } from "./src/Table.vue";
|
export { default as BasicTable } from './src/Table.vue';
|
||||||
export { default as TableAction } from "./src/components/TableAction.vue";
|
export { default as TableAction } from './src/components/TableAction.vue';
|
||||||
export * from "./src/types/table";
|
export * from './src/types/table';
|
||||||
export * from "./src/types/tableAction";
|
export * from './src/types/tableAction';
|
||||||
|
|||||||
@ -95,44 +95,44 @@ import {
|
|||||||
toRefs,
|
toRefs,
|
||||||
onMounted,
|
onMounted,
|
||||||
nextTick,
|
nextTick,
|
||||||
} from "vue";
|
} from 'vue';
|
||||||
import {
|
import {
|
||||||
ReloadOutlined,
|
ReloadOutlined,
|
||||||
ColumnHeightOutlined,
|
ColumnHeightOutlined,
|
||||||
QuestionCircleOutlined,
|
QuestionCircleOutlined,
|
||||||
} from "@vicons/antd";
|
} from '@vicons/antd';
|
||||||
import { createTableContext } from "./hooks/useTableContext";
|
import { createTableContext } from './hooks/useTableContext';
|
||||||
|
|
||||||
import ColumnSetting from "./components/settings/ColumnSetting.vue";
|
import ColumnSetting from './components/settings/ColumnSetting.vue';
|
||||||
|
|
||||||
import { useLoading } from "./hooks/useLoading";
|
import { useLoading } from './hooks/useLoading';
|
||||||
import { useColumns } from "./hooks/useColumns";
|
import { useColumns } from './hooks/useColumns';
|
||||||
import { useDataSource } from "./hooks/useDataSource";
|
import { useDataSource } from './hooks/useDataSource';
|
||||||
import { usePagination } from "./hooks/usePagination";
|
import { usePagination } from './hooks/usePagination';
|
||||||
|
|
||||||
import { basicProps } from "./props";
|
import { basicProps } from './props';
|
||||||
|
|
||||||
import { BasicTableProps } from "./types/table";
|
import { BasicTableProps } from './types/table';
|
||||||
|
|
||||||
import { getViewportOffset } from "@/utils";
|
import { getViewportOffset } from '@/utils';
|
||||||
import { useWindowSizeFn } from "@/hooks/event/useWindowSizeFn";
|
import { useWindowSizeFn } from '@/hooks/event/useWindowSizeFn';
|
||||||
import { isBoolean } from "@/utils";
|
import { isBoolean } from '@/utils';
|
||||||
|
|
||||||
const densityOptions = [
|
const densityOptions = [
|
||||||
{
|
{
|
||||||
type: "menu",
|
type: 'menu',
|
||||||
label: "紧凑",
|
label: '紧凑',
|
||||||
key: "small",
|
key: 'small',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "menu",
|
type: 'menu',
|
||||||
label: "默认",
|
label: '默认',
|
||||||
key: "medium",
|
key: 'medium',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "menu",
|
type: 'menu',
|
||||||
label: "宽松",
|
label: '宽松',
|
||||||
key: "large",
|
key: 'large',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -147,13 +147,13 @@ export default defineComponent({
|
|||||||
...basicProps,
|
...basicProps,
|
||||||
},
|
},
|
||||||
emits: [
|
emits: [
|
||||||
"fetch-success",
|
'fetch-success',
|
||||||
"fetch-error",
|
'fetch-error',
|
||||||
"update:checked-row-keys",
|
'update:checked-row-keys',
|
||||||
"edit-end",
|
'edit-end',
|
||||||
"edit-cancel",
|
'edit-cancel',
|
||||||
"edit-row-end",
|
'edit-row-end',
|
||||||
"edit-change",
|
'edit-change',
|
||||||
],
|
],
|
||||||
setup(props, { emit }) {
|
setup(props, { emit }) {
|
||||||
const deviceHeight = ref(150);
|
const deviceHeight = ref(150);
|
||||||
@ -193,7 +193,7 @@ export default defineComponent({
|
|||||||
} = useColumns(getProps);
|
} = useColumns(getProps);
|
||||||
|
|
||||||
const state = reactive({
|
const state = reactive({
|
||||||
tableSize: unref(getProps as any).size || "medium",
|
tableSize: unref(getProps as any).size || 'medium',
|
||||||
isColumnSetting: false,
|
isColumnSetting: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -216,7 +216,7 @@ export default defineComponent({
|
|||||||
|
|
||||||
//选中行
|
//选中行
|
||||||
function updateCheckedRowKeys(rowKeys) {
|
function updateCheckedRowKeys(rowKeys) {
|
||||||
emit("update:checked-row-keys", rowKeys);
|
emit('update:checked-row-keys', rowKeys);
|
||||||
}
|
}
|
||||||
|
|
||||||
//获取表格大小
|
//获取表格大小
|
||||||
@ -225,7 +225,7 @@ export default defineComponent({
|
|||||||
//组装表格信息
|
//组装表格信息
|
||||||
const getBindValues = computed(() => {
|
const getBindValues = computed(() => {
|
||||||
const tableData = unref(getDataSourceRef);
|
const tableData = unref(getDataSourceRef);
|
||||||
const maxHeight = tableData.length ? `${unref(deviceHeight)}px` : "auto";
|
const maxHeight = tableData.length ? `${unref(deviceHeight)}px` : 'auto';
|
||||||
return {
|
return {
|
||||||
...unref(getProps),
|
...unref(getProps),
|
||||||
loading: unref(getLoading),
|
loading: unref(getLoading),
|
||||||
@ -234,7 +234,7 @@ export default defineComponent({
|
|||||||
data: tableData,
|
data: tableData,
|
||||||
size: unref(getTableSize),
|
size: unref(getTableSize),
|
||||||
remote: true,
|
remote: true,
|
||||||
"max-height": maxHeight,
|
'max-height': maxHeight,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -269,14 +269,14 @@ export default defineComponent({
|
|||||||
if (!table) return;
|
if (!table) return;
|
||||||
if (!unref(getCanResize)) return;
|
if (!unref(getCanResize)) return;
|
||||||
const tableEl: any = table?.$el;
|
const tableEl: any = table?.$el;
|
||||||
const headEl = tableEl.querySelector(".n-data-table-thead ");
|
const headEl = tableEl.querySelector('.n-data-table-thead ');
|
||||||
const { bottomIncludeBody } = getViewportOffset(headEl);
|
const { bottomIncludeBody } = getViewportOffset(headEl);
|
||||||
const headerH = 64;
|
const headerH = 64;
|
||||||
let paginationH = 2;
|
let paginationH = 2;
|
||||||
let marginH = 24;
|
let marginH = 24;
|
||||||
if (!isBoolean(unref(pagination))) {
|
if (!isBoolean(unref(pagination))) {
|
||||||
paginationEl = tableEl.querySelector(
|
paginationEl = tableEl.querySelector(
|
||||||
".n-data-table__pagination"
|
'.n-data-table__pagination'
|
||||||
) as HTMLElement;
|
) as HTMLElement;
|
||||||
if (paginationEl) {
|
if (paginationEl) {
|
||||||
const offsetHeight = paginationEl.offsetHeight;
|
const offsetHeight = paginationEl.offsetHeight;
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-duplicate-enum-values */
|
/* eslint-disable @typescript-eslint/no-duplicate-enum-values */
|
||||||
import type { Component } from "vue";
|
import type { Component } from 'vue';
|
||||||
import {
|
import {
|
||||||
NInput,
|
NInput,
|
||||||
NSelect,
|
NSelect,
|
||||||
@ -8,28 +8,28 @@ import {
|
|||||||
NSwitch,
|
NSwitch,
|
||||||
NDatePicker,
|
NDatePicker,
|
||||||
NTimePicker,
|
NTimePicker,
|
||||||
} from "naive-ui";
|
} from 'naive-ui';
|
||||||
import type { ComponentType } from "./types/componentType";
|
import type { ComponentType } from './types/componentType';
|
||||||
|
|
||||||
export enum EventEnum {
|
export enum EventEnum {
|
||||||
NInput = "on-input",
|
NInput = 'on-input',
|
||||||
NInputNumber = "on-input",
|
NInputNumber = 'on-input',
|
||||||
NSelect = "on-update:value",
|
NSelect = 'on-update:value',
|
||||||
NSwitch = "on-update:value",
|
NSwitch = 'on-update:value',
|
||||||
NCheckbox = "on-update:value",
|
NCheckbox = 'on-update:value',
|
||||||
NDatePicker = "on-update:value",
|
NDatePicker = 'on-update:value',
|
||||||
NTimePicker = "on-update:value",
|
NTimePicker = 'on-update:value',
|
||||||
}
|
}
|
||||||
|
|
||||||
const componentMap = new Map<ComponentType, Component>();
|
const componentMap = new Map<ComponentType, Component>();
|
||||||
|
|
||||||
componentMap.set("NInput", NInput);
|
componentMap.set('NInput', NInput);
|
||||||
componentMap.set("NInputNumber", NInputNumber);
|
componentMap.set('NInputNumber', NInputNumber);
|
||||||
componentMap.set("NSelect", NSelect);
|
componentMap.set('NSelect', NSelect);
|
||||||
componentMap.set("NSwitch", NSwitch);
|
componentMap.set('NSwitch', NSwitch);
|
||||||
componentMap.set("NCheckbox", NCheckbox);
|
componentMap.set('NCheckbox', NCheckbox);
|
||||||
componentMap.set("NDatePicker", NDatePicker);
|
componentMap.set('NDatePicker', NDatePicker);
|
||||||
componentMap.set("NTimePicker", NTimePicker);
|
componentMap.set('NTimePicker', NTimePicker);
|
||||||
|
|
||||||
export function add(compName: ComponentType, component: Component) {
|
export function add(compName: ComponentType, component: Component) {
|
||||||
componentMap.set(compName, component);
|
componentMap.set(compName, component);
|
||||||
|
|||||||
@ -41,14 +41,14 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, PropType, computed, toRaw } from "vue";
|
import { defineComponent, PropType, computed, toRaw } from 'vue';
|
||||||
import { ActionItem } from "@/components/Table";
|
import { ActionItem } from '@/components/Table';
|
||||||
// import { usePermission } from "@/hooks/web/usePermission";
|
// import { usePermission } from "@/hooks/web/usePermission";
|
||||||
import { isBoolean, isFunction } from "@/utils";
|
import { isBoolean, isFunction } from '@/utils';
|
||||||
import { DownOutlined } from "@vicons/antd";
|
import { DownOutlined } from '@vicons/antd';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: "TableAction",
|
name: 'TableAction',
|
||||||
components: { DownOutlined },
|
components: { DownOutlined },
|
||||||
props: {
|
props: {
|
||||||
actions: {
|
actions: {
|
||||||
@ -62,7 +62,7 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
style: {
|
style: {
|
||||||
type: String as PropType<String>,
|
type: String as PropType<String>,
|
||||||
default: "button",
|
default: 'button',
|
||||||
},
|
},
|
||||||
select: {
|
select: {
|
||||||
type: Function as PropType<Function>,
|
type: Function as PropType<Function>,
|
||||||
@ -71,15 +71,15 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
setup(props) {
|
setup(props) {
|
||||||
const actionType =
|
const actionType =
|
||||||
props.style === "button"
|
props.style === 'button'
|
||||||
? "default"
|
? 'default'
|
||||||
: props.style === "text"
|
: props.style === 'text'
|
||||||
? "primary"
|
? 'primary'
|
||||||
: "default";
|
: 'default';
|
||||||
const actionText =
|
const actionText =
|
||||||
props.style === "button"
|
props.style === 'button'
|
||||||
? undefined
|
? undefined
|
||||||
: props.style === "text"
|
: props.style === 'text'
|
||||||
? true
|
? true
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
@ -87,7 +87,7 @@ export default defineComponent({
|
|||||||
return {
|
return {
|
||||||
text: actionText,
|
text: actionText,
|
||||||
type: actionType,
|
type: actionType,
|
||||||
size: "small",
|
size: 'small',
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -99,7 +99,7 @@ export default defineComponent({
|
|||||||
.map((action) => {
|
.map((action) => {
|
||||||
const { popConfirm } = action;
|
const { popConfirm } = action;
|
||||||
return {
|
return {
|
||||||
size: "small",
|
size: 'small',
|
||||||
text: actionText,
|
text: actionText,
|
||||||
type: actionType,
|
type: actionType,
|
||||||
...action,
|
...action,
|
||||||
@ -133,7 +133,7 @@ export default defineComponent({
|
|||||||
const { popConfirm } = action;
|
const { popConfirm } = action;
|
||||||
//需要展示什么风格,自己修改一下参数
|
//需要展示什么风格,自己修改一下参数
|
||||||
return {
|
return {
|
||||||
size: "small",
|
size: 'small',
|
||||||
text: actionText,
|
text: actionText,
|
||||||
type: actionType,
|
type: actionType,
|
||||||
...action,
|
...action,
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
import type { FunctionalComponent, defineComponent } from "vue";
|
import type { FunctionalComponent, defineComponent } from 'vue';
|
||||||
import type { ComponentType } from "../../types/componentType";
|
import type { ComponentType } from '../../types/componentType';
|
||||||
import { componentMap } from "@/components/Table/src/componentMap";
|
import { componentMap } from '@/components/Table/src/componentMap';
|
||||||
|
|
||||||
import { h } from "vue";
|
import { h } from 'vue';
|
||||||
|
|
||||||
import { NPopover } from "naive-ui";
|
import { NPopover } from 'naive-ui';
|
||||||
|
|
||||||
export interface ComponentProps {
|
export interface ComponentProps {
|
||||||
component: ComponentType;
|
component: ComponentType;
|
||||||
@ -15,7 +15,7 @@ export interface ComponentProps {
|
|||||||
|
|
||||||
export const CellComponent: FunctionalComponent = (
|
export const CellComponent: FunctionalComponent = (
|
||||||
{
|
{
|
||||||
component = "NInput",
|
component = 'NInput',
|
||||||
rule = true,
|
rule = true,
|
||||||
ruleMessage,
|
ruleMessage,
|
||||||
popoverVisible,
|
popoverVisible,
|
||||||
@ -30,17 +30,17 @@ export const CellComponent: FunctionalComponent = (
|
|||||||
}
|
}
|
||||||
return h(
|
return h(
|
||||||
NPopover,
|
NPopover,
|
||||||
{ "display-directive": "show", show: !!popoverVisible, manual: "manual" },
|
{ 'display-directive': 'show', show: !!popoverVisible, manual: 'manual' },
|
||||||
{
|
{
|
||||||
trigger: () => DefaultComp,
|
trigger: () => DefaultComp,
|
||||||
default: () =>
|
default: () =>
|
||||||
h(
|
h(
|
||||||
"span",
|
'span',
|
||||||
{
|
{
|
||||||
style: {
|
style: {
|
||||||
color: "red",
|
color: 'red',
|
||||||
width: "90px",
|
width: '90px',
|
||||||
display: "inline-block",
|
display: 'inline-block',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@ -36,9 +36,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { PropType } from "vue";
|
import type { PropType } from 'vue';
|
||||||
import type { BasicColumn } from "../../types/table";
|
import type { BasicColumn } from '../../types/table';
|
||||||
import type { EditRecordRow } from "./index";
|
import type { EditRecordRow } from './index';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
defineComponent,
|
defineComponent,
|
||||||
@ -48,24 +48,24 @@ import {
|
|||||||
computed,
|
computed,
|
||||||
watchEffect,
|
watchEffect,
|
||||||
toRaw,
|
toRaw,
|
||||||
} from "vue";
|
} from 'vue';
|
||||||
import { FormOutlined, CloseOutlined, CheckOutlined } from "@vicons/antd";
|
import { FormOutlined, CloseOutlined, CheckOutlined } from '@vicons/antd';
|
||||||
import { CellComponent } from "./CellComponent";
|
import { CellComponent } from './CellComponent';
|
||||||
|
|
||||||
import { useTableContext } from "../../hooks/useTableContext";
|
import { useTableContext } from '../../hooks/useTableContext';
|
||||||
|
|
||||||
import clickOutside from "@/directives/clickOutside";
|
import clickOutside from '@/directives/clickOutside';
|
||||||
|
|
||||||
import { propTypes } from "@/utils/propTypes";
|
import { propTypes } from '@/utils/propTypes';
|
||||||
import { isString, isBoolean, isFunction, isNumber, isArray } from "@/utils";
|
import { isString, isBoolean, isFunction, isNumber, isArray } from '@/utils';
|
||||||
import { createPlaceholderMessage } from "./helper";
|
import { createPlaceholderMessage } from './helper';
|
||||||
import { set, omit } from "lodash-es";
|
import { set, omit } from 'lodash-es';
|
||||||
import { EventEnum } from "@/components/Table/src/componentMap";
|
import { EventEnum } from '@/components/Table/src/componentMap';
|
||||||
|
|
||||||
import { parseISO, format } from "date-fns";
|
import { parseISO, format } from 'date-fns';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: "EditableCell",
|
name: 'EditableCell',
|
||||||
components: { FormOutlined, CloseOutlined, CheckOutlined, CellComponent },
|
components: { FormOutlined, CloseOutlined, CheckOutlined, CellComponent },
|
||||||
directives: {
|
directives: {
|
||||||
clickOutside,
|
clickOutside,
|
||||||
@ -75,7 +75,7 @@ export default defineComponent({
|
|||||||
type: [String, Number, Boolean, Object] as PropType<
|
type: [String, Number, Boolean, Object] as PropType<
|
||||||
string | number | boolean
|
string | number | boolean
|
||||||
>,
|
>,
|
||||||
default: "",
|
default: '',
|
||||||
},
|
},
|
||||||
record: {
|
record: {
|
||||||
type: Object as PropType<EditRecordRow>,
|
type: Object as PropType<EditRecordRow>,
|
||||||
@ -91,7 +91,7 @@ export default defineComponent({
|
|||||||
const isEdit = ref(false);
|
const isEdit = ref(false);
|
||||||
const elRef = ref();
|
const elRef = ref();
|
||||||
const ruleVisible = ref(false);
|
const ruleVisible = ref(false);
|
||||||
const ruleMessage = ref("");
|
const ruleMessage = ref('');
|
||||||
const optionsRef = ref<LabelValueOptions>([]);
|
const optionsRef = ref<LabelValueOptions>([]);
|
||||||
const currentValueRef = ref<any>(props.value);
|
const currentValueRef = ref<any>(props.value);
|
||||||
const defaultValueRef = ref<any>(props.value);
|
const defaultValueRef = ref<any>(props.value);
|
||||||
@ -99,7 +99,7 @@ export default defineComponent({
|
|||||||
// const { prefixCls } = useDesign('editable-cell');
|
// const { prefixCls } = useDesign('editable-cell');
|
||||||
|
|
||||||
const getComponent = computed(
|
const getComponent = computed(
|
||||||
() => props.column?.editComponent || "NInput"
|
() => props.column?.editComponent || 'NInput'
|
||||||
);
|
);
|
||||||
const getRule = computed(() => props.column?.editRule);
|
const getRule = computed(() => props.column?.editRule);
|
||||||
|
|
||||||
@ -109,7 +109,7 @@ export default defineComponent({
|
|||||||
|
|
||||||
const getIsCheckComp = computed(() => {
|
const getIsCheckComp = computed(() => {
|
||||||
const component = unref(getComponent);
|
const component = unref(getComponent);
|
||||||
return ["NCheckbox", "NRadio"].includes(component);
|
return ['NCheckbox', 'NRadio'].includes(component);
|
||||||
});
|
});
|
||||||
|
|
||||||
const getComponentProps = computed(() => {
|
const getComponentProps = computed(() => {
|
||||||
@ -120,7 +120,7 @@ export default defineComponent({
|
|||||||
|
|
||||||
const isCheckValue = unref(getIsCheckComp);
|
const isCheckValue = unref(getIsCheckComp);
|
||||||
|
|
||||||
let valueField = isCheckValue ? "checked" : "value";
|
let valueField = isCheckValue ? 'checked' : 'value';
|
||||||
const val = unref(currentValueRef);
|
const val = unref(currentValueRef);
|
||||||
|
|
||||||
let value = isCheckValue
|
let value = isCheckValue
|
||||||
@ -130,16 +130,16 @@ export default defineComponent({
|
|||||||
: val;
|
: val;
|
||||||
|
|
||||||
//TODO 特殊处理 NDatePicker 可能要根据项目 规范自行调整代码
|
//TODO 特殊处理 NDatePicker 可能要根据项目 规范自行调整代码
|
||||||
if (component === "NDatePicker") {
|
if (component === 'NDatePicker') {
|
||||||
if (isString(value)) {
|
if (isString(value)) {
|
||||||
if (compProps.valueFormat) {
|
if (compProps.valueFormat) {
|
||||||
valueField = "formatted-value";
|
valueField = 'formatted-value';
|
||||||
} else {
|
} else {
|
||||||
value = parseISO(value as any).getTime();
|
value = parseISO(value as any).getTime();
|
||||||
}
|
}
|
||||||
} else if (isArray(value)) {
|
} else if (isArray(value)) {
|
||||||
if (compProps.valueFormat) {
|
if (compProps.valueFormat) {
|
||||||
valueField = "formatted-value";
|
valueField = 'formatted-value';
|
||||||
} else {
|
} else {
|
||||||
value = value.map((item) => parseISO(item).getTime());
|
value = value.map((item) => parseISO(item).getTime());
|
||||||
}
|
}
|
||||||
@ -151,7 +151,7 @@ export default defineComponent({
|
|||||||
return {
|
return {
|
||||||
placeholder: createPlaceholderMessage(unref(getComponent)),
|
placeholder: createPlaceholderMessage(unref(getComponent)),
|
||||||
...apiSelectProps,
|
...apiSelectProps,
|
||||||
...omit(compProps, "onChange"),
|
...omit(compProps, 'onChange'),
|
||||||
[onEvent]: handleChange,
|
[onEvent]: handleChange,
|
||||||
[valueField]: value,
|
[valueField]: value,
|
||||||
};
|
};
|
||||||
@ -167,7 +167,7 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const component = unref(getComponent);
|
const component = unref(getComponent);
|
||||||
if (!component.includes("NSelect")) {
|
if (!component.includes('NSelect')) {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -179,7 +179,7 @@ export default defineComponent({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const getWrapperClass = computed(() => {
|
const getWrapperClass = computed(() => {
|
||||||
const { align = "center" } = props.column;
|
const { align = 'center' } = props.column;
|
||||||
return `edit-cell-align-${align}`;
|
return `edit-cell-align-${align}`;
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -201,7 +201,7 @@ export default defineComponent({
|
|||||||
|
|
||||||
function handleEdit() {
|
function handleEdit() {
|
||||||
if (unref(getRowEditable) || unref(props.column?.editRow)) return;
|
if (unref(getRowEditable) || unref(props.column?.editRow)) return;
|
||||||
ruleMessage.value = "";
|
ruleMessage.value = '';
|
||||||
isEdit.value = true;
|
isEdit.value = true;
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
const el = unref(elRef);
|
const el = unref(elRef);
|
||||||
@ -214,16 +214,16 @@ export default defineComponent({
|
|||||||
const compProps = props.column?.editComponentProps ?? {};
|
const compProps = props.column?.editComponentProps ?? {};
|
||||||
if (!e) {
|
if (!e) {
|
||||||
currentValueRef.value = e;
|
currentValueRef.value = e;
|
||||||
} else if (e?.target && Reflect.has(e.target, "value")) {
|
} else if (e?.target && Reflect.has(e.target, 'value')) {
|
||||||
currentValueRef.value = (e as ChangeEvent).target.value;
|
currentValueRef.value = (e as ChangeEvent).target.value;
|
||||||
} else if (component === "NCheckbox") {
|
} else if (component === 'NCheckbox') {
|
||||||
currentValueRef.value = (e as ChangeEvent).target.checked;
|
currentValueRef.value = (e as ChangeEvent).target.checked;
|
||||||
} else if (isString(e) || isBoolean(e) || isNumber(e)) {
|
} else if (isString(e) || isBoolean(e) || isNumber(e)) {
|
||||||
currentValueRef.value = e;
|
currentValueRef.value = e;
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO 特殊处理 NDatePicker 可能要根据项目 规范自行调整代码
|
//TODO 特殊处理 NDatePicker 可能要根据项目 规范自行调整代码
|
||||||
if (component === "NDatePicker") {
|
if (component === 'NDatePicker') {
|
||||||
if (isNumber(currentValueRef.value)) {
|
if (isNumber(currentValueRef.value)) {
|
||||||
if (compProps.valueFormat) {
|
if (compProps.valueFormat) {
|
||||||
currentValueRef.value = format(
|
currentValueRef.value = format(
|
||||||
@ -243,7 +243,7 @@ export default defineComponent({
|
|||||||
const onChange = props.column?.editComponentProps?.onChange;
|
const onChange = props.column?.editComponentProps?.onChange;
|
||||||
if (onChange && isFunction(onChange)) onChange(...arguments);
|
if (onChange && isFunction(onChange)) onChange(...arguments);
|
||||||
|
|
||||||
table.emit?.("edit-change", {
|
table.emit?.('edit-change', {
|
||||||
column: props.column,
|
column: props.column,
|
||||||
value: unref(currentValueRef),
|
value: unref(currentValueRef),
|
||||||
record: toRaw(props.record),
|
record: toRaw(props.record),
|
||||||
@ -270,12 +270,12 @@ export default defineComponent({
|
|||||||
ruleVisible.value = true;
|
ruleVisible.value = true;
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
ruleMessage.value = "";
|
ruleMessage.value = '';
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ruleMessage.value = "";
|
ruleMessage.value = '';
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -295,7 +295,7 @@ export default defineComponent({
|
|||||||
|
|
||||||
set(record, dataKey, value);
|
set(record, dataKey, value);
|
||||||
//const record = await table.updateTableData(index, dataKey, value);
|
//const record = await table.updateTableData(index, dataKey, value);
|
||||||
needEmit && table.emit?.("edit-end", { record, index, key, value });
|
needEmit && table.emit?.('edit-end', { record, index, key, value });
|
||||||
isEdit.value = false;
|
isEdit.value = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -312,8 +312,8 @@ export default defineComponent({
|
|||||||
const { column, index, record } = props;
|
const { column, index, record } = props;
|
||||||
const { key } = column;
|
const { key } = column;
|
||||||
ruleVisible.value = true;
|
ruleVisible.value = true;
|
||||||
ruleMessage.value = "";
|
ruleMessage.value = '';
|
||||||
table.emit?.("edit-cancel", {
|
table.emit?.('edit-cancel', {
|
||||||
record,
|
record,
|
||||||
index,
|
index,
|
||||||
key: key,
|
key: key,
|
||||||
@ -327,7 +327,7 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
const component = unref(getComponent);
|
const component = unref(getComponent);
|
||||||
|
|
||||||
if (component.includes("NInput")) {
|
if (component.includes('NInput')) {
|
||||||
handleCancel();
|
handleCancel();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -337,7 +337,7 @@ export default defineComponent({
|
|||||||
optionsRef.value = options;
|
optionsRef.value = options;
|
||||||
}
|
}
|
||||||
|
|
||||||
function initCbs(cbs: "submitCbs" | "validCbs" | "cancelCbs", handle: Fn) {
|
function initCbs(cbs: 'submitCbs' | 'validCbs' | 'cancelCbs', handle: Fn) {
|
||||||
if (props.record) {
|
if (props.record) {
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
isArray(props.record[cbs])
|
isArray(props.record[cbs])
|
||||||
|
|||||||
@ -1,21 +1,21 @@
|
|||||||
import { ComponentType } from "../../types/componentType";
|
import { ComponentType } from '../../types/componentType';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description: 生成placeholder
|
* @description: 生成placeholder
|
||||||
*/
|
*/
|
||||||
export function createPlaceholderMessage(component: ComponentType) {
|
export function createPlaceholderMessage(component: ComponentType) {
|
||||||
if (component === "NInput") return "请输入";
|
if (component === 'NInput') return '请输入';
|
||||||
if (
|
if (
|
||||||
[
|
[
|
||||||
"NPicker",
|
'NPicker',
|
||||||
"NSelect",
|
'NSelect',
|
||||||
"NCheckbox",
|
'NCheckbox',
|
||||||
"NRadio",
|
'NRadio',
|
||||||
"NSwitch",
|
'NSwitch',
|
||||||
"NDatePicker",
|
'NDatePicker',
|
||||||
"NTimePicker",
|
'NTimePicker',
|
||||||
].includes(component)
|
].includes(component)
|
||||||
)
|
)
|
||||||
return "请选择";
|
return '请选择';
|
||||||
return "";
|
return '';
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import type { BasicColumn } from "@/components/Table/src/types/table";
|
import type { BasicColumn } from '@/components/Table/src/types/table';
|
||||||
import { h, Ref } from "vue";
|
import { h, Ref } from 'vue';
|
||||||
|
|
||||||
import EditableCell from "./EditableCell.vue";
|
import EditableCell from './EditableCell.vue';
|
||||||
|
|
||||||
export function renderEditCell(column: BasicColumn) {
|
export function renderEditCell(column: BasicColumn) {
|
||||||
return (record, index) => {
|
return (record, index) => {
|
||||||
|
|||||||
@ -118,25 +118,25 @@ import {
|
|||||||
computed,
|
computed,
|
||||||
toRefs,
|
toRefs,
|
||||||
watchEffect,
|
watchEffect,
|
||||||
} from "vue";
|
} from 'vue';
|
||||||
import { useTableContext } from "../../hooks/useTableContext";
|
import { useTableContext } from '../../hooks/useTableContext';
|
||||||
import { cloneDeep } from "lodash-es";
|
import { cloneDeep } from 'lodash-es';
|
||||||
import {
|
import {
|
||||||
SettingOutlined,
|
SettingOutlined,
|
||||||
DragOutlined,
|
DragOutlined,
|
||||||
VerticalRightOutlined,
|
VerticalRightOutlined,
|
||||||
VerticalLeftOutlined,
|
VerticalLeftOutlined,
|
||||||
} from "@vicons/antd";
|
} from '@vicons/antd';
|
||||||
import Draggable from "vuedraggable";
|
import Draggable from 'vuedraggable';
|
||||||
|
|
||||||
interface Options {
|
interface Options {
|
||||||
title: string;
|
title: string;
|
||||||
key: string;
|
key: string;
|
||||||
fixed?: boolean | "left" | "right";
|
fixed?: boolean | 'left' | 'right';
|
||||||
}
|
}
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: "ColumnSetting",
|
name: 'ColumnSetting',
|
||||||
components: {
|
components: {
|
||||||
SettingOutlined,
|
SettingOutlined,
|
||||||
DragOutlined,
|
DragOutlined,
|
||||||
@ -174,7 +174,7 @@ export default defineComponent({
|
|||||||
state.checkList = checkList;
|
state.checkList = checkList;
|
||||||
state.defaultCheckList = checkList;
|
state.defaultCheckList = checkList;
|
||||||
const newColumns = columns.filter(
|
const newColumns = columns.filter(
|
||||||
(item) => item.key != "action" && item.title != "操作"
|
(item) => item.key != 'action' && item.title != '操作'
|
||||||
);
|
);
|
||||||
if (!columnsList.value.length) {
|
if (!columnsList.value.length) {
|
||||||
columnsList.value = cloneDeep(newColumns);
|
columnsList.value = cloneDeep(newColumns);
|
||||||
@ -185,7 +185,7 @@ export default defineComponent({
|
|||||||
//切换
|
//切换
|
||||||
function onChange(checkList) {
|
function onChange(checkList) {
|
||||||
if (state.selection) {
|
if (state.selection) {
|
||||||
checkList.unshift("selection");
|
checkList.unshift('selection');
|
||||||
}
|
}
|
||||||
setColumns(checkList);
|
setColumns(checkList);
|
||||||
}
|
}
|
||||||
@ -242,7 +242,7 @@ export default defineComponent({
|
|||||||
function onSelection(e) {
|
function onSelection(e) {
|
||||||
let checkList = table.getCacheColumns();
|
let checkList = table.getCacheColumns();
|
||||||
if (e) {
|
if (e) {
|
||||||
checkList.unshift({ type: "selection", key: "selection" });
|
checkList.unshift({ type: 'selection', key: 'selection' });
|
||||||
setColumns(checkList);
|
setColumns(checkList);
|
||||||
} else {
|
} else {
|
||||||
checkList.splice(0, 1);
|
checkList.splice(0, 1);
|
||||||
@ -257,7 +257,7 @@ export default defineComponent({
|
|||||||
|
|
||||||
//固定
|
//固定
|
||||||
function fixedColumn(item: any, fixed: any) {
|
function fixedColumn(item: any, fixed: any) {
|
||||||
if (!state.checkList.includes(item.key)) return;
|
if (!state.checkList.includes(item.key as never)) return;
|
||||||
let columns = getColumns();
|
let columns = getColumns();
|
||||||
const isFixed = item.fixed === fixed ? undefined : fixed;
|
const isFixed = item.fixed === fixed ? undefined : fixed;
|
||||||
let index = columns.findIndex((res) => res.key === item.key);
|
let index = columns.findIndex((res) => res.key === item.key);
|
||||||
|
|||||||
@ -3,15 +3,15 @@
|
|||||||
const table = {
|
const table = {
|
||||||
apiSetting: {
|
apiSetting: {
|
||||||
// 当前页的字段名
|
// 当前页的字段名
|
||||||
pageField: "current",
|
pageField: 'current',
|
||||||
// 每页数量字段名
|
// 每页数量字段名
|
||||||
sizeField: "size",
|
sizeField: 'size',
|
||||||
// 接口返回的数据字段名
|
// 接口返回的数据字段名
|
||||||
listField: "records",
|
listField: 'records',
|
||||||
// 接口返回总页数字段名
|
// 接口返回总页数字段名
|
||||||
totalField: "pages",
|
totalField: 'pages',
|
||||||
//总数字段名
|
//总数字段名
|
||||||
countField: "total",
|
countField: 'total',
|
||||||
},
|
},
|
||||||
//默认分页数量
|
//默认分页数量
|
||||||
defaultPageSize: 10,
|
defaultPageSize: 10,
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { ref, Ref, ComputedRef, unref, computed, watch, toRaw } from "vue";
|
import { ref, Ref, ComputedRef, unref, computed, watch, toRaw } from 'vue';
|
||||||
import type { BasicColumn, BasicTableProps } from "../types/table";
|
import type { BasicColumn, BasicTableProps } from '../types/table';
|
||||||
import { isEqual, cloneDeep } from "lodash-es";
|
import { isEqual, cloneDeep } from 'lodash-es';
|
||||||
import { isArray, isString } from "@/utils";
|
import { isArray, isString } from '@/utils';
|
||||||
// import { usePermission } from "@/hooks/web/usePermission";
|
// import { usePermission } from "@/hooks/web/usePermission";
|
||||||
// import { ActionItem } from "@/components/Table";
|
// import { ActionItem } from "@/components/Table";
|
||||||
// import { renderEditCell } from "../components/editable";
|
// import { renderEditCell } from "../components/editable";
|
||||||
@ -65,7 +65,7 @@ export function useColumns(propsRef: ComputedRef<BasicTableProps>) {
|
|||||||
) {
|
) {
|
||||||
const { actionColumn } = unref(propsRef);
|
const { actionColumn } = unref(propsRef);
|
||||||
if (!actionColumn) return;
|
if (!actionColumn) return;
|
||||||
!columns.find((col) => col.key === "action") &&
|
!columns.find((col) => col.key === 'action') &&
|
||||||
columns.push({
|
columns.push({
|
||||||
...(actionColumn as any),
|
...(actionColumn as any),
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import type { BasicTableProps } from "../types/table";
|
import type { BasicTableProps } from '../types/table';
|
||||||
import type { PaginationProps } from "../types/pagination";
|
import type { PaginationProps } from '../types/pagination';
|
||||||
import { isBoolean, isFunction } from "@/utils/is";
|
import { isBoolean, isFunction } from '@/utils/is';
|
||||||
import { APISETTING } from "../const";
|
import { APISETTING } from '../const';
|
||||||
|
|
||||||
export function useDataSource(
|
export function useDataSource(
|
||||||
propsRef: ComputedRef<BasicTableProps>,
|
propsRef: ComputedRef<BasicTableProps>,
|
||||||
@ -30,7 +30,7 @@ export function useDataSource(
|
|||||||
return rowKey
|
return rowKey
|
||||||
? rowKey
|
? rowKey
|
||||||
: () => {
|
: () => {
|
||||||
return "key";
|
return 'key';
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -115,13 +115,13 @@ export function useDataSource(
|
|||||||
page: opt[pageField] || 1,
|
page: opt[pageField] || 1,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
emit("fetch-success", {
|
emit('fetch-success', {
|
||||||
items: unref(resultInfo),
|
items: unref(resultInfo),
|
||||||
resultTotal,
|
resultTotal,
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
emit("fetch-error", error);
|
emit('fetch-error', error);
|
||||||
dataSourceRef.value = [];
|
dataSourceRef.value = [];
|
||||||
setPagination({
|
setPagination({
|
||||||
pageCount: 0,
|
pageCount: 0,
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { ref, ComputedRef, unref, computed, watch } from "vue";
|
import { ref, ComputedRef, unref, computed, watch } from 'vue';
|
||||||
import type { BasicTableProps } from "../types/table";
|
import type { BasicTableProps } from '../types/table';
|
||||||
|
|
||||||
export function useLoading(props: ComputedRef<BasicTableProps>) {
|
export function useLoading(props: ComputedRef<BasicTableProps>) {
|
||||||
const loadingRef = ref(unref(props).loading);
|
const loadingRef = ref(unref(props).loading);
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
import type { PaginationProps } from "../types/pagination";
|
import type { PaginationProps } from '../types/pagination';
|
||||||
import type { BasicTableProps } from "../types/table";
|
import type { BasicTableProps } from '../types/table';
|
||||||
import { computed, unref, ref, ComputedRef, watch } from "vue";
|
import { computed, unref, ref, ComputedRef, watch } from 'vue';
|
||||||
|
|
||||||
import { isBoolean } from "@/utils/is";
|
import { isBoolean } from '@/utils/is';
|
||||||
import { DEFAULTPAGESIZE, PAGESIZES } from "../const";
|
import { DEFAULTPAGESIZE, PAGESIZES } from '../const';
|
||||||
|
|
||||||
export function usePagination(refProps: ComputedRef<BasicTableProps>) {
|
export function usePagination(refProps: ComputedRef<BasicTableProps>) {
|
||||||
const configRef = ref<PaginationProps>({});
|
const configRef = ref<PaginationProps>({});
|
||||||
|
|||||||
@ -1,15 +1,15 @@
|
|||||||
import type { Ref } from "vue";
|
import type { Ref } from 'vue';
|
||||||
import type { BasicTableProps, TableActionType } from "../types/table";
|
import type { BasicTableProps, TableActionType } from '../types/table';
|
||||||
import { provide, inject, ComputedRef } from "vue";
|
import { provide, inject, ComputedRef } from 'vue';
|
||||||
|
|
||||||
const key = Symbol("s-table");
|
const key = Symbol('s-table');
|
||||||
|
|
||||||
type Instance = TableActionType & {
|
type Instance = TableActionType & {
|
||||||
wrapRef: Ref<Nullable<HTMLElement>>;
|
wrapRef: Ref<Nullable<HTMLElement>>;
|
||||||
getBindValues: ComputedRef<any>;
|
getBindValues: ComputedRef<any>;
|
||||||
};
|
};
|
||||||
|
|
||||||
type RetInstance = Omit<Instance, "getBindValues"> & {
|
type RetInstance = Omit<Instance, 'getBindValues'> & {
|
||||||
getBindValues: ComputedRef<BasicTableProps>;
|
getBindValues: ComputedRef<BasicTableProps>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import type { PropType } from "vue";
|
import type { PropType } from 'vue';
|
||||||
import { propTypes } from "@/utils/propTypes";
|
import { propTypes } from '@/utils/propTypes';
|
||||||
import { BasicColumn } from "./types/table";
|
import { BasicColumn } from './types/table';
|
||||||
import { NDataTable } from "naive-ui";
|
import { NDataTable } from 'naive-ui';
|
||||||
export const basicProps = {
|
export const basicProps = {
|
||||||
...NDataTable.props, // 这里继承原 UI 组件的 props
|
...NDataTable.props, // 这里继承原 UI 组件的 props
|
||||||
title: {
|
title: {
|
||||||
@ -14,7 +14,7 @@ export const basicProps = {
|
|||||||
},
|
},
|
||||||
size: {
|
size: {
|
||||||
type: String,
|
type: String,
|
||||||
default: "medium",
|
default: 'medium',
|
||||||
},
|
},
|
||||||
dataSource: {
|
dataSource: {
|
||||||
type: [Object],
|
type: [Object],
|
||||||
@ -48,7 +48,7 @@ export const basicProps = {
|
|||||||
//废弃
|
//废弃
|
||||||
showPagination: {
|
showPagination: {
|
||||||
type: [String, Boolean],
|
type: [String, Boolean],
|
||||||
default: "auto",
|
default: 'auto',
|
||||||
},
|
},
|
||||||
actionColumn: {
|
actionColumn: {
|
||||||
type: Object as PropType<BasicColumn>,
|
type: Object as PropType<BasicColumn>,
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
export type ComponentType =
|
export type ComponentType =
|
||||||
| "NInput"
|
| 'NInput'
|
||||||
| "NInputNumber"
|
| 'NInputNumber'
|
||||||
| "NSelect"
|
| 'NSelect'
|
||||||
| "NCheckbox"
|
| 'NCheckbox'
|
||||||
| "NSwitch"
|
| 'NSwitch'
|
||||||
| "NDatePicker"
|
| 'NDatePicker'
|
||||||
| "NTimePicker"
|
| 'NTimePicker'
|
||||||
| "NCascader";
|
| 'NCascader';
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
import type {
|
import type {
|
||||||
InternalRowData,
|
InternalRowData,
|
||||||
TableBaseColumn,
|
TableBaseColumn,
|
||||||
} from "naive-ui/lib/data-table/src/interface";
|
} from 'naive-ui/lib/data-table/src/interface';
|
||||||
import { ComponentType } from "./componentType";
|
import { ComponentType } from './componentType';
|
||||||
export interface BasicColumn<T = InternalRowData> extends TableBaseColumn<T> {
|
export interface BasicColumn<T = InternalRowData> extends TableBaseColumn<T> {
|
||||||
//编辑表格
|
//编辑表格
|
||||||
edit?: boolean;
|
edit?: boolean;
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
import { NButton } from "naive-ui";
|
import { NButton } from 'naive-ui';
|
||||||
import type { Component } from "vue";
|
import type { Component } from 'vue';
|
||||||
// import { PermissionsEnum } from "@/enums/permissionsEnum";
|
// import { PermissionsEnum } from "@/enums/permissionsEnum";
|
||||||
import { Fn } from "@vueuse/core";
|
import { Fn } from '@vueuse/core';
|
||||||
export interface ActionItem extends Partial<InstanceType<typeof NButton>> {
|
export interface ActionItem extends Partial<InstanceType<typeof NButton>> {
|
||||||
onClick?: Fn;
|
onClick?: Fn;
|
||||||
label?: string;
|
label?: string;
|
||||||
type?: "success" | "error" | "warning" | "info" | "primary" | "default";
|
type?: 'success' | 'error' | 'warning' | 'info' | 'primary' | 'default';
|
||||||
// 设定 color 后会覆盖 type 的样式
|
// 设定 color 后会覆盖 type 的样式
|
||||||
color?: string;
|
color?: string;
|
||||||
icon?: Component;
|
icon?: Component;
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import logoImage from "@/assets/images/logo.png";
|
import logoImage from '@/assets/svgs/logo.svg';
|
||||||
interface WebsiteConfig {
|
interface WebsiteConfig {
|
||||||
title: string;
|
title: string;
|
||||||
logo: string;
|
logo: string;
|
||||||
@ -6,8 +6,8 @@ interface WebsiteConfig {
|
|||||||
loginDesc: string;
|
loginDesc: string;
|
||||||
}
|
}
|
||||||
export const websiteConfig: WebsiteConfig = Object.freeze({
|
export const websiteConfig: WebsiteConfig = Object.freeze({
|
||||||
title: "中盛起元基础框架",
|
title: 'ZS-Naiveui-Admin',
|
||||||
logo: logoImage,
|
logo: logoImage,
|
||||||
loginImage: logoImage,
|
loginImage: logoImage,
|
||||||
loginDesc: "中盛起元基础框架",
|
loginDesc: 'ZS-Naiveui-Admin',
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
import { on, isServer } from "@/utils";
|
import { on, isServer } from '@/utils';
|
||||||
// import { isServer } from "@/utils";
|
// import { isServer } from "@/utils";
|
||||||
import type {
|
import type {
|
||||||
ComponentPublicInstance,
|
ComponentPublicInstance,
|
||||||
DirectiveBinding,
|
DirectiveBinding,
|
||||||
ObjectDirective,
|
ObjectDirective,
|
||||||
} from "vue";
|
} from 'vue';
|
||||||
|
|
||||||
type DocumentHandler = <T extends MouseEvent>(mouseup: T, mousedown: T) => void;
|
type DocumentHandler = <T extends MouseEvent>(mouseup: T, mousedown: T) => void;
|
||||||
|
|
||||||
@ -21,8 +21,8 @@ const nodeList: FlushList = new Map();
|
|||||||
let startClick: MouseEvent;
|
let startClick: MouseEvent;
|
||||||
|
|
||||||
if (!isServer) {
|
if (!isServer) {
|
||||||
on(document, "mousedown", (e: MouseEvent) => (startClick = e));
|
on(document, 'mousedown', (e: MouseEvent) => (startClick = e));
|
||||||
on(document, "mouseup", (e: MouseEvent) => {
|
on(document, 'mouseup', (e: MouseEvent) => {
|
||||||
for (const { documentHandler } of nodeList.values()) {
|
for (const { documentHandler } of nodeList.values()) {
|
||||||
documentHandler(e, startClick);
|
documentHandler(e, startClick);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
* 复制某个值至剪贴板
|
* 复制某个值至剪贴板
|
||||||
* 接收参数:string类型/Ref<string>类型/Reactive<string>类型
|
* 接收参数:string类型/Ref<string>类型/Reactive<string>类型
|
||||||
*/
|
*/
|
||||||
import type { Directive, DirectiveBinding } from "vue";
|
import type { Directive, DirectiveBinding } from 'vue';
|
||||||
|
|
||||||
interface ElType extends HTMLElement {
|
interface ElType extends HTMLElement {
|
||||||
copyData: string | number;
|
copyData: string | number;
|
||||||
@ -12,23 +12,23 @@ interface ElType extends HTMLElement {
|
|||||||
const copy: Directive = {
|
const copy: Directive = {
|
||||||
mounted(el: ElType, binding: DirectiveBinding) {
|
mounted(el: ElType, binding: DirectiveBinding) {
|
||||||
el.copyData = binding.value;
|
el.copyData = binding.value;
|
||||||
el.addEventListener("click", handleClick);
|
el.addEventListener('click', handleClick);
|
||||||
},
|
},
|
||||||
updated(el: ElType, binding: DirectiveBinding) {
|
updated(el: ElType, binding: DirectiveBinding) {
|
||||||
el.copyData = binding.value;
|
el.copyData = binding.value;
|
||||||
},
|
},
|
||||||
beforeUnmount(el: ElType) {
|
beforeUnmount(el: ElType) {
|
||||||
el.removeEventListener("click", el.__handleClick__);
|
el.removeEventListener('click', el.__handleClick__);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
function handleClick(this: any) {
|
function handleClick(this: any) {
|
||||||
const input = document.createElement("input");
|
const input = document.createElement('input');
|
||||||
input.value = this.copyData.toLocaleString();
|
input.value = this.copyData.toLocaleString();
|
||||||
document.body.appendChild(input);
|
document.body.appendChild(input);
|
||||||
input.select();
|
input.select();
|
||||||
document.execCommand("Copy");
|
document.execCommand('Copy');
|
||||||
document.body.removeChild(input);
|
document.body.removeChild(input);
|
||||||
console.log("复制成功", this.copyData);
|
console.log('复制成功', this.copyData);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default copy;
|
export default copy;
|
||||||
|
|||||||
@ -3,14 +3,14 @@
|
|||||||
* 按钮防抖指令,可自行扩展至input
|
* 按钮防抖指令,可自行扩展至input
|
||||||
* 接收参数:function类型
|
* 接收参数:function类型
|
||||||
*/
|
*/
|
||||||
import type { Directive, DirectiveBinding } from "vue";
|
import type { Directive, DirectiveBinding } from 'vue';
|
||||||
interface ElType extends HTMLElement {
|
interface ElType extends HTMLElement {
|
||||||
__handleClick__: () => any;
|
__handleClick__: () => any;
|
||||||
}
|
}
|
||||||
const debounce: Directive = {
|
const debounce: Directive = {
|
||||||
mounted(el: ElType, binding: DirectiveBinding) {
|
mounted(el: ElType, binding: DirectiveBinding) {
|
||||||
if (typeof binding.value !== "function") {
|
if (typeof binding.value !== 'function') {
|
||||||
throw "callback must be a function";
|
throw 'callback must be a function';
|
||||||
}
|
}
|
||||||
let timer: NodeJS.Timeout | null = null;
|
let timer: NodeJS.Timeout | null = null;
|
||||||
el.__handleClick__ = function () {
|
el.__handleClick__ = function () {
|
||||||
@ -21,10 +21,10 @@ const debounce: Directive = {
|
|||||||
binding.value();
|
binding.value();
|
||||||
}, 500);
|
}, 500);
|
||||||
};
|
};
|
||||||
el.addEventListener("click", el.__handleClick__);
|
el.addEventListener('click', el.__handleClick__);
|
||||||
},
|
},
|
||||||
beforeUnmount(el: ElType) {
|
beforeUnmount(el: ElType) {
|
||||||
el.removeEventListener("click", el.__handleClick__);
|
el.removeEventListener('click', el.__handleClick__);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -10,14 +10,14 @@
|
|||||||
使用:在 Dom 上加上 v-draggable 即可
|
使用:在 Dom 上加上 v-draggable 即可
|
||||||
<div class="dialog-model" v-draggable></div>
|
<div class="dialog-model" v-draggable></div>
|
||||||
*/
|
*/
|
||||||
import type { Directive } from "vue";
|
import type { Directive } from 'vue';
|
||||||
interface ElType extends HTMLElement {
|
interface ElType extends HTMLElement {
|
||||||
parentNode: any;
|
parentNode: any;
|
||||||
}
|
}
|
||||||
const draggable: Directive = {
|
const draggable: Directive = {
|
||||||
mounted: function (el: ElType) {
|
mounted: function (el: ElType) {
|
||||||
el.style.cursor = "move";
|
el.style.cursor = 'move';
|
||||||
el.style.position = "absolute";
|
el.style.position = 'absolute';
|
||||||
el.onmousedown = function (e) {
|
el.onmousedown = function (e) {
|
||||||
const disX = e.pageX - el.offsetLeft;
|
const disX = e.pageX - el.offsetLeft;
|
||||||
const disY = e.pageY - el.offsetTop;
|
const disY = e.pageY - el.offsetTop;
|
||||||
@ -37,8 +37,8 @@ const draggable: Directive = {
|
|||||||
} else if (y > maxY) {
|
} else if (y > maxY) {
|
||||||
y = maxY;
|
y = maxY;
|
||||||
}
|
}
|
||||||
el.style.left = x + "px";
|
el.style.left = x + 'px';
|
||||||
el.style.top = y + "px";
|
el.style.top = y + 'px';
|
||||||
};
|
};
|
||||||
document.onmouseup = function () {
|
document.onmouseup = function () {
|
||||||
document.onmousemove = document.onmouseup = null;
|
document.onmousemove = document.onmouseup = null;
|
||||||
|
|||||||
@ -2,19 +2,19 @@
|
|||||||
* v-longpress
|
* v-longpress
|
||||||
* 长按指令,长按时触发事件
|
* 长按指令,长按时触发事件
|
||||||
*/
|
*/
|
||||||
import type { Directive, DirectiveBinding } from "vue";
|
import type { Directive, DirectiveBinding } from 'vue';
|
||||||
|
|
||||||
const directive: Directive = {
|
const directive: Directive = {
|
||||||
mounted(el: HTMLElement, binding: DirectiveBinding) {
|
mounted(el: HTMLElement, binding: DirectiveBinding) {
|
||||||
if (typeof binding.value !== "function") {
|
if (typeof binding.value !== 'function') {
|
||||||
throw "callback must be a function";
|
throw 'callback must be a function';
|
||||||
}
|
}
|
||||||
// 定义变量
|
// 定义变量
|
||||||
let pressTimer: any = null;
|
let pressTimer: any = null;
|
||||||
// 创建计时器( 2秒后执行函数 )
|
// 创建计时器( 2秒后执行函数 )
|
||||||
const start = (e: any) => {
|
const start = (e: any) => {
|
||||||
if (e.button) {
|
if (e.button) {
|
||||||
if (e.type === "click" && e.button !== 0) {
|
if (e.type === 'click' && e.button !== 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -36,13 +36,13 @@ const directive: Directive = {
|
|||||||
binding.value(e);
|
binding.value(e);
|
||||||
};
|
};
|
||||||
// 添加事件监听器
|
// 添加事件监听器
|
||||||
el.addEventListener("mousedown", start);
|
el.addEventListener('mousedown', start);
|
||||||
el.addEventListener("touchstart", start);
|
el.addEventListener('touchstart', start);
|
||||||
// 取消计时器
|
// 取消计时器
|
||||||
el.addEventListener("click", cancel);
|
el.addEventListener('click', cancel);
|
||||||
el.addEventListener("mouseout", cancel);
|
el.addEventListener('mouseout', cancel);
|
||||||
el.addEventListener("touchend", cancel);
|
el.addEventListener('touchend', cancel);
|
||||||
el.addEventListener("touchcancel", cancel);
|
el.addEventListener('touchcancel', cancel);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -8,15 +8,15 @@
|
|||||||
使用:给 Dom 加上 v-throttle 及回调函数即可
|
使用:给 Dom 加上 v-throttle 及回调函数即可
|
||||||
<button v-throttle="debounceClick">节流提交</button>
|
<button v-throttle="debounceClick">节流提交</button>
|
||||||
*/
|
*/
|
||||||
import type { Directive, DirectiveBinding } from "vue";
|
import type { Directive, DirectiveBinding } from 'vue';
|
||||||
interface ElType extends HTMLElement {
|
interface ElType extends HTMLElement {
|
||||||
__handleClick__: () => any;
|
__handleClick__: () => any;
|
||||||
disabled: boolean;
|
disabled: boolean;
|
||||||
}
|
}
|
||||||
const throttle: Directive = {
|
const throttle: Directive = {
|
||||||
mounted(el: ElType, binding: DirectiveBinding) {
|
mounted(el: ElType, binding: DirectiveBinding) {
|
||||||
if (typeof binding.value !== "function") {
|
if (typeof binding.value !== 'function') {
|
||||||
throw "callback must be a function";
|
throw 'callback must be a function';
|
||||||
}
|
}
|
||||||
let timer: NodeJS.Timeout | null = null;
|
let timer: NodeJS.Timeout | null = null;
|
||||||
el.__handleClick__ = function () {
|
el.__handleClick__ = function () {
|
||||||
@ -31,10 +31,10 @@ const throttle: Directive = {
|
|||||||
}, 1000);
|
}, 1000);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
el.addEventListener("click", el.__handleClick__);
|
el.addEventListener('click', el.__handleClick__);
|
||||||
},
|
},
|
||||||
beforeUnmount(el: ElType) {
|
beforeUnmount(el: ElType) {
|
||||||
el.removeEventListener("click", el.__handleClick__);
|
el.removeEventListener('click', el.__handleClick__);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -31,11 +31,11 @@ export enum RequestContentTypeEnum {
|
|||||||
* @description: 请求方法
|
* @description: 请求方法
|
||||||
*/
|
*/
|
||||||
export enum RequestHttpEnum {
|
export enum RequestHttpEnum {
|
||||||
GET = "get",
|
GET = 'get',
|
||||||
POST = "post",
|
POST = 'post',
|
||||||
PATCH = "patch",
|
PATCH = 'patch',
|
||||||
PUT = "put",
|
PUT = 'put',
|
||||||
DELETE = "delete",
|
DELETE = 'delete',
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -43,34 +43,34 @@ export enum RequestHttpEnum {
|
|||||||
*/
|
*/
|
||||||
export enum RequestHttpIntervalEnum {
|
export enum RequestHttpIntervalEnum {
|
||||||
// 秒
|
// 秒
|
||||||
SECOND = "second",
|
SECOND = 'second',
|
||||||
// 分
|
// 分
|
||||||
MINUTE = "minute",
|
MINUTE = 'minute',
|
||||||
// 时
|
// 时
|
||||||
HOUR = "hour",
|
HOUR = 'hour',
|
||||||
// 天
|
// 天
|
||||||
DAY = "day",
|
DAY = 'day',
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description: 请求间隔名称
|
* @description: 请求间隔名称
|
||||||
*/
|
*/
|
||||||
export const SelectHttpTimeNameObj = {
|
export const SelectHttpTimeNameObj = {
|
||||||
[RequestHttpIntervalEnum.SECOND]: "秒",
|
[RequestHttpIntervalEnum.SECOND]: '秒',
|
||||||
[RequestHttpIntervalEnum.MINUTE]: "分",
|
[RequestHttpIntervalEnum.MINUTE]: '分',
|
||||||
[RequestHttpIntervalEnum.HOUR]: "时",
|
[RequestHttpIntervalEnum.HOUR]: '时',
|
||||||
[RequestHttpIntervalEnum.DAY]: "天",
|
[RequestHttpIntervalEnum.DAY]: '天',
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description: 请求头部类型
|
* @description: 请求头部类型
|
||||||
*/
|
*/
|
||||||
export enum RequestBodyEnum {
|
export enum RequestBodyEnum {
|
||||||
NONE = "none",
|
NONE = 'none',
|
||||||
FORM_DATA = "form-data",
|
FORM_DATA = 'form-data',
|
||||||
X_WWW_FORM_URLENCODED = "x-www-form-urlencoded",
|
X_WWW_FORM_URLENCODED = 'x-www-form-urlencoded',
|
||||||
JSON = "json",
|
JSON = 'json',
|
||||||
XML = "xml",
|
XML = 'xml',
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -88,9 +88,9 @@ export const RequestBodyEnumList = [
|
|||||||
* @description: 请求参数类型
|
* @description: 请求参数类型
|
||||||
*/
|
*/
|
||||||
export enum RequestParamsTypeEnum {
|
export enum RequestParamsTypeEnum {
|
||||||
PARAMS = "Params",
|
PARAMS = 'Params',
|
||||||
BODY = "Body",
|
BODY = 'Body',
|
||||||
HEADER = "Header",
|
HEADER = 'Header',
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -115,15 +115,15 @@ export type RequestParams = {
|
|||||||
*/
|
*/
|
||||||
export enum ContentTypeEnum {
|
export enum ContentTypeEnum {
|
||||||
// json
|
// json
|
||||||
JSON = "application/json;charset=UTF-8",
|
JSON = 'application/json;charset=UTF-8',
|
||||||
// text
|
// text
|
||||||
TEXT = "text/plain;charset=UTF-8",
|
TEXT = 'text/plain;charset=UTF-8',
|
||||||
// xml
|
// xml
|
||||||
XML = "application/xml;charset=UTF-8",
|
XML = 'application/xml;charset=UTF-8',
|
||||||
// application/x-www-form-urlencoded 一般配合qs
|
// application/x-www-form-urlencoded 一般配合qs
|
||||||
FORM_URLENCODED = "application/x-www-form-urlencoded;charset=UTF-8",
|
FORM_URLENCODED = 'application/x-www-form-urlencoded;charset=UTF-8',
|
||||||
// form-data 上传
|
// form-data 上传
|
||||||
FORM_DATA = "multipart/form-data;charset=UTF-8",
|
FORM_DATA = 'multipart/form-data;charset=UTF-8',
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|||||||
@ -1,18 +1,20 @@
|
|||||||
import { ResultEnum } from "@/enums/httpEnum";
|
import { ResultEnum } from '@/enums/httpEnum';
|
||||||
|
|
||||||
export enum PageEnum {
|
export enum PageEnum {
|
||||||
// 登录
|
// 登录
|
||||||
BASE_LOGIN = "/login",
|
BASE_LOGIN = '/login',
|
||||||
BASE_LOGIN_NAME = "lOGIN",
|
BASE_LOGIN_NAME = 'lOGIN',
|
||||||
//重定向
|
//重定向
|
||||||
REDIRECT = "/redirect",
|
REDIRECT = '/redirect',
|
||||||
REDIRECT_NAME = "Redirect",
|
REDIRECT_NAME = 'Redirect',
|
||||||
// 首页
|
// 首页
|
||||||
BASE_HOME = "/system/role",
|
BASE_HOME = '/system/role',
|
||||||
// 错误
|
// 错误
|
||||||
ERROR_PAGE_NAME_403 = "ErrorPage403",
|
ERROR_PAGE_NAME_403 = 'ErrorPage403',
|
||||||
ERROR_PAGE_NAME_404 = "ErrorPage404",
|
ERROR_PAGE_NAME_404 = 'ErrorPage404',
|
||||||
// ERROR_PAGE_NAME_500 = "ErrorPage500",
|
// ERROR_PAGE_NAME_500 = "ErrorPage500",
|
||||||
|
// 错误
|
||||||
|
ERROR_PAGE_NAME = 'ErrorPage',
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ErrorPageNameMap = new Map([
|
export const ErrorPageNameMap = new Map([
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
export enum DialogEnum {
|
export enum DialogEnum {
|
||||||
DELETE = "delete",
|
DELETE = 'delete',
|
||||||
WARNING = "warning",
|
WARNING = 'warning',
|
||||||
ERROR = "error",
|
ERROR = 'error',
|
||||||
SUCCESS = "success",
|
SUCCESS = 'success',
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,10 +1,12 @@
|
|||||||
export enum StorageEnum {
|
export enum StorageEnum {
|
||||||
// 全局设置
|
|
||||||
ZS_SYSTEM_SETTING_STORE = "YSTEM_SETTING",
|
|
||||||
// token 等信息
|
// token 等信息
|
||||||
ZS_ACCESS_TOKEN = "ACCESS_TOKEN",
|
ZS_ACCESS_TOKEN = 'ACCESS_TOKEN',
|
||||||
// 登录信息
|
// 登录信息
|
||||||
ZS_LOGIN_INFO_STORE = "LOGIN_INFO",
|
ZS_LOGIN_INFO_STORE = 'LOGIN_INFO',
|
||||||
// 当前用户信息
|
// 当前用户信息
|
||||||
ZS_CURRENT_USER = "CURRENT_USER",
|
ZS_CURRENT_USER = 'CURRENT_USER',
|
||||||
|
// 标签页
|
||||||
|
ZS_TABS_ROUTES = 'TABS_ROUTES',
|
||||||
|
// 是否锁屏
|
||||||
|
IS_SCREENLOCKED = 'IS_SCREENLOCKED',
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { tryOnMounted, tryOnUnmounted } from "@vueuse/core";
|
import { tryOnMounted, tryOnUnmounted } from '@vueuse/core';
|
||||||
import { useDebounceFn } from "@vueuse/core";
|
import { useDebounceFn } from '@vueuse/core';
|
||||||
|
|
||||||
interface WindowSizeOptions {
|
interface WindowSizeOptions {
|
||||||
once?: boolean;
|
once?: boolean;
|
||||||
@ -22,11 +22,11 @@ export function useWindowSizeFn(
|
|||||||
if (options && options.immediate) {
|
if (options && options.immediate) {
|
||||||
handler();
|
handler();
|
||||||
}
|
}
|
||||||
window.addEventListener("resize", handler);
|
window.addEventListener('resize', handler);
|
||||||
};
|
};
|
||||||
|
|
||||||
const stop = () => {
|
const stop = () => {
|
||||||
window.removeEventListener("resize", handler);
|
window.removeEventListener('resize', handler);
|
||||||
};
|
};
|
||||||
|
|
||||||
tryOnMounted(() => {
|
tryOnMounted(() => {
|
||||||
|
|||||||
18
src/hooks/setting/useDesignSetting.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import { computed } from 'vue';
|
||||||
|
import { useDesignSettingStore } from '@/store/modules/designSetting';
|
||||||
|
|
||||||
|
export function useDesignSetting() {
|
||||||
|
const designStore = useDesignSettingStore();
|
||||||
|
|
||||||
|
const getDarkTheme = computed(() => designStore.darkTheme);
|
||||||
|
|
||||||
|
const getAppTheme = computed(() => designStore.appTheme);
|
||||||
|
|
||||||
|
const getAppThemeList = computed(() => designStore.appThemeList);
|
||||||
|
|
||||||
|
return {
|
||||||
|
getDarkTheme,
|
||||||
|
getAppTheme,
|
||||||
|
getAppThemeList,
|
||||||
|
};
|
||||||
|
}
|
||||||
42
src/hooks/setting/useProjectSetting.ts
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
import { computed } from 'vue';
|
||||||
|
import { useProjectSettingStore } from '@/store/modules/projectSetting';
|
||||||
|
|
||||||
|
export function useProjectSetting() {
|
||||||
|
const projectStore = useProjectSettingStore();
|
||||||
|
|
||||||
|
const navMode = computed(() => projectStore.navMode);
|
||||||
|
|
||||||
|
const navTheme = computed(() => projectStore.navTheme);
|
||||||
|
|
||||||
|
const isMobile = computed(() => projectStore.isMobile);
|
||||||
|
|
||||||
|
const headerSetting = computed(() => projectStore.headerSetting);
|
||||||
|
|
||||||
|
const multiTabsSetting = computed(() => projectStore.multiTabsSetting);
|
||||||
|
|
||||||
|
const menuSetting = computed(() => projectStore.menuSetting);
|
||||||
|
|
||||||
|
const crumbsSetting = computed(() => projectStore.crumbsSetting);
|
||||||
|
|
||||||
|
// const permissionMode = computed(() => projectStore.permissionMode);
|
||||||
|
|
||||||
|
const showFooter = computed(() => projectStore.showFooter);
|
||||||
|
|
||||||
|
const isPageAnimate = computed(() => projectStore.isPageAnimate);
|
||||||
|
|
||||||
|
const pageAnimateType = computed(() => projectStore.pageAnimateType);
|
||||||
|
|
||||||
|
return {
|
||||||
|
navMode,
|
||||||
|
navTheme,
|
||||||
|
isMobile,
|
||||||
|
headerSetting,
|
||||||
|
multiTabsSetting,
|
||||||
|
menuSetting,
|
||||||
|
crumbsSetting,
|
||||||
|
// permissionMode,
|
||||||
|
showFooter,
|
||||||
|
isPageAnimate,
|
||||||
|
pageAnimateType,
|
||||||
|
};
|
||||||
|
}
|
||||||
95
src/hooks/useBattery.ts
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
import { computed, onMounted, reactive, toRefs } from 'vue';
|
||||||
|
|
||||||
|
interface Battery {
|
||||||
|
charging: boolean; // 当前电池是否正在充电
|
||||||
|
chargingTime: number; // 距离充电完毕还需多少秒,如果为0则充电完毕
|
||||||
|
dischargingTime: number; // 代表距离电池耗电至空且挂起需要多少秒
|
||||||
|
level: number; // 代表电量的放大等级,这个值在 0.0 至 1.0 之间
|
||||||
|
[key: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useBattery = () => {
|
||||||
|
const state = reactive({
|
||||||
|
battery: {
|
||||||
|
charging: false,
|
||||||
|
chargingTime: 0,
|
||||||
|
dischargingTime: 0,
|
||||||
|
level: 100,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// 更新电池使用状态
|
||||||
|
const updateBattery = (target) => {
|
||||||
|
for (const key in state.battery) {
|
||||||
|
state.battery[key] = target[key];
|
||||||
|
}
|
||||||
|
state.battery.level = state.battery.level * 100;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 计算电池剩余可用时间
|
||||||
|
const calcDischargingTime = computed(() => {
|
||||||
|
const hour = state.battery.dischargingTime / 3600;
|
||||||
|
const minute = (state.battery.dischargingTime / 60) % 60;
|
||||||
|
return `${~~hour}小时${~~minute}分钟`;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 计算电池充满剩余时间
|
||||||
|
const calcChargingTime = computed(() => {
|
||||||
|
console.log(state.battery);
|
||||||
|
const hour = state.battery.chargingTime / 3600;
|
||||||
|
const minute = (state.battery.chargingTime / 60) % 60;
|
||||||
|
return `${~~hour}小时${~~minute}分钟`;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 电池状态
|
||||||
|
const batteryStatus = computed(() => {
|
||||||
|
if (state.battery.charging && state.battery.level >= 100) {
|
||||||
|
return '已充满';
|
||||||
|
} else if (state.battery.charging) {
|
||||||
|
return '充电中';
|
||||||
|
} else {
|
||||||
|
return '已断开电源';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
const BatteryManager: Battery = await (
|
||||||
|
window.navigator as any
|
||||||
|
).getBattery();
|
||||||
|
updateBattery(BatteryManager);
|
||||||
|
|
||||||
|
// 电池充电状态更新时被调用
|
||||||
|
BatteryManager.onchargingchange = ({ target }) => {
|
||||||
|
updateBattery(target);
|
||||||
|
};
|
||||||
|
// 电池充电时间更新时被调用
|
||||||
|
BatteryManager.onchargingtimechange = ({ target }) => {
|
||||||
|
updateBattery(target);
|
||||||
|
};
|
||||||
|
// 电池断开充电时间更新时被调用
|
||||||
|
BatteryManager.ondischargingtimechange = ({ target }) => {
|
||||||
|
updateBattery(target);
|
||||||
|
};
|
||||||
|
// 电池电量更新时被调用
|
||||||
|
BatteryManager.onlevelchange = ({ target }) => {
|
||||||
|
updateBattery(target);
|
||||||
|
};
|
||||||
|
|
||||||
|
// new Intl.DateTimeFormat('zh', {
|
||||||
|
// year: 'numeric',
|
||||||
|
// month: '2-digit',
|
||||||
|
// day: '2-digit',
|
||||||
|
// hour: '2-digit',
|
||||||
|
// minute: '2-digit',
|
||||||
|
// second: '2-digit',
|
||||||
|
// hour12: false
|
||||||
|
// }).format(new Date())
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
...toRefs(state),
|
||||||
|
batteryStatus,
|
||||||
|
calcDischargingTime,
|
||||||
|
calcChargingTime,
|
||||||
|
};
|
||||||
|
};
|
||||||
30
src/hooks/useOnline.ts
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import { ref, onMounted, onUnmounted } from 'vue';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 用户网络是否可用
|
||||||
|
* */
|
||||||
|
export function useOnline() {
|
||||||
|
const online = ref(true);
|
||||||
|
|
||||||
|
const showStatus = (val) => {
|
||||||
|
online.value = typeof val == 'boolean' ? val : val.target.online;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 在页面加载后,设置正确的网络状态
|
||||||
|
navigator.onLine ? showStatus(true) : showStatus(false);
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
// 开始监听网络状态的变化
|
||||||
|
window.addEventListener('online', showStatus);
|
||||||
|
|
||||||
|
window.addEventListener('offline', showStatus);
|
||||||
|
});
|
||||||
|
onUnmounted(() => {
|
||||||
|
// 移除监听网络状态的变化
|
||||||
|
window.removeEventListener('online', showStatus);
|
||||||
|
|
||||||
|
window.removeEventListener('offline', showStatus);
|
||||||
|
});
|
||||||
|
|
||||||
|
return { online };
|
||||||
|
}
|
||||||
59
src/hooks/useTime.ts
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
import { ref, onMounted, onUnmounted } from 'vue';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 获取本地时间
|
||||||
|
*/
|
||||||
|
export function useTime() {
|
||||||
|
let timer; // 定时器
|
||||||
|
const year = ref(0); // 年份
|
||||||
|
const month = ref(0); // 月份
|
||||||
|
const week = ref(''); // 星期几
|
||||||
|
const day = ref(0); // 天数
|
||||||
|
const hour = ref<number | string>(0); // 小时
|
||||||
|
const minute = ref<number | string>(0); // 分钟
|
||||||
|
const second = ref(0); // 秒
|
||||||
|
|
||||||
|
// 更新时间
|
||||||
|
const updateTime = () => {
|
||||||
|
const date = new Date();
|
||||||
|
year.value = date.getFullYear();
|
||||||
|
month.value = date.getMonth() + 1;
|
||||||
|
week.value = '日一二三四五六'.charAt(date.getDay());
|
||||||
|
day.value = date.getDate();
|
||||||
|
hour.value =
|
||||||
|
(date.getHours() + '')?.padStart(2, '0') ||
|
||||||
|
new Intl.NumberFormat(undefined, { minimumIntegerDigits: 2 }).format(
|
||||||
|
date.getHours()
|
||||||
|
);
|
||||||
|
minute.value =
|
||||||
|
(date.getMinutes() + '')?.padStart(2, '0') ||
|
||||||
|
new Intl.NumberFormat(undefined, { minimumIntegerDigits: 2 }).format(
|
||||||
|
date.getMinutes()
|
||||||
|
);
|
||||||
|
second.value = date.getSeconds();
|
||||||
|
};
|
||||||
|
|
||||||
|
// 原生时间格式化
|
||||||
|
// new Intl.DateTimeFormat('zh', {
|
||||||
|
// year: 'numeric',
|
||||||
|
// month: '2-digit',
|
||||||
|
// day: '2-digit',
|
||||||
|
// hour: '2-digit',
|
||||||
|
// minute: '2-digit',
|
||||||
|
// second: '2-digit',
|
||||||
|
// hour12: false
|
||||||
|
// }).format(new Date())
|
||||||
|
|
||||||
|
updateTime();
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
clearInterval(timer);
|
||||||
|
timer = setInterval(() => updateTime(), 1000);
|
||||||
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
clearInterval(timer);
|
||||||
|
});
|
||||||
|
|
||||||
|
return { month, day, hour, minute, second, week };
|
||||||
|
}
|
||||||
70
src/hooks/web/usePage.ts
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
import type { RouteLocationRaw, Router } from 'vue-router';
|
||||||
|
|
||||||
|
import { PageEnum } from '@/enums/pageEnum';
|
||||||
|
|
||||||
|
import { RedirectName } from '@/router/constant';
|
||||||
|
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
import { isString } from '@/utils/is';
|
||||||
|
import { unref } from 'vue';
|
||||||
|
|
||||||
|
export type RouteLocationRawEx = Omit<RouteLocationRaw, 'path'> & {
|
||||||
|
path: PageEnum;
|
||||||
|
};
|
||||||
|
|
||||||
|
function handleError(e: Error) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 页面切换
|
||||||
|
*/
|
||||||
|
export function useGo(_router?: Router) {
|
||||||
|
let router;
|
||||||
|
if (!_router) {
|
||||||
|
router = useRouter();
|
||||||
|
}
|
||||||
|
const { push, replace } = _router || router;
|
||||||
|
function go(
|
||||||
|
opt: PageEnum | RouteLocationRawEx | string = PageEnum.BASE_HOME,
|
||||||
|
isReplace = false
|
||||||
|
) {
|
||||||
|
if (!opt) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (isString(opt)) {
|
||||||
|
isReplace
|
||||||
|
? replace(opt).catch(handleError)
|
||||||
|
: push(opt).catch(handleError);
|
||||||
|
} else {
|
||||||
|
const o = opt as RouteLocationRaw;
|
||||||
|
isReplace ? replace(o).catch(handleError) : push(o).catch(handleError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return go;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重做当前页面
|
||||||
|
*/
|
||||||
|
export const useRedo = (_router?: Router) => {
|
||||||
|
const { push, currentRoute } = _router || useRouter();
|
||||||
|
const { query, params = {}, name, fullPath } = unref(currentRoute.value);
|
||||||
|
function redo(): Promise<boolean> {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
if (name === RedirectName) {
|
||||||
|
resolve(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (name && Object.keys(params).length > 0) {
|
||||||
|
params['_redirect_type'] = 'name';
|
||||||
|
params['path'] = String(name);
|
||||||
|
} else {
|
||||||
|
params['_redirect_type'] = 'path';
|
||||||
|
params['path'] = fullPath;
|
||||||
|
}
|
||||||
|
push({ name: RedirectName, params, query }).then(() => resolve(true));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return redo;
|
||||||
|
};
|
||||||
52
src/hooks/web/usePermission.ts
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
// import { useUserStore } from '@/store/modules/user';
|
||||||
|
|
||||||
|
// export function usePermission() {
|
||||||
|
// const userStore = useUserStore();
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * 检查权限
|
||||||
|
// * @param accesses
|
||||||
|
// */
|
||||||
|
// function _somePermissions(accesses: string[]) {
|
||||||
|
// return userStore.getPermissions.some((item) => {
|
||||||
|
// const { value }: any = item;
|
||||||
|
// return accesses.includes(value);
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * 判断是否存在权限
|
||||||
|
// * 可用于 v-if 显示逻辑
|
||||||
|
// * */
|
||||||
|
// function hasPermission(accesses: string[]): boolean {
|
||||||
|
// if (!accesses || !accesses.length) return true;
|
||||||
|
// return _somePermissions(accesses);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * 是否包含指定的所有权限
|
||||||
|
// * @param accesses
|
||||||
|
// */
|
||||||
|
// function hasEveryPermission(accesses: string[]): boolean {
|
||||||
|
// const permissionsList = userStore.getPermissions;
|
||||||
|
// if (Array.isArray(accesses)) {
|
||||||
|
// return permissionsList.every((access: any) => accesses.includes(access.value));
|
||||||
|
// }
|
||||||
|
// throw new Error(`[hasEveryPermission]: ${accesses} should be a array !`);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * 是否包含其中某个权限
|
||||||
|
// * @param accesses
|
||||||
|
// * @param accessMap
|
||||||
|
// */
|
||||||
|
// function hasSomePermission(accesses: string[]): boolean {
|
||||||
|
// const permissionsList = userStore.getPermissions;
|
||||||
|
// if (Array.isArray(accesses)) {
|
||||||
|
// return permissionsList.some((access: any) => accesses.includes(access.value));
|
||||||
|
// }
|
||||||
|
// throw new Error(`[hasSomePermission]: ${accesses} should be a array !`);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return { hasPermission, hasEveryPermission, hasSomePermission };
|
||||||
|
// }
|
||||||
452
src/layout/components/Header/ProjectSetting.vue
Normal file
@ -0,0 +1,452 @@
|
|||||||
|
<template>
|
||||||
|
<n-drawer v-model:show="isDrawer" :width="width" :placement="placement">
|
||||||
|
<n-drawer-content :title="title" :native-scrollbar="false">
|
||||||
|
<div class="drawer">
|
||||||
|
<n-divider title-placement="center">主题</n-divider>
|
||||||
|
|
||||||
|
<div class="justify-center drawer-setting-item dark-switch">
|
||||||
|
<n-tooltip placement="bottom">
|
||||||
|
<template #trigger>
|
||||||
|
<n-switch
|
||||||
|
v-model:value="designStore.darkTheme"
|
||||||
|
class="dark-theme-switch"
|
||||||
|
>
|
||||||
|
<template #checked>
|
||||||
|
<n-icon size="14" color="#ffd93b">
|
||||||
|
<SunnySharp />
|
||||||
|
</n-icon>
|
||||||
|
</template>
|
||||||
|
<template #unchecked>
|
||||||
|
<n-icon size="14" color="#ffd93b">
|
||||||
|
<Moon />
|
||||||
|
</n-icon>
|
||||||
|
</template>
|
||||||
|
</n-switch>
|
||||||
|
</template>
|
||||||
|
<span>{{ designStore.darkTheme ? '深' : '浅' }}色主题</span>
|
||||||
|
</n-tooltip>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<n-divider title-placement="center">系统主题</n-divider>
|
||||||
|
|
||||||
|
<div class="drawer-setting-item align-items-top">
|
||||||
|
<span
|
||||||
|
class="theme-item"
|
||||||
|
v-for="(item, index) in appThemeList"
|
||||||
|
:key="index"
|
||||||
|
:style="{ 'background-color': item }"
|
||||||
|
@click="togTheme(item)"
|
||||||
|
>
|
||||||
|
<n-icon size="12" v-if="item === designStore.appTheme">
|
||||||
|
<CheckOutlined />
|
||||||
|
</n-icon>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<n-divider title-placement="center">导航栏模式</n-divider>
|
||||||
|
|
||||||
|
<div class="drawer-setting-item align-items-top">
|
||||||
|
<div class="drawer-setting-item-style align-items-top">
|
||||||
|
<n-tooltip placement="top">
|
||||||
|
<template #trigger>
|
||||||
|
<img
|
||||||
|
src="~@/assets/images/nav-theme-dark.svg"
|
||||||
|
@click="togNavMode('vertical')"
|
||||||
|
alt="左侧菜单模式"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<span>左侧菜单模式</span>
|
||||||
|
</n-tooltip>
|
||||||
|
<n-badge
|
||||||
|
dot
|
||||||
|
color="#19be6b"
|
||||||
|
v-show="settingStore.navMode === 'vertical'"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="drawer-setting-item-style">
|
||||||
|
<n-tooltip placement="top">
|
||||||
|
<template #trigger>
|
||||||
|
<img
|
||||||
|
src="~@/assets/images/nav-horizontal.svg"
|
||||||
|
alt="顶部菜单模式"
|
||||||
|
@click="togNavMode('horizontal')"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<span>顶部菜单模式</span>
|
||||||
|
</n-tooltip>
|
||||||
|
<n-badge
|
||||||
|
dot
|
||||||
|
color="#19be6b"
|
||||||
|
v-show="settingStore.navMode === 'horizontal'"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="drawer-setting-item-style">
|
||||||
|
<n-tooltip placement="top">
|
||||||
|
<template #trigger>
|
||||||
|
<img
|
||||||
|
src="~@/assets/images/nav-horizontal-mix.svg"
|
||||||
|
@click="togNavMode('horizontal-mix')"
|
||||||
|
alt="顶部菜单混合模式"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<span>顶部菜单混合模式</span>
|
||||||
|
</n-tooltip>
|
||||||
|
<n-badge
|
||||||
|
dot
|
||||||
|
color="#19be6b"
|
||||||
|
v-show="settingStore.navMode === 'horizontal-mix'"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<n-divider title-placement="center">导航栏风格</n-divider>
|
||||||
|
|
||||||
|
<div class="drawer-setting-item align-items-top">
|
||||||
|
<div class="drawer-setting-item-style align-items-top">
|
||||||
|
<n-tooltip placement="top">
|
||||||
|
<template #trigger>
|
||||||
|
<img
|
||||||
|
src="~@/assets/images/nav-theme-dark.svg"
|
||||||
|
alt="暗色侧边栏"
|
||||||
|
@click="togNavTheme('dark')"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<span>暗色侧边栏</span>
|
||||||
|
</n-tooltip>
|
||||||
|
<n-badge
|
||||||
|
dot
|
||||||
|
color="#19be6b"
|
||||||
|
v-if="settingStore.navTheme === 'dark'"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="drawer-setting-item-style">
|
||||||
|
<n-tooltip placement="top">
|
||||||
|
<template #trigger>
|
||||||
|
<img
|
||||||
|
src="~@/assets/images/nav-theme-light.svg"
|
||||||
|
alt="白色侧边栏"
|
||||||
|
@click="togNavTheme('light')"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<span>白色侧边栏</span>
|
||||||
|
</n-tooltip>
|
||||||
|
<n-badge
|
||||||
|
dot
|
||||||
|
color="#19be6b"
|
||||||
|
v-if="settingStore.navTheme === 'light'"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="drawer-setting-item-style">
|
||||||
|
<n-tooltip placement="top">
|
||||||
|
<template #trigger>
|
||||||
|
<img
|
||||||
|
src="~@/assets/images/header-theme-dark.svg"
|
||||||
|
@click="togNavTheme('header-dark')"
|
||||||
|
alt="暗色顶栏"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<span>暗色顶栏</span>
|
||||||
|
</n-tooltip>
|
||||||
|
<n-badge
|
||||||
|
dot
|
||||||
|
color="#19be6b"
|
||||||
|
v-if="settingStore.navTheme === 'header-dark'"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<n-divider title-placement="center">界面功能</n-divider>
|
||||||
|
|
||||||
|
<div class="drawer-setting-item">
|
||||||
|
<div class="drawer-setting-item-title">分割菜单</div>
|
||||||
|
<div class="drawer-setting-item-action">
|
||||||
|
<n-switch
|
||||||
|
:disabled="settingStore.navMode !== 'horizontal-mix'"
|
||||||
|
v-model:value="settingStore.menuSetting.mixMenu"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="drawer-setting-item">
|
||||||
|
<div class="drawer-setting-item-title">固定顶栏</div>
|
||||||
|
<div class="drawer-setting-item-action">
|
||||||
|
<n-switch v-model:value="settingStore.headerSetting.fixed" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- <div class="drawer-setting-item">-->
|
||||||
|
<!-- <div class="drawer-setting-item-title">-->
|
||||||
|
<!-- 固定侧边栏-->
|
||||||
|
<!-- </div>-->
|
||||||
|
<!-- <div class="drawer-setting-item-action">-->
|
||||||
|
<!-- <n-switch v-model:value="settingStore.menuSetting.fixed" />-->
|
||||||
|
<!-- </div>-->
|
||||||
|
<!-- </div>-->
|
||||||
|
|
||||||
|
<div class="drawer-setting-item">
|
||||||
|
<div class="drawer-setting-item-title">固定多页签</div>
|
||||||
|
<div class="drawer-setting-item-action">
|
||||||
|
<n-switch v-model:value="settingStore.multiTabsSetting.fixed" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<n-divider title-placement="center">界面显示</n-divider>
|
||||||
|
|
||||||
|
<div class="drawer-setting-item">
|
||||||
|
<div class="drawer-setting-item-title">显示重载页面按钮</div>
|
||||||
|
<div class="drawer-setting-item-action">
|
||||||
|
<n-switch v-model:value="settingStore.headerSetting.isReload" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="drawer-setting-item">
|
||||||
|
<div class="drawer-setting-item-title">显示面包屑导航</div>
|
||||||
|
<div class="drawer-setting-item-action">
|
||||||
|
<n-switch v-model:value="settingStore.crumbsSetting.show" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="drawer-setting-item">
|
||||||
|
<div class="drawer-setting-item-title">显示面包屑显示图标</div>
|
||||||
|
<div class="drawer-setting-item-action">
|
||||||
|
<n-switch v-model:value="settingStore.crumbsSetting.showIcon" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="drawer-setting-item">
|
||||||
|
<div class="drawer-setting-item-title">显示多页签</div>
|
||||||
|
<div class="drawer-setting-item-action">
|
||||||
|
<n-switch v-model:value="settingStore.multiTabsSetting.show" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!--1.15废弃,没啥用,占用操作空间-->
|
||||||
|
<!-- <div class="drawer-setting-item">-->
|
||||||
|
<!-- <div class="drawer-setting-item-title"> 显示页脚 </div>-->
|
||||||
|
<!-- <div class="drawer-setting-item-action">-->
|
||||||
|
<!-- <n-switch v-model:value="settingStore.showFooter" />-->
|
||||||
|
<!-- </div>-->
|
||||||
|
<!-- </div>-->
|
||||||
|
|
||||||
|
<n-divider title-placement="center">动画</n-divider>
|
||||||
|
|
||||||
|
<div class="drawer-setting-item">
|
||||||
|
<div class="drawer-setting-item-title">禁用动画</div>
|
||||||
|
<div class="drawer-setting-item-action">
|
||||||
|
<n-switch v-model:value="settingStore.isPageAnimate" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="drawer-setting-item">
|
||||||
|
<div class="drawer-setting-item-title">动画类型</div>
|
||||||
|
<div class="drawer-setting-item-select">
|
||||||
|
<n-select
|
||||||
|
v-model:value="settingStore.pageAnimateType"
|
||||||
|
:options="animateOptions"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- <div class="drawer-setting-item">
|
||||||
|
<n-alert type="warning" :showIcon="false">
|
||||||
|
<p>{{ alertText }}</p>
|
||||||
|
</n-alert>
|
||||||
|
</div> -->
|
||||||
|
</div>
|
||||||
|
<template #footer>
|
||||||
|
<n-space>
|
||||||
|
<n-button type="primary" @click="submitSetting">提交</n-button>
|
||||||
|
<n-button>重置</n-button>
|
||||||
|
</n-space>
|
||||||
|
</template>
|
||||||
|
</n-drawer-content>
|
||||||
|
</n-drawer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { useProjectSettingStore } from '@/store/modules/projectSetting';
|
||||||
|
import { useDesignSettingStore } from '@/store/modules/designSetting';
|
||||||
|
import { CheckOutlined } from '@vicons/antd';
|
||||||
|
import { Moon, SunnySharp } from '@vicons/ionicons5';
|
||||||
|
import { darkTheme } from 'naive-ui';
|
||||||
|
import { animates as animateOptions } from '@/settings/animateSetting';
|
||||||
|
import { setSystemSetting } from '@/api/system/user';
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'ProjectSetting',
|
||||||
|
components: { CheckOutlined, Moon, SunnySharp },
|
||||||
|
props: {
|
||||||
|
title: {
|
||||||
|
type: String,
|
||||||
|
default: '项目配置',
|
||||||
|
},
|
||||||
|
width: {
|
||||||
|
type: Number,
|
||||||
|
default: 280,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
setup(props) {
|
||||||
|
const settingStore = useProjectSettingStore();
|
||||||
|
const designStore = useDesignSettingStore();
|
||||||
|
const state = reactive({
|
||||||
|
width: props.width,
|
||||||
|
title: props.title,
|
||||||
|
isDrawer: false,
|
||||||
|
placement: 'right',
|
||||||
|
alertText: '',
|
||||||
|
appThemeList: designStore.appThemeList,
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => designStore.darkTheme,
|
||||||
|
(to) => {
|
||||||
|
settingStore.navTheme = to ? 'header-dark' : 'dark';
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const directionsOptions = computed(() => {
|
||||||
|
return animateOptions.find(
|
||||||
|
(item) => item.value == unref(settingStore.pageAnimateType)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
function openDrawer() {
|
||||||
|
state.isDrawer = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeDrawer() {
|
||||||
|
state.isDrawer = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function togNavTheme(theme) {
|
||||||
|
settingStore.navTheme = theme;
|
||||||
|
if (settingStore.navMode === 'horizontal' && ['light'].includes(theme)) {
|
||||||
|
settingStore.navTheme = 'dark';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function togTheme(color) {
|
||||||
|
designStore.appTheme = color;
|
||||||
|
}
|
||||||
|
|
||||||
|
function togNavMode(mode) {
|
||||||
|
settingStore.navMode = mode;
|
||||||
|
settingStore.menuSetting.mixMenu = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function submitSetting() {
|
||||||
|
const {
|
||||||
|
menuSetting,
|
||||||
|
navMode,
|
||||||
|
navTheme,
|
||||||
|
isMobile,
|
||||||
|
headerSetting,
|
||||||
|
showFooter,
|
||||||
|
multiTabsSetting,
|
||||||
|
crumbsSetting,
|
||||||
|
isPageAnimate,
|
||||||
|
pageAnimateType,
|
||||||
|
} = settingStore;
|
||||||
|
const newSettingObject = {
|
||||||
|
menuSetting,
|
||||||
|
navMode,
|
||||||
|
navTheme,
|
||||||
|
isMobile,
|
||||||
|
headerSetting,
|
||||||
|
showFooter,
|
||||||
|
multiTabsSetting,
|
||||||
|
crumbsSetting,
|
||||||
|
isPageAnimate,
|
||||||
|
pageAnimateType,
|
||||||
|
appTheme: designStore.appTheme,
|
||||||
|
};
|
||||||
|
const req = JSON.stringify(newSettingObject, null, 2);
|
||||||
|
const data = await setSystemSetting(req);
|
||||||
|
if (data.status === 200) window['$message'].success('设置成功!');
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...toRefs(state),
|
||||||
|
settingStore,
|
||||||
|
designStore,
|
||||||
|
togNavTheme,
|
||||||
|
togNavMode,
|
||||||
|
togTheme,
|
||||||
|
darkTheme,
|
||||||
|
openDrawer,
|
||||||
|
closeDrawer,
|
||||||
|
animateOptions,
|
||||||
|
directionsOptions,
|
||||||
|
submitSetting,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.drawer {
|
||||||
|
.n-divider:not(.n-divider--vertical) {
|
||||||
|
margin: 10px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-setting-item {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: center;
|
||||||
|
padding: 12px 0;
|
||||||
|
|
||||||
|
&-style {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
margin-right: 16px;
|
||||||
|
text-align: center;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-title {
|
||||||
|
flex: 1 1;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-action {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-select {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-item {
|
||||||
|
width: 20px;
|
||||||
|
min-width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
margin: 0 5px 5px 0;
|
||||||
|
line-height: 14px;
|
||||||
|
text-align: center;
|
||||||
|
cursor: pointer;
|
||||||
|
border: 1px solid #eee;
|
||||||
|
border-radius: 2px;
|
||||||
|
|
||||||
|
.n-icon {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.align-items-top {
|
||||||
|
align-items: flex-start;
|
||||||
|
padding: 2px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.justify-center {
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark-switch .n-switch {
|
||||||
|
::v-deep(.n-switch__rail) {
|
||||||
|
background-color: #000e1c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
31
src/layout/components/Header/components.ts
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import {
|
||||||
|
SettingOutlined,
|
||||||
|
SearchOutlined,
|
||||||
|
MenuFoldOutlined,
|
||||||
|
MenuUnfoldOutlined,
|
||||||
|
FullscreenOutlined,
|
||||||
|
FullscreenExitOutlined,
|
||||||
|
PoweroffOutlined,
|
||||||
|
GithubOutlined,
|
||||||
|
LockOutlined,
|
||||||
|
ReloadOutlined,
|
||||||
|
LogoutOutlined,
|
||||||
|
UserOutlined,
|
||||||
|
CheckOutlined,
|
||||||
|
} from '@vicons/antd';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
SettingOutlined,
|
||||||
|
LockOutlined,
|
||||||
|
GithubOutlined,
|
||||||
|
SearchOutlined,
|
||||||
|
MenuFoldOutlined,
|
||||||
|
MenuUnfoldOutlined,
|
||||||
|
FullscreenOutlined,
|
||||||
|
FullscreenExitOutlined,
|
||||||
|
PoweroffOutlined,
|
||||||
|
ReloadOutlined,
|
||||||
|
LogoutOutlined,
|
||||||
|
UserOutlined,
|
||||||
|
CheckOutlined,
|
||||||
|
};
|
||||||
@ -1,3 +1,3 @@
|
|||||||
import Header from "./index.vue";
|
import PageHeader from './index.vue';
|
||||||
|
|
||||||
export { Header };
|
export { PageHeader };
|
||||||
|
|||||||
@ -1,32 +1,105 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import { UserOutlined } from "@vicons/antd";
|
|
||||||
import { useUserStore } from "@/store/modules/user";
|
|
||||||
const userStore = useUserStore();
|
|
||||||
//头像下拉菜单
|
|
||||||
const avatarSelect = (key) => {
|
|
||||||
switch (key) {
|
|
||||||
case 1:
|
|
||||||
doLogout();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const avatarOptions = [
|
|
||||||
{
|
|
||||||
label: "退出登录",
|
|
||||||
key: 1,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
// 退出登录
|
|
||||||
const doLogout = () => {
|
|
||||||
userStore.logout();
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="layout-header">
|
<div class="layout-header">
|
||||||
|
<!--顶部菜单-->
|
||||||
|
<div
|
||||||
|
class="layout-header-left"
|
||||||
|
v-if="
|
||||||
|
navMode === 'horizontal' || (navMode === 'horizontal-mix' && mixMenu)
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<div class="logo" v-if="navMode === 'horizontal'">
|
||||||
|
<img :src="websiteConfig.logo" alt="" />
|
||||||
|
<h2 v-show="!collapsed" class="title">{{ websiteConfig.title }}</h2>
|
||||||
|
</div>
|
||||||
|
<AsideMenu
|
||||||
|
v-model:collapsed="collapse"
|
||||||
|
v-model:location="getMenuLocation"
|
||||||
|
:inverted="getInverted"
|
||||||
|
mode="horizontal"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<!--左侧菜单-->
|
||||||
|
<div class="layout-header-left" v-else>
|
||||||
|
<!-- 菜单收起 -->
|
||||||
|
<div
|
||||||
|
class="ml-1 layout-header-trigger layout-header-trigger-min"
|
||||||
|
@click="() => $emit('update:collapsed', !collapsed)"
|
||||||
|
>
|
||||||
|
<n-icon size="18" v-if="collapsed">
|
||||||
|
<MenuUnfoldOutlined />
|
||||||
|
</n-icon>
|
||||||
|
<n-icon size="18" v-else>
|
||||||
|
<MenuFoldOutlined />
|
||||||
|
</n-icon>
|
||||||
|
</div>
|
||||||
|
<!-- 刷新 -->
|
||||||
|
<div
|
||||||
|
class="mr-1 layout-header-trigger layout-header-trigger-min"
|
||||||
|
v-if="headerSetting.isReload"
|
||||||
|
@click="reloadPage"
|
||||||
|
>
|
||||||
|
<n-icon size="18">
|
||||||
|
<ReloadOutlined />
|
||||||
|
</n-icon>
|
||||||
|
</div>
|
||||||
|
<!-- 面包屑 -->
|
||||||
|
<n-breadcrumb v-if="crumbsSetting.show">
|
||||||
|
<template
|
||||||
|
v-for="routeItem in breadcrumbList"
|
||||||
|
:key="routeItem.name === 'Redirect' ? void 0 : routeItem.name"
|
||||||
|
>
|
||||||
|
<n-breadcrumb-item v-if="routeItem.meta.title">
|
||||||
|
<n-dropdown
|
||||||
|
v-if="routeItem.children.length"
|
||||||
|
:options="routeItem.children"
|
||||||
|
@select="dropdownSelect"
|
||||||
|
>
|
||||||
|
<span class="link-text">
|
||||||
|
<component
|
||||||
|
v-if="crumbsSetting.showIcon && routeItem.meta.icon"
|
||||||
|
:is="routeItem.meta.icon"
|
||||||
|
/>
|
||||||
|
{{ routeItem.meta.title }}
|
||||||
|
</span>
|
||||||
|
</n-dropdown>
|
||||||
|
<span class="link-text" v-else>
|
||||||
|
<component
|
||||||
|
v-if="crumbsSetting.showIcon && routeItem.meta.icon"
|
||||||
|
:is="routeItem.meta.icon"
|
||||||
|
/>
|
||||||
|
{{ routeItem.meta.title }}
|
||||||
|
</span>
|
||||||
|
</n-breadcrumb-item>
|
||||||
|
</template>
|
||||||
|
</n-breadcrumb>
|
||||||
|
</div>
|
||||||
<div class="layout-header-right">
|
<div class="layout-header-right">
|
||||||
|
<div
|
||||||
|
class="layout-header-trigger layout-header-trigger-min"
|
||||||
|
v-for="item in iconList"
|
||||||
|
:key="item.icon"
|
||||||
|
>
|
||||||
|
<n-tooltip placement="bottom">
|
||||||
|
<template #trigger>
|
||||||
|
<n-icon size="18">
|
||||||
|
<component :is="item.icon" v-on="item.eventObject || {}" />
|
||||||
|
</n-icon>
|
||||||
|
</template>
|
||||||
|
<span>{{ item.tips }}</span>
|
||||||
|
</n-tooltip>
|
||||||
|
</div>
|
||||||
|
<!--切换全屏-->
|
||||||
|
<div class="layout-header-trigger layout-header-trigger-min">
|
||||||
|
<n-tooltip placement="bottom">
|
||||||
|
<template #trigger>
|
||||||
|
<n-icon size="18">
|
||||||
|
<component :is="fullscreenIcon" @click="toggleFullScreen" />
|
||||||
|
</n-icon>
|
||||||
|
</template>
|
||||||
|
<span>全屏</span>
|
||||||
|
</n-tooltip>
|
||||||
|
</div>
|
||||||
|
<!-- 个人中心 -->
|
||||||
<div class="layout-header-trigger layout-header-trigger-min">
|
<div class="layout-header-trigger layout-header-trigger-min">
|
||||||
<n-dropdown
|
<n-dropdown
|
||||||
trigger="hover"
|
trigger="hover"
|
||||||
@ -35,7 +108,7 @@ const doLogout = () => {
|
|||||||
>
|
>
|
||||||
<div class="avatar">
|
<div class="avatar">
|
||||||
<n-avatar round>
|
<n-avatar round>
|
||||||
admin
|
{{ username }}
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<UserOutlined />
|
<UserOutlined />
|
||||||
</template>
|
</template>
|
||||||
@ -43,22 +116,361 @@ const doLogout = () => {
|
|||||||
</div>
|
</div>
|
||||||
</n-dropdown>
|
</n-dropdown>
|
||||||
</div>
|
</div>
|
||||||
|
<!--设置-->
|
||||||
|
<div
|
||||||
|
class="layout-header-trigger layout-header-trigger-min"
|
||||||
|
@click="openSetting"
|
||||||
|
>
|
||||||
|
<n-tooltip placement="bottom-end">
|
||||||
|
<template #trigger>
|
||||||
|
<n-icon size="18" style="font-weight: bold">
|
||||||
|
<SettingOutlined />
|
||||||
|
</n-icon>
|
||||||
|
</template>
|
||||||
|
<span>项目配置</span>
|
||||||
|
</n-tooltip>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<!--项目配置-->
|
||||||
|
<ProjectSetting ref="drawerSetting" />
|
||||||
</template>
|
</template>
|
||||||
<style scoped lang="scss">
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent, reactive, toRefs, ref, computed, unref } from 'vue';
|
||||||
|
import { useRouter, useRoute } from 'vue-router';
|
||||||
|
import components from './components';
|
||||||
|
import { StorageEnum } from '@/enums/storageEnum';
|
||||||
|
import { useUserStore } from '@/store/modules/user';
|
||||||
|
import { useScreenLockStore } from '@/store/modules/screenLock';
|
||||||
|
import ProjectSetting from './ProjectSetting.vue';
|
||||||
|
import { AsideMenu } from '@/layout/components/Sider';
|
||||||
|
import { useProjectSetting } from '@/hooks/setting/useProjectSetting';
|
||||||
|
import { websiteConfig } from '@/config/website.config';
|
||||||
|
import { zsDialog } from '@/utils';
|
||||||
|
import { DialogEnum } from '@/enums/pluginEnum';
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'PageHeader',
|
||||||
|
components: { ...components, ProjectSetting, AsideMenu },
|
||||||
|
props: {
|
||||||
|
collapsed: {
|
||||||
|
type: Boolean,
|
||||||
|
},
|
||||||
|
inverted: {
|
||||||
|
type: Boolean,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
setup(props) {
|
||||||
|
const userStore = useUserStore();
|
||||||
|
const useLockscreen = useScreenLockStore();
|
||||||
|
const { navMode, navTheme, headerSetting, menuSetting, crumbsSetting } =
|
||||||
|
useProjectSetting();
|
||||||
|
|
||||||
|
const { name } = userStore?.info || {};
|
||||||
|
|
||||||
|
const drawerSetting = ref();
|
||||||
|
|
||||||
|
const state = reactive({
|
||||||
|
username: name ?? '',
|
||||||
|
fullscreenIcon: 'FullscreenOutlined',
|
||||||
|
navMode,
|
||||||
|
navTheme,
|
||||||
|
headerSetting,
|
||||||
|
crumbsSetting,
|
||||||
|
});
|
||||||
|
|
||||||
|
const getInverted = computed(() => {
|
||||||
|
return ['light', 'header-dark'].includes(unref(navTheme))
|
||||||
|
? props.inverted
|
||||||
|
: !props.inverted;
|
||||||
|
});
|
||||||
|
|
||||||
|
const mixMenu = computed(() => {
|
||||||
|
return unref(menuSetting).mixMenu;
|
||||||
|
});
|
||||||
|
|
||||||
|
const getChangeStyle = computed(() => {
|
||||||
|
const { collapsed } = props;
|
||||||
|
const { minMenuWidth, menuWidth } = unref(menuSetting);
|
||||||
|
return {
|
||||||
|
left: collapsed ? `${minMenuWidth}px` : `${menuWidth}px`,
|
||||||
|
width: `calc(100% - ${
|
||||||
|
collapsed ? `${minMenuWidth}px` : `${menuWidth}px`
|
||||||
|
})`,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const getMenuLocation = computed(() => {
|
||||||
|
return 'header';
|
||||||
|
});
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
const route = useRoute();
|
||||||
|
|
||||||
|
const generator: any = (routerMap) => {
|
||||||
|
return routerMap.map((item) => {
|
||||||
|
const currentMenu = {
|
||||||
|
...item,
|
||||||
|
label: item.meta.title,
|
||||||
|
key: item.name,
|
||||||
|
disabled: item.path === '/',
|
||||||
|
};
|
||||||
|
// 是否有子菜单,并递归处理
|
||||||
|
if (item.children && item.children.length > 0) {
|
||||||
|
// Recursion
|
||||||
|
currentMenu.children = generator(item.children, currentMenu);
|
||||||
|
}
|
||||||
|
return currentMenu;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const breadcrumbList = computed(() => {
|
||||||
|
return generator(route.matched);
|
||||||
|
});
|
||||||
|
|
||||||
|
const dropdownSelect = (key) => {
|
||||||
|
router.push({ name: key });
|
||||||
|
};
|
||||||
|
|
||||||
|
// 刷新页面
|
||||||
|
const reloadPage = () => {
|
||||||
|
router.push({
|
||||||
|
path: '/redirect' + unref(route).fullPath,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 退出登录
|
||||||
|
const doLogout = () => {
|
||||||
|
zsDialog({
|
||||||
|
message: '您确定要退出登录吗',
|
||||||
|
type: DialogEnum.WARNING,
|
||||||
|
positiveButtonProps: { type: 'primary', ghost: false },
|
||||||
|
onPositiveCallback: () => {
|
||||||
|
userStore.logout().then(() => {
|
||||||
|
window['$message'].success('成功退出登录');
|
||||||
|
// 移除标签页
|
||||||
|
localStorage.removeItem(StorageEnum.ZS_TABS_ROUTES);
|
||||||
|
router
|
||||||
|
.replace({
|
||||||
|
name: 'Login',
|
||||||
|
query: {
|
||||||
|
redirect: route.fullPath,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.finally(() => location.reload());
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onNegativeClick: () => {},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 切换全屏图标
|
||||||
|
const toggleFullscreenIcon = () =>
|
||||||
|
(state.fullscreenIcon =
|
||||||
|
document.fullscreenElement !== null
|
||||||
|
? 'FullscreenExitOutlined'
|
||||||
|
: 'FullscreenOutlined');
|
||||||
|
|
||||||
|
// 监听全屏切换事件
|
||||||
|
document.addEventListener('fullscreenchange', toggleFullscreenIcon);
|
||||||
|
|
||||||
|
// 全屏切换
|
||||||
|
const toggleFullScreen = () => {
|
||||||
|
if (!document.fullscreenElement) {
|
||||||
|
document.documentElement.requestFullscreen();
|
||||||
|
} else {
|
||||||
|
if (document.exitFullscreen) {
|
||||||
|
document.exitFullscreen();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 图标列表
|
||||||
|
const iconList = [
|
||||||
|
{
|
||||||
|
icon: 'LockOutlined',
|
||||||
|
tips: '锁屏',
|
||||||
|
eventObject: {
|
||||||
|
click: () => useLockscreen.setLock(true),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
const avatarOptions = [
|
||||||
|
{
|
||||||
|
label: '个人设置',
|
||||||
|
key: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '退出登录',
|
||||||
|
key: 2,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
//头像下拉菜单
|
||||||
|
const avatarSelect = (key) => {
|
||||||
|
switch (key) {
|
||||||
|
case 1:
|
||||||
|
router.push({ name: 'Setting' });
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
doLogout();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function openSetting() {
|
||||||
|
const { openDrawer } = drawerSetting.value;
|
||||||
|
openDrawer();
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...toRefs(state),
|
||||||
|
iconList,
|
||||||
|
toggleFullScreen,
|
||||||
|
doLogout,
|
||||||
|
route,
|
||||||
|
dropdownSelect,
|
||||||
|
avatarOptions,
|
||||||
|
getChangeStyle,
|
||||||
|
avatarSelect,
|
||||||
|
breadcrumbList,
|
||||||
|
reloadPage,
|
||||||
|
drawerSetting,
|
||||||
|
openSetting,
|
||||||
|
getInverted,
|
||||||
|
getMenuLocation,
|
||||||
|
mixMenu,
|
||||||
|
websiteConfig,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
.layout-header {
|
.layout-header {
|
||||||
|
z-index: 11;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 50px;
|
height: 64px;
|
||||||
|
padding: 0;
|
||||||
box-shadow: 0 1px 4px rgb(0 21 41 / 8%);
|
box-shadow: 0 1px 4px rgb(0 21 41 / 8%);
|
||||||
transition: all 0.2s ease-in-out;
|
transition: all 0.2s ease-in-out;
|
||||||
|
|
||||||
|
&-left {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 64px;
|
||||||
|
padding-left: 10px;
|
||||||
|
overflow: hidden;
|
||||||
|
line-height: 64px;
|
||||||
|
white-space: nowrap;
|
||||||
|
|
||||||
|
img {
|
||||||
|
width: auto;
|
||||||
|
height: 32px;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
::v-deep(.ant-breadcrumb span:last-child .link-text) {
|
||||||
|
color: #515a6e;
|
||||||
|
}
|
||||||
|
|
||||||
|
.n-breadcrumb {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-menu {
|
||||||
|
color: var(--text-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&-right {
|
&-right {
|
||||||
position: absolute;
|
display: flex;
|
||||||
right: 20px;
|
align-items: center;
|
||||||
|
margin-right: 20px;
|
||||||
|
|
||||||
|
.avatar {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
height: 64px;
|
||||||
|
}
|
||||||
|
|
||||||
|
> * {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-trigger {
|
||||||
|
display: inline-block;
|
||||||
|
width: 64px;
|
||||||
|
height: 64px;
|
||||||
|
text-align: center;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease-in-out;
|
||||||
|
|
||||||
|
.n-icon {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
height: 64px;
|
||||||
|
line-height: 64px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: hsl(0deg 0% 100% / 8%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.anticon {
|
||||||
|
font-size: 16px;
|
||||||
|
color: #515a6e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-trigger-min {
|
||||||
|
width: auto;
|
||||||
|
padding: 0 12px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.layout-header-light {
|
||||||
|
color: #515a6e;
|
||||||
|
background: #fff;
|
||||||
|
|
||||||
|
.n-icon {
|
||||||
|
color: #515a6e;
|
||||||
|
}
|
||||||
|
|
||||||
|
.layout-header-left {
|
||||||
|
::v-deep(
|
||||||
|
.n-breadcrumb .n-breadcrumb-item:last-child .n-breadcrumb-item__link
|
||||||
|
) {
|
||||||
|
color: #515a6e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.layout-header-trigger {
|
||||||
|
&:hover {
|
||||||
|
background: #f8f8f9;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.layout-header-fix {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
left: 200px;
|
||||||
|
z-index: 11;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
3
src/layout/components/Logo/index.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import Logo from './index.vue';
|
||||||
|
|
||||||
|
export { Logo };
|
||||||
59
src/layout/components/Logo/index.vue
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
<template>
|
||||||
|
<div class="logo">
|
||||||
|
<!-- <img :src="websiteConfig.logo" alt="" :class="{ 'mr-2': !collapsed }" /> -->
|
||||||
|
|
||||||
|
<SvgIcon
|
||||||
|
name="logo"
|
||||||
|
:class="{ 'mr-2': !props.collapsed }"
|
||||||
|
class="logo-svgs"
|
||||||
|
:color="designStore.appTheme"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<h2
|
||||||
|
v-show="!props.collapsed"
|
||||||
|
class="title"
|
||||||
|
:style="{ color: designStore.appTheme }"
|
||||||
|
>
|
||||||
|
{{ websiteConfig.title }}
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { useDesignSettingStore } from '@/store/modules/designSetting';
|
||||||
|
import { websiteConfig } from '../../../config/website.config';
|
||||||
|
|
||||||
|
const designStore = useDesignSettingStore();
|
||||||
|
const props = defineProps({
|
||||||
|
collapsed: {
|
||||||
|
type: Boolean,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.logo {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 64px;
|
||||||
|
overflow: hidden;
|
||||||
|
line-height: 64px;
|
||||||
|
white-space: nowrap;
|
||||||
|
|
||||||
|
&-svgs {
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
// color: var(--n-item-text-color-child-active) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
width: auto;
|
||||||
|
height: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -1,3 +1,3 @@
|
|||||||
import Main from "./index.vue";
|
import MainView from './index.vue';
|
||||||
|
|
||||||
export { Main };
|
export { MainView };
|
||||||
|
|||||||
@ -1,7 +1,53 @@
|
|||||||
<template>
|
<template>
|
||||||
<router-view>
|
<RouterView>
|
||||||
<template #default="{ Component, route }">
|
<template #default="{ Component, route }">
|
||||||
<component :is="Component" :key="route.fullPath" />
|
<transition :name="getTransitionName" mode="out-in" appear>
|
||||||
|
<keep-alive
|
||||||
|
v-if="keepAliveComponents.length"
|
||||||
|
:include="keepAliveComponents"
|
||||||
|
>
|
||||||
|
<component :is="Component" :key="route.fullPath" />
|
||||||
|
</keep-alive>
|
||||||
|
<component v-else :is="Component" :key="route.fullPath" />
|
||||||
|
</transition>
|
||||||
</template>
|
</template>
|
||||||
</router-view>
|
</RouterView>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { defineComponent, computed, unref } from 'vue';
|
||||||
|
import { useAsyncRouteStore } from '@/store/modules/asyncRoute';
|
||||||
|
import { useProjectSetting } from '@/hooks/setting/useProjectSetting';
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'MainView',
|
||||||
|
components: {},
|
||||||
|
props: {
|
||||||
|
notNeedKey: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
animate: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
setup() {
|
||||||
|
const { isPageAnimate, pageAnimateType } = useProjectSetting();
|
||||||
|
const asyncRouteStore = useAsyncRouteStore();
|
||||||
|
// 需要缓存的路由组件
|
||||||
|
const keepAliveComponents = computed(
|
||||||
|
() => asyncRouteStore.keepAliveComponents
|
||||||
|
);
|
||||||
|
|
||||||
|
const getTransitionName = computed(() => {
|
||||||
|
return unref(isPageAnimate) ? unref(pageAnimateType) : '';
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
keepAliveComponents,
|
||||||
|
getTransitionName,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|||||||
3
src/layout/components/Sider/index.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import AsideMenu from './index.vue';
|
||||||
|
|
||||||
|
export { AsideMenu };
|
||||||
@ -1,126 +1,186 @@
|
|||||||
<template>
|
<template>
|
||||||
<n-layout-sider
|
<NMenu
|
||||||
bordered
|
:options="menus"
|
||||||
collapse-mode="width"
|
:inverted="inverted"
|
||||||
show-trigger="bar"
|
:mode="mode"
|
||||||
:native-scrollbar="false"
|
:collapsed="collapsed"
|
||||||
:collapsed-width="60"
|
:collapsed-width="64"
|
||||||
:width="200"
|
:collapsed-icon-size="20"
|
||||||
@collapse="true"
|
:indent="24"
|
||||||
@expand="false"
|
:expanded-keys="openKeys"
|
||||||
>
|
:value="getSelectedKeys"
|
||||||
<div class="sider-flex">
|
@update:value="clickMenuItem"
|
||||||
<aside>
|
@update:expanded-keys="menuExpanded"
|
||||||
<n-space vertical class="sider-top"> 中盛起元基础框架 </n-space>
|
/>
|
||||||
<n-menu
|
|
||||||
:options="menus"
|
|
||||||
:collapsed-width="64"
|
|
||||||
:collapsed-icon-size="20"
|
|
||||||
:indent="24"
|
|
||||||
:expanded-keys="state.openKeys"
|
|
||||||
:value="getSelectedKeys"
|
|
||||||
@update:value="clickMenuItem"
|
|
||||||
@update:expanded-keys="menuExpanded"
|
|
||||||
/>
|
|
||||||
</aside>
|
|
||||||
</div>
|
|
||||||
</n-layout-sider>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script lang="ts">
|
||||||
import { useRoute } from "vue-router";
|
import { useRoute, useRouter } from 'vue-router';
|
||||||
import { routerTurnByName, generatorMenu } from "@/utils";
|
import { useAsyncRouteStore } from '@/store/modules/asyncRoute';
|
||||||
import { useAsyncRouteStore } from "@/store/modules/asyncRoute";
|
import { generatorMenu, generatorMenuMix } from '@/utils';
|
||||||
|
import { useProjectSettingStore } from '@/store/modules/projectSetting';
|
||||||
|
import { useProjectSetting } from '@/hooks/setting/useProjectSetting';
|
||||||
|
|
||||||
// 当前路由
|
export default defineComponent({
|
||||||
const currentRoute = useRoute();
|
name: 'AppMenu',
|
||||||
// 获取当前打开的子菜单
|
components: {},
|
||||||
const matched = currentRoute.matched;
|
props: {
|
||||||
const getOpenKeys =
|
mode: {
|
||||||
matched && matched.length ? matched.map((item) => item.name) : [];
|
// 菜单模式
|
||||||
|
type: String,
|
||||||
|
default: 'vertical',
|
||||||
|
},
|
||||||
|
collapsed: {
|
||||||
|
// 侧边栏菜单是否收起
|
||||||
|
type: Boolean,
|
||||||
|
},
|
||||||
|
//位置
|
||||||
|
location: {
|
||||||
|
type: String,
|
||||||
|
default: 'left',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
emits: ['update:collapsed', 'clickMenuItem'],
|
||||||
|
setup(props, { emit }) {
|
||||||
|
// 当前路由
|
||||||
|
const currentRoute = useRoute();
|
||||||
|
const router = useRouter();
|
||||||
|
const asyncRouteStore = useAsyncRouteStore();
|
||||||
|
const settingStore = useProjectSettingStore();
|
||||||
|
const menus = ref<any[]>([]);
|
||||||
|
const selectedKeys = ref<string>(currentRoute.name as string);
|
||||||
|
const headerMenuSelectKey = ref<string>('');
|
||||||
|
|
||||||
const asyncRouteStore = useAsyncRouteStore();
|
const { navMode } = useProjectSetting();
|
||||||
const menus = ref<any[]>([]);
|
|
||||||
const selectedKeys = ref<string>(currentRoute.name as string);
|
|
||||||
|
|
||||||
const state = reactive({
|
// 获取当前打开的子菜单
|
||||||
openKeys: getOpenKeys,
|
const matched = currentRoute.matched;
|
||||||
});
|
|
||||||
|
|
||||||
// 跟随页面路由变化,切换菜单选中状态
|
const getOpenKeys =
|
||||||
watch(
|
matched && matched.length ? matched.map((item) => item.name) : [];
|
||||||
() => currentRoute.fullPath,
|
|
||||||
() => {
|
|
||||||
console.log(123);
|
|
||||||
updateMenu();
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
function updateSelectedKeys() {
|
const state = reactive({
|
||||||
const matched = currentRoute.matched;
|
openKeys: getOpenKeys,
|
||||||
state.openKeys = matched.map((item) => item.name);
|
});
|
||||||
const activeMenu: string = (currentRoute.meta?.activeMenu as string) || "";
|
|
||||||
selectedKeys.value = activeMenu
|
|
||||||
? (activeMenu as string)
|
|
||||||
: (currentRoute.name as string);
|
|
||||||
|
|
||||||
console.log(selectedKeys.value);
|
const inverted = computed(() => {
|
||||||
}
|
return ['dark', 'header-dark'].includes(settingStore.navTheme);
|
||||||
|
});
|
||||||
|
|
||||||
function updateMenu() {
|
const getSelectedKeys = computed(() => {
|
||||||
menus.value = generatorMenu(asyncRouteStore.getMenus);
|
let location = props.location;
|
||||||
updateSelectedKeys();
|
return location === 'left' ||
|
||||||
}
|
(location === 'header' && unref(navMode) === 'horizontal')
|
||||||
|
? unref(selectedKeys)
|
||||||
|
: unref(headerMenuSelectKey);
|
||||||
|
});
|
||||||
|
|
||||||
const getSelectedKeys = computed(() => {
|
// 监听分割菜单
|
||||||
return unref(selectedKeys);
|
watch(
|
||||||
});
|
() => settingStore.menuSetting.mixMenu,
|
||||||
|
() => {
|
||||||
|
updateMenu();
|
||||||
|
if (props.collapsed) {
|
||||||
|
emit('update:collapsed', !props.collapsed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
const clickMenuItem = (key: string) => {
|
// 监听菜单收缩状态
|
||||||
if (/http(s)?:/.test(key)) {
|
// watch(
|
||||||
window.open(key);
|
// () => props.collapsed,
|
||||||
} else {
|
// (newVal) => {
|
||||||
routerTurnByName(key);
|
// }
|
||||||
}
|
// );
|
||||||
};
|
|
||||||
|
|
||||||
//展开菜单
|
// 跟随页面路由变化,切换菜单选中状态
|
||||||
function menuExpanded(openKeys: string[]) {
|
watch(
|
||||||
if (!openKeys) return;
|
() => currentRoute.fullPath,
|
||||||
const latestOpenKey = openKeys.find(
|
() => {
|
||||||
(key) => state.openKeys.indexOf(key) === -1
|
updateMenu();
|
||||||
);
|
}
|
||||||
const isExistChildren = findChildrenLen(latestOpenKey as string);
|
);
|
||||||
state.openKeys = isExistChildren
|
|
||||||
? latestOpenKey
|
|
||||||
? [latestOpenKey]
|
|
||||||
: []
|
|
||||||
: openKeys;
|
|
||||||
}
|
|
||||||
|
|
||||||
//查找是否存在子路由
|
function updateSelectedKeys() {
|
||||||
function findChildrenLen(key: string) {
|
const matched = currentRoute.matched;
|
||||||
if (!key) return false;
|
state.openKeys = matched.map((item) => item.name);
|
||||||
const subRouteChildren: string[] = [];
|
const activeMenu: string =
|
||||||
for (const { children, key } of unref(menus)) {
|
(currentRoute.meta?.activeMenu as string) || '';
|
||||||
if (children && children.length) {
|
selectedKeys.value = activeMenu
|
||||||
subRouteChildren.push(key as string);
|
? (activeMenu as string)
|
||||||
|
: (currentRoute.name as string);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return subRouteChildren.includes(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
function updateMenu() {
|
||||||
updateMenu();
|
if (!settingStore.menuSetting.mixMenu) {
|
||||||
|
menus.value = generatorMenu(asyncRouteStore.getMenus);
|
||||||
|
} else {
|
||||||
|
//混合菜单
|
||||||
|
const firstRouteName: string =
|
||||||
|
(currentRoute.matched[0].name as string) || '';
|
||||||
|
menus.value = generatorMenuMix(
|
||||||
|
asyncRouteStore.getMenus,
|
||||||
|
firstRouteName,
|
||||||
|
props.location
|
||||||
|
);
|
||||||
|
const activeMenu: string = currentRoute?.matched[0].meta
|
||||||
|
?.activeMenu as string;
|
||||||
|
headerMenuSelectKey.value =
|
||||||
|
(activeMenu ? activeMenu : firstRouteName) || '';
|
||||||
|
}
|
||||||
|
updateSelectedKeys();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 点击菜单
|
||||||
|
function clickMenuItem(key: string) {
|
||||||
|
if (/http(s)?:/.test(key)) {
|
||||||
|
window.open(key);
|
||||||
|
} else {
|
||||||
|
router.push({ name: key });
|
||||||
|
}
|
||||||
|
emit('clickMenuItem' as any, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
//展开菜单
|
||||||
|
function menuExpanded(openKeys: string[]) {
|
||||||
|
if (!openKeys) return;
|
||||||
|
const latestOpenKey = openKeys.find(
|
||||||
|
(key) => state.openKeys.indexOf(key) === -1
|
||||||
|
);
|
||||||
|
const isExistChildren = findChildrenLen(latestOpenKey as string);
|
||||||
|
state.openKeys = isExistChildren
|
||||||
|
? latestOpenKey
|
||||||
|
? [latestOpenKey]
|
||||||
|
: []
|
||||||
|
: openKeys;
|
||||||
|
}
|
||||||
|
|
||||||
|
//查找是否存在子路由
|
||||||
|
function findChildrenLen(key: string) {
|
||||||
|
if (!key) return false;
|
||||||
|
const subRouteChildren: string[] = [];
|
||||||
|
for (const { children, key } of unref(menus)) {
|
||||||
|
if (children && children.length) {
|
||||||
|
subRouteChildren.push(key as string);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return subRouteChildren.includes(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
updateMenu();
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
...toRefs(state),
|
||||||
|
inverted,
|
||||||
|
menus,
|
||||||
|
selectedKeys,
|
||||||
|
headerMenuSelectKey,
|
||||||
|
getSelectedKeys,
|
||||||
|
clickMenuItem,
|
||||||
|
menuExpanded,
|
||||||
|
};
|
||||||
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.sider-top {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
margin-top: 10px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
3
src/layout/components/TagsView/index.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import TabsView from './index.vue';
|
||||||
|
|
||||||
|
export { TabsView };
|
||||||
641
src/layout/components/TagsView/index.vue
Normal file
@ -0,0 +1,641 @@
|
|||||||
|
<template>
|
||||||
|
<div
|
||||||
|
class="box-border tabs-view"
|
||||||
|
:class="{
|
||||||
|
'tabs-view-fix': state.multiTabsSetting.fixed,
|
||||||
|
'tabs-view-fixed-header': state.isMultiHeaderFixed,
|
||||||
|
'tabs-view-default-background': getDarkTheme === false,
|
||||||
|
'tabs-view-dark-background': getDarkTheme === true,
|
||||||
|
}"
|
||||||
|
:style="getChangeStyle"
|
||||||
|
>
|
||||||
|
<div class="tabs-view-main">
|
||||||
|
<div
|
||||||
|
ref="navWrap"
|
||||||
|
class="tabs-card"
|
||||||
|
:class="{ 'tabs-card-scrollable': state.scrollable }"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="tabs-card-prev"
|
||||||
|
:class="{ 'tabs-card-prev-hide': !state.scrollable }"
|
||||||
|
@click="scrollPrev"
|
||||||
|
>
|
||||||
|
<n-icon size="16" color="#515a6e">
|
||||||
|
<LeftOutlined />
|
||||||
|
</n-icon>
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
class="tabs-card-next"
|
||||||
|
:class="{ 'tabs-card-next-hide': !state.scrollable }"
|
||||||
|
@click="scrollNext"
|
||||||
|
>
|
||||||
|
<n-icon size="16" color="#515a6e">
|
||||||
|
<RightOutlined />
|
||||||
|
</n-icon>
|
||||||
|
</span>
|
||||||
|
<div ref="navScroll" class="tabs-card-scroll">
|
||||||
|
<Draggable
|
||||||
|
:list="tabsList"
|
||||||
|
animation="300"
|
||||||
|
item-key="fullPath"
|
||||||
|
class="flex"
|
||||||
|
>
|
||||||
|
<template #item="{ element }">
|
||||||
|
<div
|
||||||
|
:id="`tag${element.fullPath.split('/').join('\/')}`"
|
||||||
|
class="tabs-card-scroll-item"
|
||||||
|
:class="{ 'active-item': state.activeKey === element.fullPath }"
|
||||||
|
@click.stop="goPage(element)"
|
||||||
|
@contextmenu="handleContextMenu($event, element)"
|
||||||
|
>
|
||||||
|
<span>{{ element.meta.title }}</span>
|
||||||
|
<n-icon
|
||||||
|
size="14"
|
||||||
|
@click.stop="closeTabItem(element)"
|
||||||
|
v-if="!element.meta.affix"
|
||||||
|
>
|
||||||
|
<CloseOutlined />
|
||||||
|
</n-icon>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</Draggable>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="tabs-close">
|
||||||
|
<n-dropdown
|
||||||
|
trigger="hover"
|
||||||
|
@select="closeHandleSelect"
|
||||||
|
placement="bottom-end"
|
||||||
|
:options="TabsMenuOptions"
|
||||||
|
>
|
||||||
|
<div class="tabs-close-btn">
|
||||||
|
<n-icon size="16" color="#515a6e">
|
||||||
|
<DownOutlined />
|
||||||
|
</n-icon>
|
||||||
|
</div>
|
||||||
|
</n-dropdown>
|
||||||
|
</div>
|
||||||
|
<n-dropdown
|
||||||
|
:show="state.showDropdown"
|
||||||
|
:x="state.dropdownX"
|
||||||
|
:y="state.dropdownY"
|
||||||
|
@clickoutside="onClickOutside"
|
||||||
|
placement="bottom-start"
|
||||||
|
@select="closeHandleSelect"
|
||||||
|
:options="TabsMenuOptions"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { useRoute, useRouter } from 'vue-router';
|
||||||
|
import { storage } from '@/utils';
|
||||||
|
import { useTabsViewStore } from '@/store/modules/tabsView';
|
||||||
|
import { useAsyncRouteStore } from '@/store/modules/asyncRoute';
|
||||||
|
import { RouteItem } from '@/store/modules/tabsView';
|
||||||
|
import { useProjectSetting } from '@/hooks/setting/useProjectSetting';
|
||||||
|
import Draggable from 'vuedraggable';
|
||||||
|
import { PageEnum } from '@/enums/pageEnum';
|
||||||
|
import {
|
||||||
|
DownOutlined,
|
||||||
|
ReloadOutlined,
|
||||||
|
CloseOutlined,
|
||||||
|
ColumnWidthOutlined,
|
||||||
|
MinusOutlined,
|
||||||
|
LeftOutlined,
|
||||||
|
RightOutlined,
|
||||||
|
} from '@vicons/antd';
|
||||||
|
import { renderIcon } from '@/utils';
|
||||||
|
// import elementResizeDetectorMaker from "element-resize-detector";
|
||||||
|
import { useDesignSetting } from '@/hooks/setting/useDesignSetting';
|
||||||
|
import { useProjectSettingStore } from '@/store/modules/projectSetting';
|
||||||
|
import { useThemeVars } from 'naive-ui';
|
||||||
|
import { useGo } from '@/hooks/web/usePage';
|
||||||
|
import { StorageEnum } from '@/enums/storageEnum';
|
||||||
|
const props = defineProps({
|
||||||
|
collapsed: {
|
||||||
|
type: Boolean,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const { getDarkTheme, getAppTheme } = useDesignSetting();
|
||||||
|
const { navMode, headerSetting, menuSetting, multiTabsSetting, isMobile } =
|
||||||
|
useProjectSetting();
|
||||||
|
const settingStore = useProjectSettingStore();
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
|
const router = useRouter();
|
||||||
|
const tabsViewStore = useTabsViewStore();
|
||||||
|
const asyncRouteStore = useAsyncRouteStore();
|
||||||
|
const navScroll: any = ref(null);
|
||||||
|
const navWrap: any = ref(null);
|
||||||
|
const isCurrent = ref(false);
|
||||||
|
const go = useGo();
|
||||||
|
|
||||||
|
const themeVars = useThemeVars();
|
||||||
|
|
||||||
|
const getCardColor = computed(() => {
|
||||||
|
return themeVars.value.cardColor;
|
||||||
|
});
|
||||||
|
|
||||||
|
const getBaseColor = computed(() => {
|
||||||
|
return themeVars.value.textColor1;
|
||||||
|
});
|
||||||
|
|
||||||
|
const state = reactive({
|
||||||
|
activeKey: route.fullPath,
|
||||||
|
scrollable: false,
|
||||||
|
dropdownX: 0,
|
||||||
|
dropdownY: 0,
|
||||||
|
showDropdown: false,
|
||||||
|
isMultiHeaderFixed: false,
|
||||||
|
multiTabsSetting: multiTabsSetting,
|
||||||
|
});
|
||||||
|
|
||||||
|
// 获取简易的路由对象
|
||||||
|
const getSimpleRoute = (route): RouteItem => {
|
||||||
|
const { fullPath, hash, meta, name, params, path, query } = route;
|
||||||
|
return { fullPath, hash, meta, name, params, path, query };
|
||||||
|
};
|
||||||
|
|
||||||
|
const isMixMenuNoneSub = computed(() => {
|
||||||
|
const mixMenu = settingStore.menuSetting.mixMenu;
|
||||||
|
const currentRoute = useRoute();
|
||||||
|
if (navMode.value != 'horizontal-mix') return true;
|
||||||
|
return !(
|
||||||
|
navMode.value === 'horizontal-mix' &&
|
||||||
|
mixMenu &&
|
||||||
|
currentRoute.meta.isRoot
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
//动态组装样式 菜单缩进
|
||||||
|
const getChangeStyle = computed(() => {
|
||||||
|
const { collapsed } = props;
|
||||||
|
const { minMenuWidth, menuWidth }: any = menuSetting.value;
|
||||||
|
const { fixed }: any = multiTabsSetting.value;
|
||||||
|
let lenNum =
|
||||||
|
navMode.value === 'horizontal' || !isMixMenuNoneSub.value
|
||||||
|
? '0px'
|
||||||
|
: collapsed
|
||||||
|
? `${minMenuWidth}px`
|
||||||
|
: `${menuWidth}px`;
|
||||||
|
|
||||||
|
if (isMobile.value) {
|
||||||
|
return {
|
||||||
|
left: '0px',
|
||||||
|
width: '100%',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
left: lenNum,
|
||||||
|
width: `calc(100% - ${!fixed ? '0px' : lenNum})`,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
//tags 右侧下拉菜单
|
||||||
|
const TabsMenuOptions = computed(() => {
|
||||||
|
const isDisabled = tabsList.value.length <= 1;
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
label: '刷新当前',
|
||||||
|
key: '1',
|
||||||
|
icon: renderIcon(ReloadOutlined),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: `关闭当前`,
|
||||||
|
key: '2',
|
||||||
|
disabled: isCurrent.value || isDisabled,
|
||||||
|
icon: renderIcon(CloseOutlined),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '关闭其他',
|
||||||
|
key: '3',
|
||||||
|
disabled: isDisabled,
|
||||||
|
icon: renderIcon(ColumnWidthOutlined),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '关闭全部',
|
||||||
|
key: '4',
|
||||||
|
disabled: isDisabled,
|
||||||
|
icon: renderIcon(MinusOutlined),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
});
|
||||||
|
|
||||||
|
let cacheRoutes: RouteItem[] = [];
|
||||||
|
const simpleRoute = getSimpleRoute(route);
|
||||||
|
try {
|
||||||
|
const routesStr = storage.get(StorageEnum.ZS_TABS_ROUTES) as
|
||||||
|
| string
|
||||||
|
| null
|
||||||
|
| undefined;
|
||||||
|
cacheRoutes = routesStr ? JSON.parse(routesStr) : [simpleRoute];
|
||||||
|
} catch (e) {
|
||||||
|
cacheRoutes = [simpleRoute];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将最新的路由信息同步到 localStorage 中
|
||||||
|
const routes = router.getRoutes();
|
||||||
|
cacheRoutes.forEach((cacheRoute) => {
|
||||||
|
const route = routes.find((route) => route.path === cacheRoute.path);
|
||||||
|
if (route) {
|
||||||
|
cacheRoute.meta = route.meta || cacheRoute.meta;
|
||||||
|
cacheRoute.name = (route.name || cacheRoute.name) as string;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 初始化标签页
|
||||||
|
tabsViewStore.initTabs(cacheRoutes);
|
||||||
|
|
||||||
|
//监听滚动条
|
||||||
|
function onScroll(e) {
|
||||||
|
let scrollTop =
|
||||||
|
e.target.scrollTop ||
|
||||||
|
document.documentElement.scrollTop ||
|
||||||
|
window.pageYOffset ||
|
||||||
|
document.body.scrollTop; // 滚动条偏移量
|
||||||
|
state.isMultiHeaderFixed = !!(
|
||||||
|
!headerSetting.value.fixed &&
|
||||||
|
multiTabsSetting.value.fixed &&
|
||||||
|
scrollTop >= 64
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener('scroll', onScroll, true);
|
||||||
|
|
||||||
|
// 移除缓存组件名称
|
||||||
|
const delKeepAliveCompName = () => {
|
||||||
|
if (route.meta.keepAlive) {
|
||||||
|
const name = router.currentRoute.value.matched.find(
|
||||||
|
(item) => item.name == route.name
|
||||||
|
)?.components?.default.name;
|
||||||
|
if (name) {
|
||||||
|
asyncRouteStore.keepAliveComponents =
|
||||||
|
asyncRouteStore.keepAliveComponents.filter((item) => item != name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 标签页列表
|
||||||
|
const tabsList: any = computed(() => tabsViewStore.tabsList);
|
||||||
|
const whiteList: string[] = [
|
||||||
|
PageEnum.BASE_LOGIN_NAME,
|
||||||
|
PageEnum.REDIRECT_NAME,
|
||||||
|
PageEnum.ERROR_PAGE_NAME,
|
||||||
|
];
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => route.fullPath,
|
||||||
|
(to) => {
|
||||||
|
if (whiteList.includes(route.name as string)) return;
|
||||||
|
state.activeKey = to;
|
||||||
|
tabsViewStore.addTab(getSimpleRoute(route));
|
||||||
|
updateNavScroll(true);
|
||||||
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
// 在页面关闭或刷新之前,保存数据
|
||||||
|
window.addEventListener('beforeunload', () => {
|
||||||
|
storage.set(StorageEnum.ZS_TABS_ROUTES, JSON.stringify(tabsList.value));
|
||||||
|
});
|
||||||
|
|
||||||
|
// 关闭当前页面
|
||||||
|
const removeTab = (route) => {
|
||||||
|
if (tabsList.value.length === 1) {
|
||||||
|
return window['$message'].warning('这已经是最后一页,不能再关闭了!');
|
||||||
|
}
|
||||||
|
delKeepAliveCompName();
|
||||||
|
tabsViewStore.closeCurrentTab(route);
|
||||||
|
// 如果关闭的是当前页
|
||||||
|
if (state.activeKey === route.fullPath) {
|
||||||
|
const currentRoute = tabsList.value[Math.max(0, tabsList.value.length - 1)];
|
||||||
|
state.activeKey = currentRoute.fullPath;
|
||||||
|
router.push(currentRoute);
|
||||||
|
}
|
||||||
|
updateNavScroll();
|
||||||
|
};
|
||||||
|
|
||||||
|
// 刷新页面
|
||||||
|
const reloadPage = () => {
|
||||||
|
delKeepAliveCompName();
|
||||||
|
router.push({
|
||||||
|
path: '/redirect' + route.fullPath,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 注入刷新页面方法
|
||||||
|
provide('reloadPage', reloadPage);
|
||||||
|
|
||||||
|
// 关闭左侧
|
||||||
|
const closeLeft = (route) => {
|
||||||
|
tabsViewStore.closeLeftTabs(route);
|
||||||
|
state.activeKey = route.fullPath;
|
||||||
|
router.replace(route.fullPath);
|
||||||
|
updateNavScroll();
|
||||||
|
};
|
||||||
|
|
||||||
|
// 关闭右侧
|
||||||
|
const closeRight = (route) => {
|
||||||
|
tabsViewStore.closeRightTabs(route);
|
||||||
|
state.activeKey = route.fullPath;
|
||||||
|
router.replace(route.fullPath);
|
||||||
|
updateNavScroll();
|
||||||
|
};
|
||||||
|
|
||||||
|
// 关闭其他
|
||||||
|
const closeOther = (route) => {
|
||||||
|
tabsViewStore.closeOtherTabs(route);
|
||||||
|
state.activeKey = route.fullPath;
|
||||||
|
router.replace(route.fullPath);
|
||||||
|
updateNavScroll();
|
||||||
|
};
|
||||||
|
|
||||||
|
// 关闭全部
|
||||||
|
const closeAll = () => {
|
||||||
|
tabsViewStore.closeAllTabs();
|
||||||
|
router.replace(PageEnum.BASE_HOME);
|
||||||
|
updateNavScroll();
|
||||||
|
};
|
||||||
|
|
||||||
|
//tab 操作
|
||||||
|
const closeHandleSelect = (key) => {
|
||||||
|
switch (key) {
|
||||||
|
//刷新
|
||||||
|
case '1':
|
||||||
|
reloadPage();
|
||||||
|
break;
|
||||||
|
//关闭
|
||||||
|
case '2':
|
||||||
|
removeTab(route);
|
||||||
|
break;
|
||||||
|
//关闭其他
|
||||||
|
case '3':
|
||||||
|
closeOther(route);
|
||||||
|
break;
|
||||||
|
//关闭所有
|
||||||
|
case '4':
|
||||||
|
closeAll();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
updateNavScroll();
|
||||||
|
state.showDropdown = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param value 要滚动到的位置
|
||||||
|
* @param amplitude 每次滚动的长度
|
||||||
|
*/
|
||||||
|
function scrollTo(value: number, amplitude: number) {
|
||||||
|
const currentScroll = navScroll.value.scrollLeft;
|
||||||
|
const scrollWidth =
|
||||||
|
(amplitude > 0 && currentScroll + amplitude >= value) ||
|
||||||
|
(amplitude < 0 && currentScroll + amplitude <= value)
|
||||||
|
? value
|
||||||
|
: currentScroll + amplitude;
|
||||||
|
navScroll.value && navScroll.value.scrollTo(scrollWidth, 0);
|
||||||
|
if (scrollWidth === value) return;
|
||||||
|
return window.requestAnimationFrame(() => scrollTo(value, amplitude));
|
||||||
|
}
|
||||||
|
|
||||||
|
function scrollPrev() {
|
||||||
|
const containerWidth = navScroll.value.offsetWidth;
|
||||||
|
const currentScroll = navScroll.value.scrollLeft;
|
||||||
|
|
||||||
|
if (!currentScroll) return;
|
||||||
|
const scrollLeft =
|
||||||
|
currentScroll > containerWidth ? currentScroll - containerWidth : 0;
|
||||||
|
scrollTo(scrollLeft, (scrollLeft - currentScroll) / 20);
|
||||||
|
}
|
||||||
|
|
||||||
|
function scrollNext() {
|
||||||
|
const containerWidth = navScroll.value.offsetWidth;
|
||||||
|
const navWidth = navScroll.value.scrollWidth;
|
||||||
|
const currentScroll = navScroll.value.scrollLeft;
|
||||||
|
|
||||||
|
if (navWidth - currentScroll <= containerWidth) return;
|
||||||
|
const scrollLeft =
|
||||||
|
navWidth - currentScroll > containerWidth * 2
|
||||||
|
? currentScroll + containerWidth
|
||||||
|
: navWidth - containerWidth;
|
||||||
|
scrollTo(scrollLeft, (scrollLeft - currentScroll) / 20);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param autoScroll 是否开启自动滚动功能
|
||||||
|
*/
|
||||||
|
async function updateNavScroll(autoScroll?: boolean) {
|
||||||
|
await nextTick();
|
||||||
|
if (!navScroll.value) return;
|
||||||
|
const containerWidth = navScroll.value.offsetWidth;
|
||||||
|
const navWidth = navScroll.value.scrollWidth;
|
||||||
|
|
||||||
|
if (containerWidth < navWidth) {
|
||||||
|
state.scrollable = true;
|
||||||
|
if (autoScroll) {
|
||||||
|
let tagList =
|
||||||
|
navScroll.value.querySelectorAll('.tabs-card-scroll-item') || [];
|
||||||
|
[...tagList].forEach((tag: HTMLElement) => {
|
||||||
|
// fix SyntaxError
|
||||||
|
if (tag.id === `tag${state.activeKey.split('/').join('\/')}`) {
|
||||||
|
tag.scrollIntoView && tag.scrollIntoView();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
state.scrollable = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleResize() {
|
||||||
|
updateNavScroll(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleContextMenu(e, item) {
|
||||||
|
e.preventDefault();
|
||||||
|
isCurrent.value = PageEnum.BASE_HOME === item.path;
|
||||||
|
state.showDropdown = false;
|
||||||
|
nextTick().then(() => {
|
||||||
|
state.showDropdown = true;
|
||||||
|
state.dropdownX = e.clientX;
|
||||||
|
state.dropdownY = e.clientY;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function onClickOutside() {
|
||||||
|
state.showDropdown = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//tags 跳转页面
|
||||||
|
function goPage(e) {
|
||||||
|
const { fullPath } = e;
|
||||||
|
if (fullPath === route.fullPath) return;
|
||||||
|
state.activeKey = fullPath;
|
||||||
|
go(e, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
//删除tab
|
||||||
|
function closeTabItem(e) {
|
||||||
|
const { fullPath } = e;
|
||||||
|
const routeInfo = tabsList.value.find((item) => item.fullPath == fullPath);
|
||||||
|
removeTab(routeInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
// onElementResize();
|
||||||
|
});
|
||||||
|
|
||||||
|
// function onElementResize() {
|
||||||
|
// let observer;
|
||||||
|
// observer = elementResizeDetectorMaker();
|
||||||
|
// observer.listenTo(navWrap.value, handleResize);
|
||||||
|
// }
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.tabs-view {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
padding: 6px 0;
|
||||||
|
transition: all 0.2s ease-in-out;
|
||||||
|
|
||||||
|
&-main {
|
||||||
|
display: flex;
|
||||||
|
min-width: 100%;
|
||||||
|
max-width: 100%;
|
||||||
|
height: 32px;
|
||||||
|
|
||||||
|
.tabs-card {
|
||||||
|
position: relative;
|
||||||
|
flex-grow: 1;
|
||||||
|
flex-shrink: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
-webkit-box-flex: 1;
|
||||||
|
|
||||||
|
.tabs-card-prev,
|
||||||
|
.tabs-card-next {
|
||||||
|
position: absolute;
|
||||||
|
width: 32px;
|
||||||
|
line-height: 32px;
|
||||||
|
text-align: center;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
.n-icon {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabs-card-prev {
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabs-card-next {
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabs-card-next-hide,
|
||||||
|
.tabs-card-prev-hide {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-scroll {
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
|
||||||
|
&-item {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
flex: 0 0 auto;
|
||||||
|
height: 32px;
|
||||||
|
padding: 6px 16px 4px;
|
||||||
|
margin-right: 6px;
|
||||||
|
color: v-bind(getbasecolor);
|
||||||
|
cursor: pointer;
|
||||||
|
background: v-bind(getcardcolor);
|
||||||
|
border-radius: 3px;
|
||||||
|
|
||||||
|
span {
|
||||||
|
float: left;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: #515a6e;
|
||||||
|
}
|
||||||
|
|
||||||
|
.n-icon {
|
||||||
|
position: relative;
|
||||||
|
width: 21px;
|
||||||
|
height: 22px;
|
||||||
|
margin-right: -6px;
|
||||||
|
color: #808695;
|
||||||
|
text-align: center;
|
||||||
|
vertical-align: middle;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: #515a6e !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
svg {
|
||||||
|
display: inline-block;
|
||||||
|
height: 21px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.active-item {
|
||||||
|
color: v-bind(getapptheme);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabs-card-scrollable {
|
||||||
|
padding: 0 32px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabs-close {
|
||||||
|
width: 32px;
|
||||||
|
min-width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
line-height: 32px;
|
||||||
|
text-align: center;
|
||||||
|
cursor: pointer;
|
||||||
|
background: var(--color);
|
||||||
|
border-radius: 2px;
|
||||||
|
|
||||||
|
&-btn {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 100%;
|
||||||
|
color: var(--color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabs-view-default-background {
|
||||||
|
background: #f5f7f9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabs-view-dark-background {
|
||||||
|
background: #101014;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabs-view-fix {
|
||||||
|
position: fixed;
|
||||||
|
left: 200px;
|
||||||
|
z-index: 5;
|
||||||
|
padding: 6px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabs-view-fixed-header {
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -1,3 +0,0 @@
|
|||||||
import TransitionMain from "./index.vue";
|
|
||||||
|
|
||||||
export { TransitionMain };
|
|
||||||
@ -1,14 +0,0 @@
|
|||||||
<template>
|
|
||||||
<router-view v-slot="{ Component, route }">
|
|
||||||
<transition name="fade" mode="out-in" appear>
|
|
||||||
<component
|
|
||||||
v-if="route.noKeepAlive"
|
|
||||||
:is="Component"
|
|
||||||
:key="route.fullPath"
|
|
||||||
/>
|
|
||||||
<keep-alive v-else>
|
|
||||||
<component :is="Component" :key="route.fullPath" />
|
|
||||||
</keep-alive>
|
|
||||||
</transition>
|
|
||||||
</router-view>
|
|
||||||
</template>
|
|
||||||
@ -1,33 +1,283 @@
|
|||||||
<script setup lang="ts"></script>
|
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<n-layout class="layout" position="absolute" has-sider>
|
<n-layout class="layout" :position="fixedMenu" has-sider>
|
||||||
<Sider />
|
<n-layout-sider
|
||||||
|
v-if="
|
||||||
|
!isMobile &&
|
||||||
|
isMixMenuNoneSub &&
|
||||||
|
(navMode === 'vertical' || navMode === 'horizontal-mix')
|
||||||
|
"
|
||||||
|
show-trigger="bar"
|
||||||
|
@collapse="collapsed = true"
|
||||||
|
:position="fixedMenu"
|
||||||
|
@expand="collapsed = false"
|
||||||
|
:collapsed="collapsed"
|
||||||
|
collapse-mode="width"
|
||||||
|
:collapsed-width="64"
|
||||||
|
:width="leftMenuWidth"
|
||||||
|
:native-scrollbar="false"
|
||||||
|
:inverted="inverted"
|
||||||
|
class="layout-sider"
|
||||||
|
>
|
||||||
|
<Logo :collapsed="collapsed" />
|
||||||
|
<AsideMenu
|
||||||
|
v-model:collapsed="collapsed"
|
||||||
|
v-model:location="getMenuLocation"
|
||||||
|
/>
|
||||||
|
</n-layout-sider>
|
||||||
|
|
||||||
<n-layout>
|
<n-drawer
|
||||||
<n-layout-header>
|
v-model:show="showSideDrawer"
|
||||||
<Header />
|
:width="menuWidth"
|
||||||
|
:placement="'left'"
|
||||||
|
class="layout-side-drawer"
|
||||||
|
>
|
||||||
|
<n-layout-sider
|
||||||
|
:position="fixedMenu"
|
||||||
|
:collapsed="false"
|
||||||
|
:width="menuWidth"
|
||||||
|
:native-scrollbar="false"
|
||||||
|
:inverted="inverted"
|
||||||
|
class="layout-sider"
|
||||||
|
>
|
||||||
|
<Logo :collapsed="collapsed" />
|
||||||
|
<AsideMenu v-model:location="getMenuLocation" />
|
||||||
|
</n-layout-sider>
|
||||||
|
</n-drawer>
|
||||||
|
|
||||||
|
<n-layout :inverted="inverted">
|
||||||
|
<n-layout-header :inverted="getHeaderInverted" :position="fixedHeader">
|
||||||
|
<PageHeader v-model:collapsed="collapsed" :inverted="inverted" />
|
||||||
</n-layout-header>
|
</n-layout-header>
|
||||||
|
|
||||||
<n-layout-content>
|
<n-layout-content
|
||||||
<div class="layout-content-main">
|
class="layout-content"
|
||||||
<Main />
|
:class="{ 'layout-default-background': getDarkTheme === false }"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="layout-content-main"
|
||||||
|
:class="{
|
||||||
|
'layout-content-main-fix': fixedMulti,
|
||||||
|
'fluid-header': fixedHeader === 'static',
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<TabsView v-if="isMultiTabs" v-model:collapsed="collapsed" />
|
||||||
|
<div
|
||||||
|
class="main-view"
|
||||||
|
:class="{
|
||||||
|
'main-view-fix': fixedMulti,
|
||||||
|
noMultiTabs: !isMultiTabs,
|
||||||
|
'mt-3': !isMultiTabs,
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<MainView />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<!--1.15废弃,没啥用,占用操作空间-->
|
||||||
|
<!-- <NLayoutFooter v-if="getShowFooter">-->
|
||||||
|
<!-- <PageFooter />-->
|
||||||
|
<!-- </NLayoutFooter>-->
|
||||||
</n-layout-content>
|
</n-layout-content>
|
||||||
|
<n-back-top :right="100" />
|
||||||
</n-layout>
|
</n-layout>
|
||||||
</n-layout>
|
</n-layout>
|
||||||
</template>
|
</template>
|
||||||
<style scoped lang="scss">
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref, unref, computed, onMounted } from 'vue';
|
||||||
|
import { Logo } from './components/Logo';
|
||||||
|
import { TabsView } from './components/TagsView';
|
||||||
|
import { MainView } from './components/Main';
|
||||||
|
import { AsideMenu } from './components/Sider';
|
||||||
|
import { PageHeader } from './components/Header';
|
||||||
|
import { useProjectSetting } from '@/hooks/setting/useProjectSetting';
|
||||||
|
import { useDesignSetting } from '@/hooks/setting/useDesignSetting';
|
||||||
|
import { useRoute } from 'vue-router';
|
||||||
|
import { useProjectSettingStore } from '@/store/modules/projectSetting';
|
||||||
|
|
||||||
|
const { getDarkTheme } = useDesignSetting();
|
||||||
|
const {
|
||||||
|
// showFooter,
|
||||||
|
navMode,
|
||||||
|
navTheme,
|
||||||
|
headerSetting,
|
||||||
|
menuSetting,
|
||||||
|
multiTabsSetting,
|
||||||
|
} = useProjectSetting();
|
||||||
|
|
||||||
|
const settingStore = useProjectSettingStore();
|
||||||
|
|
||||||
|
const collapsed = ref<boolean>(false);
|
||||||
|
|
||||||
|
const { mobileWidth, menuWidth } = unref(menuSetting);
|
||||||
|
|
||||||
|
const isMobile = computed<boolean>({
|
||||||
|
get: () => settingStore.getIsMobile,
|
||||||
|
set: (val) => settingStore.setIsMobile(val),
|
||||||
|
});
|
||||||
|
|
||||||
|
const fixedHeader = computed(() => {
|
||||||
|
const { fixed } = unref(headerSetting);
|
||||||
|
return fixed ? 'absolute' : 'static';
|
||||||
|
});
|
||||||
|
|
||||||
|
const isMixMenuNoneSub = computed(() => {
|
||||||
|
const mixMenu = unref(menuSetting).mixMenu;
|
||||||
|
const currentRoute = useRoute();
|
||||||
|
if (unref(navMode) != 'horizontal-mix') return true;
|
||||||
|
if (
|
||||||
|
unref(navMode) === 'horizontal-mix' &&
|
||||||
|
mixMenu &&
|
||||||
|
currentRoute.meta.isRoot
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
const fixedMenu = computed(() => {
|
||||||
|
const { fixed } = unref(headerSetting);
|
||||||
|
return fixed ? 'absolute' : 'static';
|
||||||
|
});
|
||||||
|
|
||||||
|
const isMultiTabs = computed(() => {
|
||||||
|
return unref(multiTabsSetting).show;
|
||||||
|
});
|
||||||
|
|
||||||
|
const fixedMulti = computed(() => {
|
||||||
|
return unref(multiTabsSetting).fixed;
|
||||||
|
});
|
||||||
|
|
||||||
|
const inverted = computed(() => {
|
||||||
|
return ['dark', 'header-dark'].includes(unref(navTheme));
|
||||||
|
});
|
||||||
|
|
||||||
|
const getHeaderInverted = computed(() => {
|
||||||
|
return ['light', 'header-dark'].includes(unref(navTheme))
|
||||||
|
? unref(inverted)
|
||||||
|
: !unref(inverted);
|
||||||
|
});
|
||||||
|
|
||||||
|
const leftMenuWidth = computed(() => {
|
||||||
|
const { minMenuWidth, menuWidth } = unref(menuSetting);
|
||||||
|
return collapsed.value ? minMenuWidth : menuWidth;
|
||||||
|
});
|
||||||
|
|
||||||
|
const getMenuLocation = computed(() => {
|
||||||
|
return 'left';
|
||||||
|
});
|
||||||
|
|
||||||
|
// 控制显示或隐藏移动端侧边栏
|
||||||
|
const showSideDrawer = computed({
|
||||||
|
get: () => isMobile.value && collapsed.value,
|
||||||
|
set: (val) => (collapsed.value = val),
|
||||||
|
});
|
||||||
|
|
||||||
|
//判断是否触发移动端模式
|
||||||
|
const checkMobileMode = () => {
|
||||||
|
if (document.body.clientWidth <= mobileWidth) {
|
||||||
|
isMobile.value = true;
|
||||||
|
} else {
|
||||||
|
isMobile.value = false;
|
||||||
|
}
|
||||||
|
collapsed.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const watchWidth = () => {
|
||||||
|
const Width = document.body.clientWidth;
|
||||||
|
if (Width <= 950) {
|
||||||
|
collapsed.value = true;
|
||||||
|
} else collapsed.value = false;
|
||||||
|
|
||||||
|
checkMobileMode();
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
checkMobileMode();
|
||||||
|
window.addEventListener('resize', watchWidth);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.layout-side-drawer {
|
||||||
|
background-color: rgb(0 20 40);
|
||||||
|
|
||||||
|
.layout-sider {
|
||||||
|
position: relative;
|
||||||
|
z-index: 13;
|
||||||
|
min-height: 100vh;
|
||||||
|
box-shadow: 2px 0 8px 0 rgb(29 35 41 / 5%);
|
||||||
|
transition: all 0.2s ease-in-out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<style lang="scss" scoped>
|
||||||
.layout {
|
.layout {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex: auto;
|
flex: auto;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
|
|
||||||
&-content-main {
|
&-default-background {
|
||||||
flex: auto;
|
|
||||||
min-height: calc(100vh - 50px);
|
|
||||||
padding: 10px;
|
|
||||||
background: #f5f7f9;
|
background: #f5f7f9;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.layout-sider {
|
||||||
|
position: relative;
|
||||||
|
z-index: 13;
|
||||||
|
min-height: 100vh;
|
||||||
|
box-shadow: 2px 0 8px 0 rgb(29 35 41 / 5%);
|
||||||
|
transition: all 0.2s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.layout-sider-fix {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-layout {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.layout-right-fix {
|
||||||
|
min-height: 100vh;
|
||||||
|
padding-left: 200px;
|
||||||
|
overflow-x: hidden;
|
||||||
|
transition: all 0.2s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.layout-content {
|
||||||
|
flex: auto;
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.n-layout-header.n-layout-header--absolute-positioned {
|
||||||
|
z-index: 11;
|
||||||
|
}
|
||||||
|
|
||||||
|
.n-layout-footer {
|
||||||
|
background: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.layout-content-main {
|
||||||
|
position: relative;
|
||||||
|
padding-top: 64px;
|
||||||
|
margin: 0 10px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.layout-content-main-fix {
|
||||||
|
padding-top: 64px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fluid-header {
|
||||||
|
padding-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-view-fix {
|
||||||
|
padding-top: 44px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.noMultiTabs {
|
||||||
|
padding-top: 0;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
23
src/main.ts
@ -1,11 +1,12 @@
|
|||||||
import { createApp } from "vue";
|
import './styles/tailwind.css';
|
||||||
import App from "./App.vue";
|
import { createApp } from 'vue';
|
||||||
import router, { setupRouter } from "@/router";
|
import App from './App.vue';
|
||||||
import { setupStore } from "@/store";
|
import router, { setupRouter } from '@/router';
|
||||||
import { setupNaive, setupNaiveDiscreteApi, setupDirectives } from "@/plugins";
|
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() {
|
async function appInit() {
|
||||||
const app = createApp(App);
|
const app = createApp(App);
|
||||||
@ -24,15 +25,17 @@ async function appInit() {
|
|||||||
// 挂载路由
|
// 挂载路由
|
||||||
setupRouter(app);
|
setupRouter(app);
|
||||||
|
|
||||||
|
app.component('SvgIcon', SvgIcon);
|
||||||
|
|
||||||
// 路由准备就绪后挂载APP实例
|
// 路由准备就绪后挂载APP实例
|
||||||
await router.isReady();
|
await router.isReady();
|
||||||
|
|
||||||
const meta = document.createElement("meta");
|
const meta = document.createElement('meta');
|
||||||
meta.name = "naive-ui-style";
|
meta.name = 'naive-ui-style';
|
||||||
document.head.appendChild(meta);
|
document.head.appendChild(meta);
|
||||||
|
|
||||||
// 挂载到页面
|
// 挂载到页面
|
||||||
app.mount("#app", true);
|
app.mount('#app', true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void appInit();
|
void appInit();
|
||||||
|
|||||||
1
src/plugins/assets.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
import 'virtual:svg-icons-register';
|
||||||
@ -1,11 +1,11 @@
|
|||||||
import { App } from "vue";
|
import { App } from 'vue';
|
||||||
|
|
||||||
import draggable from "@/directives/draggable";
|
import draggable from '@/directives/draggable';
|
||||||
/**
|
/**
|
||||||
* 注册全局自定义指令
|
* 注册全局自定义指令
|
||||||
* @param app
|
* @param app
|
||||||
*/
|
*/
|
||||||
export function setupDirectives(app: App) {
|
export function setupDirectives(app: App) {
|
||||||
// 拖拽指令
|
// 拖拽指令
|
||||||
app.directive("draggable", draggable);
|
app.directive('draggable', draggable);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,7 +7,7 @@ import {
|
|||||||
InformationCircleOutline as InformationCircleIcon,
|
InformationCircleOutline as InformationCircleIcon,
|
||||||
BagOutline as BagOutlineIcon,
|
BagOutline as BagOutlineIcon,
|
||||||
BagCheckOutline as BagCheckOutlineIcon,
|
BagCheckOutline as BagCheckOutlineIcon,
|
||||||
} from "@vicons/ionicons5";
|
} from '@vicons/ionicons5';
|
||||||
|
|
||||||
const ionicons5 = {
|
const ionicons5 = {
|
||||||
AlbumsOutlineIcon,
|
AlbumsOutlineIcon,
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
export { setupNaive } from "@/plugins/naive";
|
export { setupNaive } from '@/plugins/naive';
|
||||||
export { icons } from "@/plugins/icon";
|
export { icons } from '@/plugins/icon';
|
||||||
export { setupNaiveDiscreteApi } from "@/plugins/naiveDiscreteApi";
|
export { setupNaiveDiscreteApi } from '@/plugins/naiveDiscreteApi';
|
||||||
export { setupDirectives } from "@/plugins/directives";
|
export { setupDirectives } from '@/plugins/directives';
|
||||||
|
export * from '@/plugins/assets';
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import type { App } from "vue";
|
import type { App } from 'vue';
|
||||||
import {
|
import {
|
||||||
create,
|
create,
|
||||||
NA,
|
NA,
|
||||||
@ -100,7 +100,7 @@ import {
|
|||||||
NWatermark,
|
NWatermark,
|
||||||
NEmpty,
|
NEmpty,
|
||||||
NCollapseTransition,
|
NCollapseTransition,
|
||||||
} from "naive-ui";
|
} from 'naive-ui';
|
||||||
|
|
||||||
const naive = create({
|
const naive = create({
|
||||||
components: [
|
components: [
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
import * as NaiveUI from "naive-ui";
|
import * as NaiveUI from 'naive-ui';
|
||||||
import { computed } from "vue";
|
import { computed } from 'vue';
|
||||||
|
import { useDesignSetting } from '@/store/modules/designSetting';
|
||||||
|
import { lighten } from '@/utils';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 挂载 Naive-ui 脱离上下文的 API
|
* 挂载 Naive-ui 脱离上下文的 API
|
||||||
@ -8,29 +10,30 @@ import { computed } from "vue";
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
export function setupNaiveDiscreteApi() {
|
export function setupNaiveDiscreteApi() {
|
||||||
|
const designStore = useDesignSetting();
|
||||||
const configProviderPropsRef = computed(() => ({
|
const configProviderPropsRef = computed(() => ({
|
||||||
// theme: NaiveUI.darkTheme,
|
theme: designStore.darkTheme ? NaiveUI.darkTheme : undefined,
|
||||||
// themeOverrides: {
|
themeOverrides: {
|
||||||
// common: {
|
common: {
|
||||||
// primaryColor: designStore.appTheme,
|
primaryColor: designStore.appTheme,
|
||||||
// primaryColorHover: designStore.appTheme,
|
primaryColorHover: lighten(designStore.appTheme, 6),
|
||||||
// primaryColorPressed: designStore.appTheme,
|
primaryColorPressed: lighten(designStore.appTheme, 6),
|
||||||
// },
|
},
|
||||||
// LoadingBar: {
|
LoadingBar: {
|
||||||
// colorLoading: designStore.appTheme,
|
colorLoading: designStore.appTheme,
|
||||||
// },
|
},
|
||||||
// },
|
},
|
||||||
}));
|
}));
|
||||||
const { message, dialog, notification, loadingBar } =
|
const { message, dialog, notification, loadingBar } =
|
||||||
NaiveUI.createDiscreteApi(
|
NaiveUI.createDiscreteApi(
|
||||||
["message", "dialog", "notification", "loadingBar"],
|
['message', 'dialog', 'notification', 'loadingBar'],
|
||||||
{
|
{
|
||||||
configProviderProps: configProviderPropsRef,
|
configProviderProps: configProviderPropsRef,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
(window as any)["$message"] = message;
|
(window as any)['$message'] = message;
|
||||||
(window as any)["$dialog"] = dialog;
|
(window as any)['$dialog'] = dialog;
|
||||||
(window as any)["$notification"] = notification;
|
(window as any)['$notification'] = notification;
|
||||||
(window as any)["$loading"] = loadingBar;
|
(window as any)['$loading'] = loadingBar;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,21 +1,21 @@
|
|||||||
import { RouteRecordRaw } from "vue-router";
|
import { RouteRecordRaw } from 'vue-router';
|
||||||
import type { AppRouteRecordRaw } from "@/router/types";
|
import type { AppRouteRecordRaw } from '@/router/types';
|
||||||
import { ErrorPage404, ErrorPage403, Layout } from "@/router/constant";
|
import { ErrorPage404, ErrorPage403, Layout } from '@/router/constant';
|
||||||
import { PageEnum } from "@/enums/pageEnum";
|
import { PageEnum } from '@/enums/pageEnum';
|
||||||
// import { GoReload } from "@/components/GoReload";
|
// import { GoReload } from "@/components/GoReload";
|
||||||
|
|
||||||
export const LoginRoute: RouteRecordRaw = {
|
export const LoginRoute: RouteRecordRaw = {
|
||||||
path: PageEnum.BASE_LOGIN,
|
path: PageEnum.BASE_LOGIN,
|
||||||
name: PageEnum.BASE_LOGIN_NAME,
|
name: PageEnum.BASE_LOGIN_NAME,
|
||||||
component: () => import("@/views/login/index.vue"),
|
component: () => import('@/views/login/index.vue'),
|
||||||
meta: {
|
meta: {
|
||||||
title: "登录",
|
title: '登录',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export const HttpErrorPage: RouteRecordRaw[] = [
|
export const HttpErrorPage: RouteRecordRaw[] = [
|
||||||
{
|
{
|
||||||
path: "/error/404",
|
path: '/error/404',
|
||||||
name: PageEnum.ERROR_PAGE_NAME_404,
|
name: PageEnum.ERROR_PAGE_NAME_404,
|
||||||
component: ErrorPage404,
|
component: ErrorPage404,
|
||||||
meta: {
|
meta: {
|
||||||
@ -23,7 +23,7 @@ export const HttpErrorPage: RouteRecordRaw[] = [
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/error/403",
|
path: '/error/403',
|
||||||
name: PageEnum.ERROR_PAGE_NAME_403,
|
name: PageEnum.ERROR_PAGE_NAME_403,
|
||||||
component: ErrorPage403,
|
component: ErrorPage403,
|
||||||
meta: {
|
meta: {
|
||||||
@ -41,20 +41,20 @@ export const HttpErrorPage: RouteRecordRaw[] = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
export const ErrorPageRoute: RouteRecordRaw = {
|
export const ErrorPageRoute: RouteRecordRaw = {
|
||||||
path: "/:path(.*)*",
|
path: '/:path(.*)*',
|
||||||
name: "ErrorPage",
|
name: 'ErrorPage',
|
||||||
component: Layout,
|
component: Layout,
|
||||||
meta: {
|
meta: {
|
||||||
title: "ErrorPage",
|
title: 'ErrorPage',
|
||||||
// hideBreadcrumb: true,
|
// hideBreadcrumb: true,
|
||||||
},
|
},
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: "/:path(.*)*",
|
path: '/:path(.*)*',
|
||||||
name: "ErrorPageSon",
|
name: 'ErrorPageSon',
|
||||||
component: ErrorPage404,
|
component: ErrorPage404,
|
||||||
meta: {
|
meta: {
|
||||||
title: "ErrorPage",
|
title: 'ErrorPage',
|
||||||
// hideBreadcrumb: true,
|
// hideBreadcrumb: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -80,9 +80,9 @@ export const RedirectRoute: AppRouteRecordRaw = {
|
|||||||
},
|
},
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: "/redirect/:path(.*)",
|
path: '/redirect/:path(.*)',
|
||||||
name: PageEnum.REDIRECT_NAME,
|
name: PageEnum.REDIRECT_NAME,
|
||||||
component: () => import("@/views/redirect/index.vue"),
|
component: () => import('@/views/redirect/index.vue'),
|
||||||
meta: {
|
meta: {
|
||||||
title: PageEnum.REDIRECT_NAME,
|
title: PageEnum.REDIRECT_NAME,
|
||||||
hideBreadcrumb: true,
|
hideBreadcrumb: true,
|
||||||
|
|||||||