parent
77a71db326
commit
eab11687aa
56
.eslintrc.js
56
.eslintrc.js
|
|
@ -1,30 +1,30 @@
|
|||
module.exports = {
|
||||
env: {
|
||||
browser: true,
|
||||
es2021: true,
|
||||
node: true
|
||||
},
|
||||
globals: {
|
||||
defineProps: 'readonly',
|
||||
defineEmits: 'readonly',
|
||||
defineExpose: 'readonly'
|
||||
},
|
||||
parser: 'vue-eslint-parser',
|
||||
extends: [
|
||||
'eslint:recommended',
|
||||
'plugin:vue/vue3-essential',
|
||||
'plugin:@typescript-eslint/recommended'
|
||||
],
|
||||
parserOptions: {
|
||||
ecmaVersion: 'latest',
|
||||
parser: '@typescript-eslint/parser',
|
||||
sourceType: 'module'
|
||||
},
|
||||
plugins: ['vue', '@typescript-eslint'],
|
||||
rules: {
|
||||
'vue/multi-word-component-names': 'off',
|
||||
'@typescript-eslint/no-empty-function': 'off', // 关闭空方法检查
|
||||
'@typescript-eslint/no-explicit-any': 'off', // 关闭any类型的警告
|
||||
'vue/no-v-model-argument': 'off'
|
||||
}
|
||||
env: {
|
||||
browser: true,
|
||||
es2021: true,
|
||||
node: true
|
||||
},
|
||||
globals: {
|
||||
defineProps: 'readonly',
|
||||
defineEmits: 'readonly',
|
||||
defineExpose: 'readonly'
|
||||
},
|
||||
parser: 'vue-eslint-parser',
|
||||
extends: [
|
||||
'eslint:recommended',
|
||||
'plugin:vue/vue3-essential',
|
||||
'plugin:@typescript-eslint/recommended'
|
||||
],
|
||||
parserOptions: {
|
||||
ecmaVersion: 'latest',
|
||||
parser: '@typescript-eslint/parser',
|
||||
sourceType: 'module'
|
||||
},
|
||||
plugins: ['vue', '@typescript-eslint'],
|
||||
rules: {
|
||||
'vue/multi-word-component-names': 'off',
|
||||
'@typescript-eslint/no-empty-function': 'off', // 关闭空方法检查
|
||||
'@typescript-eslint/no-explicit-any': 'off', // 关闭any类型的警告
|
||||
'vue/no-v-model-argument': 'off'
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -2,35 +2,35 @@
|
|||
* 代码格式化配置
|
||||
*/
|
||||
module.exports = {
|
||||
// 指定每个缩进级别的空格数
|
||||
tabWidth: 2,
|
||||
// 使用制表符而不是空格缩进行
|
||||
useTabs: true,
|
||||
// 在语句末尾打印分号
|
||||
semi: true,
|
||||
// 使用单引号而不是双引号
|
||||
singleQuote: true,
|
||||
// 更改引用对象属性的时间 可选值"<as-needed|consistent|preserve>"
|
||||
quoteProps: 'as-needed',
|
||||
// 多行时尽可能打印尾随逗号。(例如,单行数组永远不会出现逗号结尾。) 可选值"<none|es5|all>",默认none
|
||||
trailingComma: 'none',
|
||||
// 在对象文字中的括号之间打印空格
|
||||
bracketSpacing: true,
|
||||
// 在单独的箭头函数参数周围包括括号 always:(x) => x \ avoid:x => x
|
||||
arrowParens: 'avoid',
|
||||
// 这两个选项可用于格式化以给定字符偏移量(分别包括和不包括)开始和结束的代码
|
||||
rangeStart: 0,
|
||||
rangeEnd: Infinity,
|
||||
// 指定要使用的解析器,不需要写文件开头的 @prettier
|
||||
requirePragma: false,
|
||||
// 不需要自动在文件开头插入 @prettier
|
||||
insertPragma: false,
|
||||
// 换行设置 always\never\preserve
|
||||
proseWrap: 'never',
|
||||
// 指定HTML文件的全局空格敏感度 css\strict\ignore
|
||||
htmlWhitespaceSensitivity: 'css',
|
||||
// Vue文件脚本和样式标签缩进
|
||||
vueIndentScriptAndStyle: false,
|
||||
// 换行符使用 lf 结尾是 可选值"<auto|lf|crlf|cr>"
|
||||
endOfLine: 'lf'
|
||||
// 指定每个缩进级别的空格数
|
||||
tabWidth: 2,
|
||||
// 使用制表符而不是空格缩进行
|
||||
useTabs: false,
|
||||
// 在语句末尾打印分号
|
||||
semi: true,
|
||||
// 使用单引号而不是双引号
|
||||
singleQuote: true,
|
||||
// 更改引用对象属性的时间 可选值"<as-needed|consistent|preserve>"
|
||||
quoteProps: 'as-needed',
|
||||
// 多行时尽可能打印尾随逗号。(例如,单行数组永远不会出现逗号结尾。) 可选值"<none|es5|all>",默认none
|
||||
trailingComma: 'none',
|
||||
// 在对象文字中的括号之间打印空格
|
||||
bracketSpacing: true,
|
||||
// 在单独的箭头函数参数周围包括括号 always:(x) => x \ avoid:x => x
|
||||
arrowParens: 'avoid',
|
||||
// 这两个选项可用于格式化以给定字符偏移量(分别包括和不包括)开始和结束的代码
|
||||
rangeStart: 0,
|
||||
rangeEnd: Infinity,
|
||||
// 指定要使用的解析器,不需要写文件开头的 @prettier
|
||||
requirePragma: false,
|
||||
// 不需要自动在文件开头插入 @prettier
|
||||
insertPragma: false,
|
||||
// 换行设置 always\never\preserve
|
||||
proseWrap: 'never',
|
||||
// 指定HTML文件的全局空格敏感度 css\strict\ignore
|
||||
htmlWhitespaceSensitivity: 'css',
|
||||
// Vue文件脚本和样式标签缩进
|
||||
vueIndentScriptAndStyle: false,
|
||||
// 换行符使用 lf 结尾是 可选值"<auto|lf|crlf|cr>"
|
||||
endOfLine: 'lf'
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,26 +1,26 @@
|
|||
module.exports = {
|
||||
// 继承的规则
|
||||
extends: ['@commitlint/config-conventional'],
|
||||
// 定义规则类型
|
||||
rules: {
|
||||
// type 类型定义,表示 git 提交的 type 必须在以下类型范围内
|
||||
'type-enum': [
|
||||
2,
|
||||
'always',
|
||||
[
|
||||
'feat', // 新功能 feature
|
||||
'fix', // 修复 bug
|
||||
'docs', // 文档注释
|
||||
'style', // 代码格式(不影响代码运行的变动)
|
||||
'refactor', // 重构(既不增加新功能,也不是修复bug)
|
||||
'perf', // 性能优化
|
||||
'test', // 增加测试
|
||||
'chore', // 构建过程或辅助工具的变动
|
||||
'revert', // 回退
|
||||
'build' // 打包
|
||||
]
|
||||
],
|
||||
// subject 大小写不做校验
|
||||
'subject-case': [0]
|
||||
}
|
||||
// 继承的规则
|
||||
extends: ['@commitlint/config-conventional'],
|
||||
// 定义规则类型
|
||||
rules: {
|
||||
// type 类型定义,表示 git 提交的 type 必须在以下类型范围内
|
||||
'type-enum': [
|
||||
2,
|
||||
'always',
|
||||
[
|
||||
'feat', // 新功能 feature
|
||||
'fix', // 修复 bug
|
||||
'docs', // 文档注释
|
||||
'style', // 代码格式(不影响代码运行的变动)
|
||||
'refactor', // 重构(既不增加新功能,也不是修复bug)
|
||||
'perf', // 性能优化
|
||||
'test', // 增加测试
|
||||
'chore', // 构建过程或辅助工具的变动
|
||||
'revert', // 回退
|
||||
'build' // 打包
|
||||
]
|
||||
],
|
||||
// subject 大小写不做校验
|
||||
'subject-case': [0]
|
||||
}
|
||||
};
|
||||
|
|
|
|||
20
index.html
20
index.html
|
|
@ -1,13 +1,13 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>vue3-element-admin</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>vue3-element-admin</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
106
package.json
106
package.json
|
|
@ -1,55 +1,55 @@
|
|||
{
|
||||
"name": "vue3-element-admin",
|
||||
"version": "1.0.0",
|
||||
"scripts": {
|
||||
"dev": "vite serve --mode development",
|
||||
"build:prod": "vue-tsc --noEmit && vite build --mode production",
|
||||
"serve": "vite preview",
|
||||
"lint": "eslint src/**/*.{ts,js,vue} --fix",
|
||||
"prepare": "husky install",
|
||||
"prettier": "prettier --write ."
|
||||
},
|
||||
"dependencies": {
|
||||
"@element-plus/icons-vue": "^1.0.0",
|
||||
"@wangeditor/editor": "^5.0.0",
|
||||
"@wangeditor/editor-for-vue": "^5.1.10",
|
||||
"axios": "^0.24.0",
|
||||
"better-scroll": "^2.4.2",
|
||||
"echarts": "^5.2.2",
|
||||
"element-plus": "^2.1.8",
|
||||
"nprogress": "^0.2.0",
|
||||
"path-browserify": "^1.0.1",
|
||||
"path-to-regexp": "^6.2.0",
|
||||
"pinia": "^2.0.12",
|
||||
"screenfull": "^6.0.0",
|
||||
"sortablejs": "^1.14.0",
|
||||
"vue": "^3.2.25",
|
||||
"vue-i18n": "^9.1.9",
|
||||
"vue-router": "^4.0.12"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@commitlint/cli": "^16.2.3",
|
||||
"@commitlint/config-conventional": "^16.2.1",
|
||||
"@types/node": "^16.11.7",
|
||||
"@types/nprogress": "^0.2.0",
|
||||
"@types/path-browserify": "^1.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.19.0",
|
||||
"@typescript-eslint/parser": "^5.19.0",
|
||||
"@vitejs/plugin-vue": "^1.9.3",
|
||||
"eslint": "^8.14.0",
|
||||
"eslint-config-prettier": "^8.5.0",
|
||||
"eslint-plugin-prettier": "^4.0.0",
|
||||
"eslint-plugin-vue": "^8.6.0",
|
||||
"fast-glob": "^3.2.11",
|
||||
"husky": "^7.0.4",
|
||||
"prettier": "^2.6.2",
|
||||
"sass": "^1.43.4",
|
||||
"typescript": "^4.5.4",
|
||||
"vite": "^2.9.7",
|
||||
"vite-plugin-svg-icons": "^2.0.1",
|
||||
"vue-tsc": "^0.34.7"
|
||||
},
|
||||
"repository": "https://gitee.com/youlaiorg/vue3-element-admin.git",
|
||||
"author": "有来开源组织",
|
||||
"license": "MIT"
|
||||
"name": "vue3-element-admin",
|
||||
"version": "1.0.0",
|
||||
"scripts": {
|
||||
"dev": "vite serve --mode development",
|
||||
"build:prod": "vue-tsc --noEmit && vite build --mode production",
|
||||
"serve": "vite preview",
|
||||
"lint": "eslint src/**/*.{ts,js,vue} --fix",
|
||||
"prepare": "husky install",
|
||||
"prettier": "prettier --write ."
|
||||
},
|
||||
"dependencies": {
|
||||
"@element-plus/icons-vue": "^1.0.0",
|
||||
"@wangeditor/editor": "^5.0.0",
|
||||
"@wangeditor/editor-for-vue": "^5.1.10",
|
||||
"axios": "^0.24.0",
|
||||
"better-scroll": "^2.4.2",
|
||||
"echarts": "^5.2.2",
|
||||
"element-plus": "^2.1.8",
|
||||
"nprogress": "^0.2.0",
|
||||
"path-browserify": "^1.0.1",
|
||||
"path-to-regexp": "^6.2.0",
|
||||
"pinia": "^2.0.12",
|
||||
"screenfull": "^6.0.0",
|
||||
"sortablejs": "^1.14.0",
|
||||
"vue": "^3.2.25",
|
||||
"vue-i18n": "^9.1.9",
|
||||
"vue-router": "^4.0.12"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@commitlint/cli": "^16.2.3",
|
||||
"@commitlint/config-conventional": "^16.2.1",
|
||||
"@types/node": "^16.11.7",
|
||||
"@types/nprogress": "^0.2.0",
|
||||
"@types/path-browserify": "^1.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.19.0",
|
||||
"@typescript-eslint/parser": "^5.19.0",
|
||||
"@vitejs/plugin-vue": "^1.9.3",
|
||||
"eslint": "^8.14.0",
|
||||
"eslint-config-prettier": "^8.5.0",
|
||||
"eslint-plugin-prettier": "^4.0.0",
|
||||
"eslint-plugin-vue": "^8.6.0",
|
||||
"husky": "^7.0.4",
|
||||
"prettier": "^2.6.2",
|
||||
"sass": "^1.43.4",
|
||||
"typescript": "^4.5.4",
|
||||
"vite": "^2.9.7",
|
||||
"fast-glob": "^3.2.11",
|
||||
"vite-plugin-svg-icons": "^2.0.1",
|
||||
"vue-tsc": "^0.34.7"
|
||||
},
|
||||
"repository": "https://gitee.com/youlaiorg/vue3-element-admin.git",
|
||||
"author": "有来开源组织",
|
||||
"license": "MIT"
|
||||
}
|
||||
|
|
|
|||
22
src/App.vue
22
src/App.vue
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<el-config-provider :locale="locale" :size="size">
|
||||
<router-view />
|
||||
</el-config-provider>
|
||||
<el-config-provider :locale="locale" :size="size">
|
||||
<router-view />
|
||||
</el-config-provider>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
|
@ -22,13 +22,13 @@ const size: any = computed(() => app.size);
|
|||
const locale = ref();
|
||||
|
||||
watch(
|
||||
language,
|
||||
value => {
|
||||
locale.value = value == 'en' ? en : zhCn;
|
||||
},
|
||||
{
|
||||
// 初始化立即执行
|
||||
immediate: true
|
||||
}
|
||||
language,
|
||||
value => {
|
||||
locale.value = value == 'en' ? en : zhCn;
|
||||
},
|
||||
{
|
||||
// 初始化立即执行
|
||||
immediate: true
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -6,11 +6,11 @@ import request from '@/utils/request';
|
|||
* @returns
|
||||
*/
|
||||
export function payOrder(data: SeataFormData) {
|
||||
return request({
|
||||
url: '/youlai-lab/api/v1/seata/order/_pay',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
return request({
|
||||
url: '/youlai-lab/api/v1/seata/order/_pay',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -18,10 +18,10 @@ export function payOrder(data: SeataFormData) {
|
|||
* @returns
|
||||
*/
|
||||
export function getSeataData() {
|
||||
return request({
|
||||
url: '/youlai-lab/api/v1/seata/data',
|
||||
method: 'get'
|
||||
});
|
||||
return request({
|
||||
url: '/youlai-lab/api/v1/seata/data',
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -29,8 +29,8 @@ export function getSeataData() {
|
|||
* @returns
|
||||
*/
|
||||
export function resetSeataData() {
|
||||
return request({
|
||||
url: '/youlai-lab/api/v1/seata/data/_reset',
|
||||
method: 'put'
|
||||
});
|
||||
return request({
|
||||
url: '/youlai-lab/api/v1/seata/data/_reset',
|
||||
method: 'put'
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,32 +7,32 @@ import { AxiosPromise } from 'axios';
|
|||
* @param data
|
||||
*/
|
||||
export function login(data: LoginFormData): AxiosPromise<LoginResponseData> {
|
||||
return request({
|
||||
url: '/youlai-auth/oauth/token',
|
||||
method: 'post',
|
||||
params: data,
|
||||
headers: {
|
||||
Authorization: 'Basic bWFsbC1hZG1pbi13ZWI6MTIzNDU2' // 客户端信息Base64明文:mall-admin-web:123456
|
||||
}
|
||||
});
|
||||
return request({
|
||||
url: '/youlai-auth/oauth/token',
|
||||
method: 'post',
|
||||
params: data,
|
||||
headers: {
|
||||
Authorization: 'Basic bWFsbC1hZG1pbi13ZWI6MTIzNDU2' // 客户端信息Base64明文:mall-admin-web:123456
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 注销
|
||||
*/
|
||||
export function logout() {
|
||||
return request({
|
||||
url: '/youlai-auth/oauth/logout',
|
||||
method: 'delete'
|
||||
});
|
||||
return request({
|
||||
url: '/youlai-auth/oauth/logout',
|
||||
method: 'delete'
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取图片验证码
|
||||
*/
|
||||
export function getCaptcha(): AxiosPromise<Captcha> {
|
||||
return request({
|
||||
url: '/captcha?t=' + new Date().getTime().toString(),
|
||||
method: 'get'
|
||||
});
|
||||
return request({
|
||||
url: '/captcha?t=' + new Date().getTime().toString(),
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,13 +8,13 @@ import { AxiosPromise } from 'axios';
|
|||
* @param queryParams
|
||||
*/
|
||||
export function listOrderPages(
|
||||
queryParams: OrderQueryParam
|
||||
queryParams: OrderQueryParam
|
||||
): AxiosPromise<OrderPageResult> {
|
||||
return request({
|
||||
url: '/mall-oms/api/v1/orders',
|
||||
method: 'get',
|
||||
params: queryParams
|
||||
});
|
||||
return request({
|
||||
url: '/mall-oms/api/v1/orders',
|
||||
method: 'get',
|
||||
params: queryParams
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -23,8 +23,8 @@ export function listOrderPages(
|
|||
* @param orderId
|
||||
*/
|
||||
export function getOrderDetail(orderId: number) {
|
||||
return request({
|
||||
url: '/mall-oms/api/v1/orders/' + orderId,
|
||||
method: 'get'
|
||||
});
|
||||
return request({
|
||||
url: '/mall-oms/api/v1/orders/' + orderId,
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,11 +6,11 @@ import request from '@/utils/request';
|
|||
* @param params
|
||||
*/
|
||||
export function listAttributes(params: object) {
|
||||
return request({
|
||||
url: '/mall-pms/api/v1/attributes',
|
||||
method: 'get',
|
||||
params: params
|
||||
});
|
||||
return request({
|
||||
url: '/mall-pms/api/v1/attributes',
|
||||
method: 'get',
|
||||
params: params
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -19,9 +19,9 @@ export function listAttributes(params: object) {
|
|||
* @param data
|
||||
*/
|
||||
export function saveAttributeBatch(data: object) {
|
||||
return request({
|
||||
url: '/mall-pms/api/v1/attributes/batch',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
return request({
|
||||
url: '/mall-pms/api/v1/attributes/batch',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import {
|
||||
BrandFormData,
|
||||
BrandItem,
|
||||
BrandPageResult,
|
||||
BrandQueryParam
|
||||
BrandFormData,
|
||||
BrandItem,
|
||||
BrandPageResult,
|
||||
BrandQueryParam
|
||||
} from '@/types';
|
||||
import request from '@/utils/request';
|
||||
import { AxiosPromise } from 'axios';
|
||||
|
|
@ -13,13 +13,13 @@ import { AxiosPromise } from 'axios';
|
|||
* @param queryParams
|
||||
*/
|
||||
export function listBrandPages(
|
||||
queryParams: BrandQueryParam
|
||||
queryParams: BrandQueryParam
|
||||
): AxiosPromise<BrandPageResult> {
|
||||
return request({
|
||||
url: '/mall-pms/api/v1/brands/page',
|
||||
method: 'get',
|
||||
params: queryParams
|
||||
});
|
||||
return request({
|
||||
url: '/mall-pms/api/v1/brands/page',
|
||||
method: 'get',
|
||||
params: queryParams
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -28,13 +28,13 @@ export function listBrandPages(
|
|||
* @param queryParams
|
||||
*/
|
||||
export function listBrands(
|
||||
queryParams?: BrandQueryParam
|
||||
queryParams?: BrandQueryParam
|
||||
): AxiosPromise<BrandItem[]> {
|
||||
return request({
|
||||
url: '/mall-pms/api/v1/brands',
|
||||
method: 'get',
|
||||
params: queryParams
|
||||
});
|
||||
return request({
|
||||
url: '/mall-pms/api/v1/brands',
|
||||
method: 'get',
|
||||
params: queryParams
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -43,10 +43,10 @@ export function listBrands(
|
|||
* @param id
|
||||
*/
|
||||
export function getBrandFormDetail(id: number): AxiosPromise<BrandFormData> {
|
||||
return request({
|
||||
url: '/mall-pms/api/v1/brands/' + id,
|
||||
method: 'get'
|
||||
});
|
||||
return request({
|
||||
url: '/mall-pms/api/v1/brands/' + id,
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -55,11 +55,11 @@ export function getBrandFormDetail(id: number): AxiosPromise<BrandFormData> {
|
|||
* @param data
|
||||
*/
|
||||
export function addBrand(data: BrandFormData) {
|
||||
return request({
|
||||
url: '/mall-pms/api/v1/brands',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
return request({
|
||||
url: '/mall-pms/api/v1/brands',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -69,11 +69,11 @@ export function addBrand(data: BrandFormData) {
|
|||
* @param data
|
||||
*/
|
||||
export function updateBrand(id: number, data: BrandFormData) {
|
||||
return request({
|
||||
url: '/mall-pms/api/v1/brands/' + id,
|
||||
method: 'put',
|
||||
data: data
|
||||
});
|
||||
return request({
|
||||
url: '/mall-pms/api/v1/brands/' + id,
|
||||
method: 'put',
|
||||
data: data
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -82,8 +82,8 @@ export function updateBrand(id: number, data: BrandFormData) {
|
|||
* @param ids
|
||||
*/
|
||||
export function deleteBrands(ids: string) {
|
||||
return request({
|
||||
url: '/mall-pms/api/v1/brands/' + ids,
|
||||
method: 'delete'
|
||||
});
|
||||
return request({
|
||||
url: '/mall-pms/api/v1/brands/' + ids,
|
||||
method: 'delete'
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,11 +6,11 @@ import request from '@/utils/request';
|
|||
* @param queryParams
|
||||
*/
|
||||
export function listCategories(queryParams: object) {
|
||||
return request({
|
||||
url: '/mall-pms/api/v1/categories',
|
||||
method: 'get',
|
||||
params: queryParams
|
||||
});
|
||||
return request({
|
||||
url: '/mall-pms/api/v1/categories',
|
||||
method: 'get',
|
||||
params: queryParams
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -19,11 +19,11 @@ export function listCategories(queryParams: object) {
|
|||
* @param queryParams
|
||||
*/
|
||||
export function listCascadeCategories(queryParams?: object) {
|
||||
return request({
|
||||
url: '/mall-pms/api/v1/categories/cascade',
|
||||
method: 'get',
|
||||
params: queryParams
|
||||
});
|
||||
return request({
|
||||
url: '/mall-pms/api/v1/categories/cascade',
|
||||
method: 'get',
|
||||
params: queryParams
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -32,10 +32,10 @@ export function listCascadeCategories(queryParams?: object) {
|
|||
* @param id
|
||||
*/
|
||||
export function getCategoryDetail(id: number) {
|
||||
return request({
|
||||
url: '/mall-pms/api/v1/categories/' + id,
|
||||
method: 'get'
|
||||
});
|
||||
return request({
|
||||
url: '/mall-pms/api/v1/categories/' + id,
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -44,11 +44,11 @@ export function getCategoryDetail(id: number) {
|
|||
* @param data
|
||||
*/
|
||||
export function addCategory(data: object) {
|
||||
return request({
|
||||
url: '/mall-pms/api/v1/categories',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
return request({
|
||||
url: '/mall-pms/api/v1/categories',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -58,11 +58,11 @@ export function addCategory(data: object) {
|
|||
* @param data
|
||||
*/
|
||||
export function updateCategory(id: number, data: object) {
|
||||
return request({
|
||||
url: '/mall-pms/api/v1/categories/' + id,
|
||||
method: 'put',
|
||||
data: data
|
||||
});
|
||||
return request({
|
||||
url: '/mall-pms/api/v1/categories/' + id,
|
||||
method: 'put',
|
||||
data: data
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -71,10 +71,10 @@ export function updateCategory(id: number, data: object) {
|
|||
* @param ids
|
||||
*/
|
||||
export function deleteCategories(ids: string) {
|
||||
return request({
|
||||
url: '/mall-pms/api/v1/categories/' + ids,
|
||||
method: 'delete'
|
||||
});
|
||||
return request({
|
||||
url: '/mall-pms/api/v1/categories/' + ids,
|
||||
method: 'delete'
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -84,9 +84,9 @@ export function deleteCategories(ids: string) {
|
|||
* @param data
|
||||
*/
|
||||
export function updateCategoryPart(id: number, data: object) {
|
||||
return request({
|
||||
url: '/mall-pms/api/v1/categories/' + id,
|
||||
method: 'patch',
|
||||
data: data
|
||||
});
|
||||
return request({
|
||||
url: '/mall-pms/api/v1/categories/' + id,
|
||||
method: 'patch',
|
||||
data: data
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,13 +8,13 @@ import { AxiosPromise } from 'axios';
|
|||
* @param queryParams
|
||||
*/
|
||||
export function listGoodsPages(
|
||||
queryParams: GoodsQueryParam
|
||||
queryParams: GoodsQueryParam
|
||||
): AxiosPromise<GoodsPageResult> {
|
||||
return request({
|
||||
url: '/mall-pms/api/v1/goods/page',
|
||||
method: 'get',
|
||||
params: queryParams
|
||||
});
|
||||
return request({
|
||||
url: '/mall-pms/api/v1/goods/page',
|
||||
method: 'get',
|
||||
params: queryParams
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -23,10 +23,10 @@ export function listGoodsPages(
|
|||
* @param id
|
||||
*/
|
||||
export function getGoodsDetail(id: string): AxiosPromise<GoodsDetail> {
|
||||
return request({
|
||||
url: '/mall-pms/api/v1/goods/' + id,
|
||||
method: 'get'
|
||||
});
|
||||
return request({
|
||||
url: '/mall-pms/api/v1/goods/' + id,
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -35,11 +35,11 @@ export function getGoodsDetail(id: string): AxiosPromise<GoodsDetail> {
|
|||
* @param data
|
||||
*/
|
||||
export function addGoods(data: object) {
|
||||
return request({
|
||||
url: '/mall-pms/api/v1/goods',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
return request({
|
||||
url: '/mall-pms/api/v1/goods',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -49,11 +49,11 @@ export function addGoods(data: object) {
|
|||
* @param data
|
||||
*/
|
||||
export function updateGoods(id: number, data: object) {
|
||||
return request({
|
||||
url: '/mall-pms/api/v1/goods/' + id,
|
||||
method: 'put',
|
||||
data: data
|
||||
});
|
||||
return request({
|
||||
url: '/mall-pms/api/v1/goods/' + id,
|
||||
method: 'put',
|
||||
data: data
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -62,8 +62,8 @@ export function updateGoods(id: number, data: object) {
|
|||
* @param ids
|
||||
*/
|
||||
export function deleteGoods(ids: string) {
|
||||
return request({
|
||||
url: '/mall-pms/api/v1/goods/' + ids,
|
||||
method: 'delete'
|
||||
});
|
||||
return request({
|
||||
url: '/mall-pms/api/v1/goods/' + ids,
|
||||
method: 'delete'
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,13 +8,13 @@ import { AxiosPromise } from 'axios';
|
|||
* @param queryParams
|
||||
*/
|
||||
export function listAdvertPages(
|
||||
queryParams: AdvertQueryParam
|
||||
queryParams: AdvertQueryParam
|
||||
): AxiosPromise<AdvertPageResult> {
|
||||
return request({
|
||||
url: '/mall-sms/api/v1/adverts',
|
||||
method: 'get',
|
||||
params: queryParams
|
||||
});
|
||||
return request({
|
||||
url: '/mall-sms/api/v1/adverts',
|
||||
method: 'get',
|
||||
params: queryParams
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -23,10 +23,10 @@ export function listAdvertPages(
|
|||
* @param id
|
||||
*/
|
||||
export function getAdvertFormDetail(id: number): AxiosPromise<AdvertFormData> {
|
||||
return request({
|
||||
url: '/mall-sms/api/v1/adverts/' + id,
|
||||
method: 'get'
|
||||
});
|
||||
return request({
|
||||
url: '/mall-sms/api/v1/adverts/' + id,
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -35,11 +35,11 @@ export function getAdvertFormDetail(id: number): AxiosPromise<AdvertFormData> {
|
|||
* @param data
|
||||
*/
|
||||
export function addAdvert(data: AdvertFormData) {
|
||||
return request({
|
||||
url: '/mall-sms/api/v1/adverts',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
return request({
|
||||
url: '/mall-sms/api/v1/adverts',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -49,11 +49,11 @@ export function addAdvert(data: AdvertFormData) {
|
|||
* @param data
|
||||
*/
|
||||
export function updateAdvert(id: number, data: AdvertFormData) {
|
||||
return request({
|
||||
url: '/mall-sms/api/v1/adverts/' + id,
|
||||
method: 'put',
|
||||
data: data
|
||||
});
|
||||
return request({
|
||||
url: '/mall-sms/api/v1/adverts/' + id,
|
||||
method: 'put',
|
||||
data: data
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -62,8 +62,8 @@ export function updateAdvert(id: number, data: AdvertFormData) {
|
|||
* @param ids
|
||||
*/
|
||||
export function deleteAdverts(ids: string) {
|
||||
return request({
|
||||
url: '/mall-sms/api/v1/adverts/' + ids,
|
||||
method: 'delete'
|
||||
});
|
||||
return request({
|
||||
url: '/mall-sms/api/v1/adverts/' + ids,
|
||||
method: 'delete'
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,55 +1,55 @@
|
|||
import {
|
||||
ClientFormData,
|
||||
ClientPageResult,
|
||||
ClientQueryParam
|
||||
ClientFormData,
|
||||
ClientPageResult,
|
||||
ClientQueryParam
|
||||
} from '@/types/api/system/client';
|
||||
import request from '@/utils/request';
|
||||
import { AxiosPromise } from 'axios';
|
||||
|
||||
export function listClientPages(
|
||||
queryParams: ClientQueryParam
|
||||
queryParams: ClientQueryParam
|
||||
): AxiosPromise<ClientPageResult> {
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/oauth-clients',
|
||||
method: 'get',
|
||||
params: queryParams
|
||||
});
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/oauth-clients',
|
||||
method: 'get',
|
||||
params: queryParams
|
||||
});
|
||||
}
|
||||
|
||||
export function getClientFormDetial(id: number): AxiosPromise<ClientFormData> {
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/oauth-clients/' + id,
|
||||
method: 'get'
|
||||
});
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/oauth-clients/' + id,
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
|
||||
export function addClient(data: ClientFormData) {
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/oauth-clients',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/oauth-clients',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
}
|
||||
|
||||
export function updateClient(id: string, data: ClientFormData) {
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/oauth-clients/' + id,
|
||||
method: 'put',
|
||||
data: data
|
||||
});
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/oauth-clients/' + id,
|
||||
method: 'put',
|
||||
data: data
|
||||
});
|
||||
}
|
||||
|
||||
export function deleteClients(ids: string) {
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/oauth-clients/' + ids,
|
||||
method: 'delete'
|
||||
});
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/oauth-clients/' + ids,
|
||||
method: 'delete'
|
||||
});
|
||||
}
|
||||
|
||||
export function updateClientPart(id: number, data: object) {
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/oauth-clients/' + id,
|
||||
method: 'patch',
|
||||
data: data
|
||||
});
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/oauth-clients/' + id,
|
||||
method: 'patch',
|
||||
data: data
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,23 +8,23 @@ import { AxiosPromise } from 'axios';
|
|||
* @param queryParams
|
||||
*/
|
||||
export function listTableDepartments(
|
||||
queryParams?: DeptQueryParam
|
||||
queryParams?: DeptQueryParam
|
||||
): AxiosPromise<DeptItem[]> {
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/depts/table',
|
||||
method: 'get',
|
||||
params: queryParams
|
||||
});
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/depts/table',
|
||||
method: 'get',
|
||||
params: queryParams
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 部门下拉列表
|
||||
*/
|
||||
export function listSelectDepartments(): AxiosPromise<Option[]> {
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/depts/select',
|
||||
method: 'get'
|
||||
});
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/depts/select',
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -33,10 +33,10 @@ export function listSelectDepartments(): AxiosPromise<Option[]> {
|
|||
* @param id
|
||||
*/
|
||||
export function getDeptDetail(id: string): AxiosPromise<DeptFormData> {
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/depts/' + id,
|
||||
method: 'get'
|
||||
});
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/depts/' + id,
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -45,11 +45,11 @@ export function getDeptDetail(id: string): AxiosPromise<DeptFormData> {
|
|||
* @param data
|
||||
*/
|
||||
export function addDept(data: DeptFormData) {
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/depts',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/depts',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -59,11 +59,11 @@ export function addDept(data: DeptFormData) {
|
|||
* @param data
|
||||
*/
|
||||
export function updateDept(id: string, data: DeptFormData) {
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/depts/' + id,
|
||||
method: 'put',
|
||||
data: data
|
||||
});
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/depts/' + id,
|
||||
method: 'put',
|
||||
data: data
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -72,8 +72,8 @@ export function updateDept(id: string, data: DeptFormData) {
|
|||
* @param ids
|
||||
*/
|
||||
export function deleteDept(ids: string) {
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/depts/' + ids,
|
||||
method: 'delete'
|
||||
});
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/depts/' + ids,
|
||||
method: 'delete'
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
import {
|
||||
DictFormData,
|
||||
DictItemFormData,
|
||||
DictItemPageResult,
|
||||
DictItemQueryParam,
|
||||
DictPageResult,
|
||||
DictQueryParam,
|
||||
Option
|
||||
DictFormData,
|
||||
DictItemFormData,
|
||||
DictItemPageResult,
|
||||
DictItemQueryParam,
|
||||
DictPageResult,
|
||||
DictQueryParam,
|
||||
Option
|
||||
} from '@/types';
|
||||
import request from '@/utils/request';
|
||||
import { AxiosPromise } from 'axios';
|
||||
|
|
@ -16,13 +16,13 @@ import { AxiosPromise } from 'axios';
|
|||
* @param queryParams
|
||||
*/
|
||||
export function listDictPages(
|
||||
queryParams: DictQueryParam
|
||||
queryParams: DictQueryParam
|
||||
): AxiosPromise<DictPageResult> {
|
||||
return request({
|
||||
url: '/youlai-admin/api/v2/dict/page',
|
||||
method: 'get',
|
||||
params: queryParams
|
||||
});
|
||||
return request({
|
||||
url: '/youlai-admin/api/v2/dict/page',
|
||||
method: 'get',
|
||||
params: queryParams
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -31,10 +31,10 @@ export function listDictPages(
|
|||
* @param id
|
||||
*/
|
||||
export function getDictFormDetail(id: number): AxiosPromise<DictFormData> {
|
||||
return request({
|
||||
url: '/youlai-admin/api/v2/dict/' + id,
|
||||
method: 'get'
|
||||
});
|
||||
return request({
|
||||
url: '/youlai-admin/api/v2/dict/' + id,
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -43,11 +43,11 @@ export function getDictFormDetail(id: number): AxiosPromise<DictFormData> {
|
|||
* @param data
|
||||
*/
|
||||
export function addDict(data: DictFormData) {
|
||||
return request({
|
||||
url: '/youlai-admin/api/v2/dict',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
return request({
|
||||
url: '/youlai-admin/api/v2/dict',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -57,11 +57,11 @@ export function addDict(data: DictFormData) {
|
|||
* @param data
|
||||
*/
|
||||
export function updateDict(id: number, data: DictFormData) {
|
||||
return request({
|
||||
url: '/youlai-admin/api/v2/dict/' + id,
|
||||
method: 'put',
|
||||
data: data
|
||||
});
|
||||
return request({
|
||||
url: '/youlai-admin/api/v2/dict/' + id,
|
||||
method: 'put',
|
||||
data: data
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -69,10 +69,10 @@ export function updateDict(id: number, data: DictFormData) {
|
|||
* @param ids 字典ID,多个以英文逗号(,)分割
|
||||
*/
|
||||
export function deleteDict(ids: string) {
|
||||
return request({
|
||||
url: '/youlai-admin/api/v2/dict/' + ids,
|
||||
method: 'delete'
|
||||
});
|
||||
return request({
|
||||
url: '/youlai-admin/api/v2/dict/' + ids,
|
||||
method: 'delete'
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -81,13 +81,13 @@ export function deleteDict(ids: string) {
|
|||
* @param queryParams
|
||||
*/
|
||||
export function listDictItemPages(
|
||||
queryParams: DictItemQueryParam
|
||||
queryParams: DictItemQueryParam
|
||||
): AxiosPromise<DictItemPageResult> {
|
||||
return request({
|
||||
url: '/youlai-admin/api/v2/dict/items/page',
|
||||
method: 'get',
|
||||
params: queryParams
|
||||
});
|
||||
return request({
|
||||
url: '/youlai-admin/api/v2/dict/items/page',
|
||||
method: 'get',
|
||||
params: queryParams
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -96,11 +96,11 @@ export function listDictItemPages(
|
|||
* @param dictCode
|
||||
*/
|
||||
export function listDictsByCode(dictCode: string): AxiosPromise<Option[]> {
|
||||
return request({
|
||||
url: '/youlai-admin/api/v2/dict/items',
|
||||
method: 'get',
|
||||
params: { dictCode: dictCode }
|
||||
});
|
||||
return request({
|
||||
url: '/youlai-admin/api/v2/dict/items',
|
||||
method: 'get',
|
||||
params: { dictCode: dictCode }
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -109,10 +109,10 @@ export function listDictsByCode(dictCode: string): AxiosPromise<Option[]> {
|
|||
* @param id
|
||||
*/
|
||||
export function getDictItemDetail(id: number): AxiosPromise<DictItemFormData> {
|
||||
return request({
|
||||
url: '/youlai-admin/api/v2/dict/items/' + id,
|
||||
method: 'get'
|
||||
});
|
||||
return request({
|
||||
url: '/youlai-admin/api/v2/dict/items/' + id,
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -121,11 +121,11 @@ export function getDictItemDetail(id: number): AxiosPromise<DictItemFormData> {
|
|||
* @param data
|
||||
*/
|
||||
export function addDictItem(data: any) {
|
||||
return request({
|
||||
url: '/youlai-admin/api/v2/dict/items',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
return request({
|
||||
url: '/youlai-admin/api/v2/dict/items',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -135,11 +135,11 @@ export function addDictItem(data: any) {
|
|||
* @param data
|
||||
*/
|
||||
export function updateDictItem(id: number, data: any) {
|
||||
return request({
|
||||
url: '/youlai-admin/api/v2/dict/items/' + id,
|
||||
method: 'put',
|
||||
data: data
|
||||
});
|
||||
return request({
|
||||
url: '/youlai-admin/api/v2/dict/items/' + id,
|
||||
method: 'put',
|
||||
data: data
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -147,8 +147,8 @@ export function updateDictItem(id: number, data: any) {
|
|||
* @param ids 字典项ID,多个以英文逗号(,)分割
|
||||
*/
|
||||
export function deleteDictItem(ids: string) {
|
||||
return request({
|
||||
url: '/youlai-admin/api/v2/dict/items/' + ids,
|
||||
method: 'delete'
|
||||
});
|
||||
return request({
|
||||
url: '/youlai-admin/api/v2/dict/items/' + ids,
|
||||
method: 'delete'
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,16 +6,16 @@ import request from '@/utils/request';
|
|||
* @param file
|
||||
*/
|
||||
export function uploadFile(file: File) {
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/files',
|
||||
method: 'post',
|
||||
data: formData,
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data'
|
||||
}
|
||||
});
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/files',
|
||||
method: 'post',
|
||||
data: formData,
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data'
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -24,9 +24,9 @@ export function uploadFile(file: File) {
|
|||
* @param path
|
||||
*/
|
||||
export function deleteFile(path?: string) {
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/files',
|
||||
method: 'delete',
|
||||
params: { path: path }
|
||||
});
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/files',
|
||||
method: 'delete',
|
||||
params: { path: path }
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,10 +6,10 @@ import { AxiosPromise } from 'axios';
|
|||
* 获取路由列表
|
||||
*/
|
||||
export function listRoutes() {
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/menus/route',
|
||||
method: 'get'
|
||||
});
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/menus/route',
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -18,23 +18,23 @@ export function listRoutes() {
|
|||
* @param queryParams
|
||||
*/
|
||||
export function listTableMenus(
|
||||
queryParams: MenuQueryParam
|
||||
queryParams: MenuQueryParam
|
||||
): AxiosPromise<MenuItem[]> {
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/menus/table',
|
||||
method: 'get',
|
||||
params: queryParams
|
||||
});
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/menus/table',
|
||||
method: 'get',
|
||||
params: queryParams
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取菜单下拉列表
|
||||
*/
|
||||
export function listSelectMenus(): AxiosPromise<Option[]> {
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/menus/select',
|
||||
method: 'get'
|
||||
});
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/menus/select',
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -42,10 +42,10 @@ export function listSelectMenus(): AxiosPromise<Option[]> {
|
|||
* @param id
|
||||
*/
|
||||
export function getMenuDetail(id: number): AxiosPromise<MenuFormData> {
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/menus/' + id,
|
||||
method: 'get'
|
||||
});
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/menus/' + id,
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -54,11 +54,11 @@ export function getMenuDetail(id: number): AxiosPromise<MenuFormData> {
|
|||
* @param data
|
||||
*/
|
||||
export function addMenu(data: MenuFormData) {
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/menus',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/menus',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -68,11 +68,11 @@ export function addMenu(data: MenuFormData) {
|
|||
* @param data
|
||||
*/
|
||||
export function updateMenu(id: string, data: MenuFormData) {
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/menus/' + id,
|
||||
method: 'put',
|
||||
data: data
|
||||
});
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/menus/' + id,
|
||||
method: 'put',
|
||||
data: data
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -81,8 +81,8 @@ export function updateMenu(id: string, data: MenuFormData) {
|
|||
* @param ids 菜单ID,多个以英文逗号(,)分割
|
||||
*/
|
||||
export function deleteMenus(ids: string) {
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/menus/' + ids,
|
||||
method: 'delete'
|
||||
});
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/menus/' + ids,
|
||||
method: 'delete'
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import {
|
||||
PermFormData,
|
||||
PermItem,
|
||||
PermPageResult,
|
||||
PermQueryParam
|
||||
PermFormData,
|
||||
PermItem,
|
||||
PermPageResult,
|
||||
PermQueryParam
|
||||
} from '@/types';
|
||||
import request from '@/utils/request';
|
||||
import { AxiosPromise } from 'axios';
|
||||
|
|
@ -13,13 +13,13 @@ import { AxiosPromise } from 'axios';
|
|||
* @param queryParams
|
||||
*/
|
||||
export function listPermPages(
|
||||
queryParams: PermQueryParam
|
||||
queryParams: PermQueryParam
|
||||
): AxiosPromise<PermPageResult> {
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/permissions/page',
|
||||
method: 'get',
|
||||
params: queryParams
|
||||
});
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/permissions/page',
|
||||
method: 'get',
|
||||
params: queryParams
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -28,13 +28,13 @@ export function listPermPages(
|
|||
* @param queryParams
|
||||
*/
|
||||
export function listPerms(
|
||||
queryParams: PermQueryParam
|
||||
queryParams: PermQueryParam
|
||||
): AxiosPromise<PermItem[]> {
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/permissions',
|
||||
method: 'get',
|
||||
params: queryParams
|
||||
});
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/permissions',
|
||||
method: 'get',
|
||||
params: queryParams
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -43,10 +43,10 @@ export function listPerms(
|
|||
* @param id
|
||||
*/
|
||||
export function getPermFormDetail(id: number): AxiosPromise<PermFormData> {
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/permissions/' + id,
|
||||
method: 'get'
|
||||
});
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/permissions/' + id,
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -55,11 +55,11 @@ export function getPermFormDetail(id: number): AxiosPromise<PermFormData> {
|
|||
* @param data
|
||||
*/
|
||||
export function addPerm(data: PermFormData) {
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/permissions',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/permissions',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -69,11 +69,11 @@ export function addPerm(data: PermFormData) {
|
|||
* @param data
|
||||
*/
|
||||
export function updatePerm(id: number, data: PermFormData) {
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/permissions/' + id,
|
||||
method: 'put',
|
||||
data: data
|
||||
});
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/permissions/' + id,
|
||||
method: 'put',
|
||||
data: data
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -82,8 +82,8 @@ export function updatePerm(id: number, data: PermFormData) {
|
|||
* @param ids
|
||||
*/
|
||||
export function deletePerms(ids: string) {
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/permissions/' + ids,
|
||||
method: 'delete'
|
||||
});
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/permissions/' + ids,
|
||||
method: 'delete'
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import {
|
||||
RoleFormData,
|
||||
RoleItem,
|
||||
RolePageResult,
|
||||
RoleQueryParam
|
||||
RoleFormData,
|
||||
RoleItem,
|
||||
RolePageResult,
|
||||
RoleQueryParam
|
||||
} from '@/types';
|
||||
import request from '@/utils/request';
|
||||
import { AxiosPromise } from 'axios';
|
||||
|
|
@ -13,13 +13,13 @@ import { AxiosPromise } from 'axios';
|
|||
* @param queryParams
|
||||
*/
|
||||
export function listRolePages(
|
||||
queryParams?: RoleQueryParam
|
||||
queryParams?: RoleQueryParam
|
||||
): AxiosPromise<RolePageResult> {
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/roles/page',
|
||||
method: 'get',
|
||||
params: queryParams
|
||||
});
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/roles/page',
|
||||
method: 'get',
|
||||
params: queryParams
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -28,13 +28,13 @@ export function listRolePages(
|
|||
* @param queryParams
|
||||
*/
|
||||
export function listRoles(
|
||||
queryParams?: RoleQueryParam
|
||||
queryParams?: RoleQueryParam
|
||||
): AxiosPromise<RoleItem[]> {
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/roles',
|
||||
method: 'get',
|
||||
params: queryParams
|
||||
});
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/roles',
|
||||
method: 'get',
|
||||
params: queryParams
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -43,10 +43,10 @@ export function listRoles(
|
|||
* @param id
|
||||
*/
|
||||
export function getRoleFormDetail(id: number): AxiosPromise<RoleFormData> {
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/roles/' + id,
|
||||
method: 'get'
|
||||
});
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/roles/' + id,
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -55,11 +55,11 @@ export function getRoleFormDetail(id: number): AxiosPromise<RoleFormData> {
|
|||
* @param data
|
||||
*/
|
||||
export function addRole(data: RoleFormData) {
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/roles',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/roles',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -69,11 +69,11 @@ export function addRole(data: RoleFormData) {
|
|||
* @param data
|
||||
*/
|
||||
export function updateRole(id: number, data: RoleFormData) {
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/roles/' + id,
|
||||
method: 'put',
|
||||
data: data
|
||||
});
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/roles/' + id,
|
||||
method: 'put',
|
||||
data: data
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -82,10 +82,10 @@ export function updateRole(id: number, data: RoleFormData) {
|
|||
* @param ids
|
||||
*/
|
||||
export function deleteRoles(ids: string) {
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/roles/' + ids,
|
||||
method: 'delete'
|
||||
});
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/roles/' + ids,
|
||||
method: 'delete'
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -94,10 +94,10 @@ export function deleteRoles(ids: string) {
|
|||
* @param roleId
|
||||
*/
|
||||
export function listRoleMenuIds(roleId: number): AxiosPromise<number[]> {
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/roles/' + roleId + '/menu_ids',
|
||||
method: 'get'
|
||||
});
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/roles/' + roleId + '/menu_ids',
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -107,11 +107,11 @@ export function listRoleMenuIds(roleId: number): AxiosPromise<number[]> {
|
|||
* @param menuIds
|
||||
*/
|
||||
export function updateRoleMenu(roleId: number, menuIds: Array<number>) {
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/roles/' + roleId + '/menus',
|
||||
method: 'put',
|
||||
data: { menuIds: menuIds }
|
||||
});
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/roles/' + roleId + '/menus',
|
||||
method: 'put',
|
||||
data: { menuIds: menuIds }
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -120,11 +120,11 @@ export function updateRoleMenu(roleId: number, menuIds: Array<number>) {
|
|||
* @param roleId
|
||||
*/
|
||||
export function listRolePerms(roleId: number, menuId: number) {
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/roles/' + roleId + '/permissions',
|
||||
method: 'get',
|
||||
params: { menuId: menuId }
|
||||
});
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/roles/' + roleId + '/permissions',
|
||||
method: 'get',
|
||||
params: { menuId: menuId }
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -135,13 +135,13 @@ export function listRolePerms(roleId: number, menuId: number) {
|
|||
* @param permIds
|
||||
*/
|
||||
export function saveRolePerms(
|
||||
roleId: number,
|
||||
menuId: number,
|
||||
permIds: Array<number>
|
||||
roleId: number,
|
||||
menuId: number,
|
||||
permIds: Array<number>
|
||||
) {
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/roles/' + roleId + '/permissions',
|
||||
method: 'put',
|
||||
data: { menuId: menuId, permIds: permIds }
|
||||
});
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/roles/' + roleId + '/permissions',
|
||||
method: 'put',
|
||||
data: { menuId: menuId, permIds: permIds }
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,20 +1,20 @@
|
|||
import request from '@/utils/request';
|
||||
import { AxiosPromise } from 'axios';
|
||||
import {
|
||||
UserFormData,
|
||||
UserInfo,
|
||||
UserPageResult,
|
||||
UserQueryParam
|
||||
UserFormData,
|
||||
UserInfo,
|
||||
UserPageResult,
|
||||
UserQueryParam
|
||||
} from '@/types';
|
||||
|
||||
/**
|
||||
* 登录成功后获取用户信息(昵称、头像、权限集合和角色集合)
|
||||
*/
|
||||
export function getUserInfo(): AxiosPromise<UserInfo> {
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/users/me',
|
||||
method: 'get'
|
||||
});
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/users/me',
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -23,13 +23,13 @@ export function getUserInfo(): AxiosPromise<UserInfo> {
|
|||
* @param queryParams
|
||||
*/
|
||||
export function listUsersPage(
|
||||
queryParams: UserQueryParam
|
||||
queryParams: UserQueryParam
|
||||
): AxiosPromise<UserPageResult> {
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/users/page',
|
||||
method: 'get',
|
||||
params: queryParams
|
||||
});
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/users/page',
|
||||
method: 'get',
|
||||
params: queryParams
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -38,10 +38,10 @@ export function listUsersPage(
|
|||
* @param userId
|
||||
*/
|
||||
export function getUserDetail(userId: number): AxiosPromise<UserFormData> {
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/users/' + userId,
|
||||
method: 'get'
|
||||
});
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/users/' + userId,
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -50,11 +50,11 @@ export function getUserDetail(userId: number): AxiosPromise<UserFormData> {
|
|||
* @param data
|
||||
*/
|
||||
export function addUser(data: any) {
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/users',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/users',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -64,11 +64,11 @@ export function addUser(data: any) {
|
|||
* @param data
|
||||
*/
|
||||
export function updateUser(id: number, data: UserFormData) {
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/users/' + id,
|
||||
method: 'put',
|
||||
data: data
|
||||
});
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/users/' + id,
|
||||
method: 'put',
|
||||
data: data
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -78,11 +78,11 @@ export function updateUser(id: number, data: UserFormData) {
|
|||
* @param data
|
||||
*/
|
||||
export function updateUserPart(id: number, data: any) {
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/users/' + id,
|
||||
method: 'patch',
|
||||
data: data
|
||||
});
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/users/' + id,
|
||||
method: 'patch',
|
||||
data: data
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -91,10 +91,10 @@ export function updateUserPart(id: number, data: any) {
|
|||
* @param ids
|
||||
*/
|
||||
export function deleteUsers(ids: string) {
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/users/' + ids,
|
||||
method: 'delete'
|
||||
});
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/users/' + ids,
|
||||
method: 'delete'
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -103,11 +103,11 @@ export function deleteUsers(ids: string) {
|
|||
* @returns
|
||||
*/
|
||||
export function downloadTemplate() {
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/users/template',
|
||||
method: 'get',
|
||||
responseType: 'arraybuffer'
|
||||
});
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/users/template',
|
||||
method: 'get',
|
||||
responseType: 'arraybuffer'
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -117,12 +117,12 @@ export function downloadTemplate() {
|
|||
* @returns
|
||||
*/
|
||||
export function exportUser(queryParams: UserQueryParam) {
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/users/_export',
|
||||
method: 'get',
|
||||
params: queryParams,
|
||||
responseType: 'arraybuffer'
|
||||
});
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/users/_export',
|
||||
method: 'get',
|
||||
params: queryParams,
|
||||
responseType: 'arraybuffer'
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -131,16 +131,16 @@ export function exportUser(queryParams: UserQueryParam) {
|
|||
* @param file
|
||||
*/
|
||||
export function importUser(deptId: number, roleIds: string, file: File) {
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
formData.append('deptId', deptId.toString());
|
||||
formData.append('roleIds', roleIds);
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/users/_import',
|
||||
method: 'post',
|
||||
data: formData,
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data'
|
||||
}
|
||||
});
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
formData.append('deptId', deptId.toString());
|
||||
formData.append('roleIds', roleIds);
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/users/_import',
|
||||
method: 'post',
|
||||
data: formData,
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data'
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,13 +8,13 @@ import { AxiosPromise } from 'axios';
|
|||
* @param queryParams
|
||||
*/
|
||||
export function listMemebersPage(
|
||||
queryParams: MemberQueryParam
|
||||
queryParams: MemberQueryParam
|
||||
): AxiosPromise<MemberPageResult> {
|
||||
return request({
|
||||
url: '/mall-ums/api/v1/members',
|
||||
method: 'get',
|
||||
params: queryParams
|
||||
});
|
||||
return request({
|
||||
url: '/mall-ums/api/v1/members',
|
||||
method: 'get',
|
||||
params: queryParams
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -23,10 +23,10 @@ export function listMemebersPage(
|
|||
* @param id
|
||||
*/
|
||||
export function getMemberDetail(id: number) {
|
||||
return request({
|
||||
url: '/mall-ums/api/v1/members/' + id,
|
||||
method: 'get'
|
||||
});
|
||||
return request({
|
||||
url: '/mall-ums/api/v1/members/' + id,
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -35,11 +35,11 @@ export function getMemberDetail(id: number) {
|
|||
* @param data
|
||||
*/
|
||||
export function addMember(data: object) {
|
||||
return request({
|
||||
url: '/mall-ums/api/v1/members',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
return request({
|
||||
url: '/mall-ums/api/v1/members',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -49,9 +49,9 @@ export function addMember(data: object) {
|
|||
* @param data
|
||||
*/
|
||||
export function updateMember(id: number, data: object) {
|
||||
return request({
|
||||
url: '/mall-ums/api/v1/members/' + id,
|
||||
method: 'put',
|
||||
data: data
|
||||
});
|
||||
return request({
|
||||
url: '/mall-ums/api/v1/members/' + id,
|
||||
method: 'put',
|
||||
data: data
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@
|
|||
import Pagination from '@/components/Pagination/index.vue';
|
||||
|
||||
declare module '@vue/runtime-core' {
|
||||
export interface GlobalComponents {
|
||||
Pagination: typeof Pagination;
|
||||
}
|
||||
export interface GlobalComponents {
|
||||
Pagination: typeof Pagination;
|
||||
}
|
||||
}
|
||||
export {};
|
||||
|
|
|
|||
|
|
@ -1,20 +1,20 @@
|
|||
<template>
|
||||
<el-breadcrumb class="app-breadcrumb" separator-class="el-icon-arrow-right">
|
||||
<transition-group name="breadcrumb">
|
||||
<el-breadcrumb-item v-for="(item, index) in breadcrumbs" :key="item.path">
|
||||
<span
|
||||
v-if="
|
||||
item.redirect === 'noredirect' || index === breadcrumbs.length - 1
|
||||
"
|
||||
class="no-redirect"
|
||||
>{{ generateTitle(item.meta.title) }}</span
|
||||
>
|
||||
<a v-else @click.prevent="handleLink(item)">
|
||||
{{ generateTitle(item.meta.title) }}
|
||||
</a>
|
||||
</el-breadcrumb-item>
|
||||
</transition-group>
|
||||
</el-breadcrumb>
|
||||
<el-breadcrumb class="app-breadcrumb" separator-class="el-icon-arrow-right">
|
||||
<transition-group name="breadcrumb">
|
||||
<el-breadcrumb-item v-for="(item, index) in breadcrumbs" :key="item.path">
|
||||
<span
|
||||
v-if="
|
||||
item.redirect === 'noredirect' || index === breadcrumbs.length - 1
|
||||
"
|
||||
class="no-redirect"
|
||||
>{{ generateTitle(item.meta.title) }}</span
|
||||
>
|
||||
<a v-else @click.prevent="handleLink(item)">
|
||||
{{ generateTitle(item.meta.title) }}
|
||||
</a>
|
||||
</el-breadcrumb-item>
|
||||
</transition-group>
|
||||
</el-breadcrumb>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
|
@ -26,82 +26,82 @@ import { generateTitle } from '@/utils/i18n';
|
|||
|
||||
const currentRoute = useRoute();
|
||||
const pathCompile = (path: string) => {
|
||||
const { params } = currentRoute;
|
||||
const toPath = compile(path);
|
||||
return toPath(params);
|
||||
const { params } = currentRoute;
|
||||
const toPath = compile(path);
|
||||
return toPath(params);
|
||||
};
|
||||
|
||||
const breadcrumbs = ref([] as Array<RouteLocationMatched>);
|
||||
|
||||
function getBreadcrumb() {
|
||||
let matched = currentRoute.matched.filter(
|
||||
item => item.meta && item.meta.title
|
||||
);
|
||||
const first = matched[0];
|
||||
if (!isDashboard(first)) {
|
||||
matched = [
|
||||
{ path: '/dashboard', meta: { title: 'dashboard' } } as any
|
||||
].concat(matched);
|
||||
}
|
||||
breadcrumbs.value = matched.filter(item => {
|
||||
return item.meta && item.meta.title && item.meta.breadcrumb !== false;
|
||||
});
|
||||
let matched = currentRoute.matched.filter(
|
||||
item => item.meta && item.meta.title
|
||||
);
|
||||
const first = matched[0];
|
||||
if (!isDashboard(first)) {
|
||||
matched = [
|
||||
{ path: '/dashboard', meta: { title: 'dashboard' } } as any
|
||||
].concat(matched);
|
||||
}
|
||||
breadcrumbs.value = matched.filter(item => {
|
||||
return item.meta && item.meta.title && item.meta.breadcrumb !== false;
|
||||
});
|
||||
}
|
||||
|
||||
function isDashboard(route: RouteLocationMatched) {
|
||||
const name = route && route.name;
|
||||
if (!name) {
|
||||
return false;
|
||||
}
|
||||
return (
|
||||
name.toString().trim().toLocaleLowerCase() ===
|
||||
'Dashboard'.toLocaleLowerCase()
|
||||
);
|
||||
const name = route && route.name;
|
||||
if (!name) {
|
||||
return false;
|
||||
}
|
||||
return (
|
||||
name.toString().trim().toLocaleLowerCase() ===
|
||||
'Dashboard'.toLocaleLowerCase()
|
||||
);
|
||||
}
|
||||
|
||||
function handleLink(item: any) {
|
||||
const { redirect, path } = item;
|
||||
if (redirect) {
|
||||
router.push(redirect).catch(err => {
|
||||
console.warn(err);
|
||||
});
|
||||
return;
|
||||
}
|
||||
router.push(pathCompile(path)).catch(err => {
|
||||
console.warn(err);
|
||||
});
|
||||
const { redirect, path } = item;
|
||||
if (redirect) {
|
||||
router.push(redirect).catch(err => {
|
||||
console.warn(err);
|
||||
});
|
||||
return;
|
||||
}
|
||||
router.push(pathCompile(path)).catch(err => {
|
||||
console.warn(err);
|
||||
});
|
||||
}
|
||||
|
||||
watch(
|
||||
() => currentRoute.path,
|
||||
path => {
|
||||
if (path.startsWith('/redirect/')) {
|
||||
return;
|
||||
}
|
||||
getBreadcrumb();
|
||||
}
|
||||
() => currentRoute.path,
|
||||
path => {
|
||||
if (path.startsWith('/redirect/')) {
|
||||
return;
|
||||
}
|
||||
getBreadcrumb();
|
||||
}
|
||||
);
|
||||
|
||||
onBeforeMount(() => {
|
||||
getBreadcrumb();
|
||||
getBreadcrumb();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.el-breadcrumb__inner,
|
||||
.el-breadcrumb__inner a {
|
||||
font-weight: 400 !important;
|
||||
font-weight: 400 !important;
|
||||
}
|
||||
|
||||
.app-breadcrumb.el-breadcrumb {
|
||||
display: inline-block;
|
||||
font-size: 14px;
|
||||
line-height: 50px;
|
||||
margin-left: 8px;
|
||||
display: inline-block;
|
||||
font-size: 14px;
|
||||
line-height: 50px;
|
||||
margin-left: 8px;
|
||||
|
||||
.no-redirect {
|
||||
color: #97a8be;
|
||||
cursor: text;
|
||||
}
|
||||
.no-redirect {
|
||||
color: #97a8be;
|
||||
cursor: text;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -1,59 +1,59 @@
|
|||
<template>
|
||||
<a
|
||||
href="https://github.com/hxrui"
|
||||
target="_blank"
|
||||
class="github-corner"
|
||||
aria-label="View source on Github"
|
||||
>
|
||||
<svg
|
||||
width="80"
|
||||
height="80"
|
||||
viewBox="0 0 250 250"
|
||||
style="fill: #40c9c6; color: #fff"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z" />
|
||||
<path
|
||||
d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2"
|
||||
fill="currentColor"
|
||||
style="transform-origin: 130px 106px"
|
||||
class="octo-arm"
|
||||
/>
|
||||
<path
|
||||
d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z"
|
||||
fill="currentColor"
|
||||
class="octo-body"
|
||||
/>
|
||||
</svg>
|
||||
</a>
|
||||
<a
|
||||
href="https://github.com/hxrui"
|
||||
target="_blank"
|
||||
class="github-corner"
|
||||
aria-label="View source on Github"
|
||||
>
|
||||
<svg
|
||||
width="80"
|
||||
height="80"
|
||||
viewBox="0 0 250 250"
|
||||
style="fill: #40c9c6; color: #fff"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z" />
|
||||
<path
|
||||
d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2"
|
||||
fill="currentColor"
|
||||
style="transform-origin: 130px 106px"
|
||||
class="octo-arm"
|
||||
/>
|
||||
<path
|
||||
d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z"
|
||||
fill="currentColor"
|
||||
class="octo-body"
|
||||
/>
|
||||
</svg>
|
||||
</a>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.github-corner:hover .octo-arm {
|
||||
animation: octocat-wave 560ms ease-in-out;
|
||||
animation: octocat-wave 560ms ease-in-out;
|
||||
}
|
||||
|
||||
@keyframes octocat-wave {
|
||||
0%,
|
||||
100% {
|
||||
transform: rotate(0);
|
||||
}
|
||||
20%,
|
||||
60% {
|
||||
transform: rotate(-25deg);
|
||||
}
|
||||
40%,
|
||||
80% {
|
||||
transform: rotate(10deg);
|
||||
}
|
||||
0%,
|
||||
100% {
|
||||
transform: rotate(0);
|
||||
}
|
||||
20%,
|
||||
60% {
|
||||
transform: rotate(-25deg);
|
||||
}
|
||||
40%,
|
||||
80% {
|
||||
transform: rotate(10deg);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 500px) {
|
||||
.github-corner:hover .octo-arm {
|
||||
animation: none;
|
||||
}
|
||||
.github-corner .octo-arm {
|
||||
animation: octocat-wave 560ms ease-in-out;
|
||||
}
|
||||
.github-corner:hover .octo-arm {
|
||||
animation: none;
|
||||
}
|
||||
.github-corner .octo-arm {
|
||||
animation: octocat-wave 560ms ease-in-out;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -1,46 +1,46 @@
|
|||
<template>
|
||||
<div style="padding: 0 15px" @click="toggleClick">
|
||||
<svg
|
||||
:class="{ 'is-active': isActive }"
|
||||
class="hamburger"
|
||||
viewBox="0 0 1024 1024"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="64"
|
||||
height="64"
|
||||
>
|
||||
<path
|
||||
d="M408 442h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm-8 204c0 4.4 3.6 8 8 8h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56zm504-486H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 632H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM142.4 642.1L298.7 519a8.84 8.84 0 0 0 0-13.9L142.4 381.9c-5.8-4.6-14.4-.5-14.4 6.9v246.3a8.9 8.9 0 0 0 14.4 7z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div style="padding: 0 15px" @click="toggleClick">
|
||||
<svg
|
||||
:class="{ 'is-active': isActive }"
|
||||
class="hamburger"
|
||||
viewBox="0 0 1024 1024"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="64"
|
||||
height="64"
|
||||
>
|
||||
<path
|
||||
d="M408 442h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm-8 204c0 4.4 3.6 8 8 8h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56zm504-486H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 632H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM142.4 642.1L298.7 519a8.84 8.84 0 0 0 0-13.9L142.4 381.9c-5.8-4.6-14.4-.5-14.4 6.9v246.3a8.9 8.9 0 0 0 14.4 7z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'Hamburger',
|
||||
props: {
|
||||
isActive: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
toggleClick() {
|
||||
this.$emit('toggleClick');
|
||||
}
|
||||
}
|
||||
name: 'Hamburger',
|
||||
props: {
|
||||
isActive: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
toggleClick() {
|
||||
this.$emit('toggleClick');
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.hamburger {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.hamburger.is-active {
|
||||
transform: rotate(180deg);
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -1,29 +1,29 @@
|
|||
<template>
|
||||
<div class="icon-select">
|
||||
<el-input
|
||||
v-model="iconName"
|
||||
clearable
|
||||
placeholder="请输入图标名称"
|
||||
@clear="filterIcons"
|
||||
@input="filterIcons"
|
||||
>
|
||||
<template #suffix><i class="el-icon-search el-input__icon" /></template>
|
||||
</el-input>
|
||||
<div class="icon-select__list">
|
||||
<div
|
||||
v-for="(item, index) in iconList"
|
||||
:key="index"
|
||||
@click="selectedIcon(item)"
|
||||
>
|
||||
<svg-icon
|
||||
color="#999"
|
||||
:icon-class="item"
|
||||
style="height: 30px; width: 16px; margin-right: 5px"
|
||||
/>
|
||||
<span>{{ item }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="icon-select">
|
||||
<el-input
|
||||
v-model="iconName"
|
||||
clearable
|
||||
placeholder="请输入图标名称"
|
||||
@clear="filterIcons"
|
||||
@input="filterIcons"
|
||||
>
|
||||
<template #suffix><i class="el-icon-search el-input__icon" /></template>
|
||||
</el-input>
|
||||
<div class="icon-select__list">
|
||||
<div
|
||||
v-for="(item, index) in iconList"
|
||||
:key="index"
|
||||
@click="selectedIcon(item)"
|
||||
>
|
||||
<svg-icon
|
||||
color="#999"
|
||||
:icon-class="item"
|
||||
style="height: 30px; width: 16px; margin-right: 5px"
|
||||
/>
|
||||
<span>{{ item }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
|
@ -33,8 +33,8 @@ import SvgIcon from '@/components/SvgIcon/index.vue';
|
|||
const icons = [] as string[];
|
||||
const modules = import.meta.glob('../../assets/icons/*.svg');
|
||||
for (const path in modules) {
|
||||
const p = path.split('assets/icons/')[1].split('.svg')[0];
|
||||
icons.push(p);
|
||||
const p = path.split('assets/icons/')[1].split('.svg')[0];
|
||||
icons.push(p);
|
||||
}
|
||||
const iconList = ref(icons);
|
||||
|
||||
|
|
@ -43,51 +43,51 @@ const iconName = ref('');
|
|||
const emit = defineEmits(['selected']);
|
||||
|
||||
function filterIcons() {
|
||||
iconList.value = icons;
|
||||
if (iconName.value) {
|
||||
iconList.value = icons.filter(item => item.indexOf(iconName.value) !== -1);
|
||||
}
|
||||
iconList.value = icons;
|
||||
if (iconName.value) {
|
||||
iconList.value = icons.filter(item => item.indexOf(iconName.value) !== -1);
|
||||
}
|
||||
}
|
||||
|
||||
function selectedIcon(name: string) {
|
||||
emit('selected', name);
|
||||
document.body.click();
|
||||
emit('selected', name);
|
||||
document.body.click();
|
||||
}
|
||||
|
||||
function reset() {
|
||||
iconName.value = '';
|
||||
iconList.value = icons;
|
||||
iconName.value = '';
|
||||
iconList.value = icons;
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
reset
|
||||
reset
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.icon-select {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
|
||||
&__list {
|
||||
height: 200px;
|
||||
overflow-y: scroll;
|
||||
&__list {
|
||||
height: 200px;
|
||||
overflow-y: scroll;
|
||||
|
||||
div {
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
margin-bottom: -5px;
|
||||
cursor: pointer;
|
||||
width: 33%;
|
||||
float: left;
|
||||
}
|
||||
div {
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
margin-bottom: -5px;
|
||||
cursor: pointer;
|
||||
width: 33%;
|
||||
float: left;
|
||||
}
|
||||
|
||||
span {
|
||||
display: inline-block;
|
||||
vertical-align: -0.15em;
|
||||
fill: currentColor;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
span {
|
||||
display: inline-block;
|
||||
vertical-align: -0.15em;
|
||||
fill: currentColor;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -1,19 +1,19 @@
|
|||
<template>
|
||||
<el-dropdown class="lang-select" trigger="click" @command="handleSetLanguage">
|
||||
<div class="lang-select__icon">
|
||||
<svg-icon class-name="international-icon" icon-class="language" />
|
||||
</div>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item :disabled="language === 'zh-cn'" command="zh-cn">
|
||||
中文
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item :disabled="language === 'en'" command="en">
|
||||
English
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
<el-dropdown class="lang-select" trigger="click" @command="handleSetLanguage">
|
||||
<div class="lang-select__icon">
|
||||
<svg-icon class-name="international-icon" icon-class="language" />
|
||||
</div>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item :disabled="language === 'zh-cn'" command="zh-cn">
|
||||
中文
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item :disabled="language === 'en'" command="en">
|
||||
English
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
|
@ -30,18 +30,18 @@ import SvgIcon from '@/components/SvgIcon/index.vue';
|
|||
const { locale } = useI18n();
|
||||
|
||||
function handleSetLanguage(lang: string) {
|
||||
locale.value = lang;
|
||||
app.setLanguage(lang);
|
||||
if (lang == 'en') {
|
||||
ElMessage.success('Switch Language Successful!');
|
||||
} else {
|
||||
ElMessage.success('切换语言成功!');
|
||||
}
|
||||
locale.value = lang;
|
||||
app.setLanguage(lang);
|
||||
if (lang == 'en') {
|
||||
ElMessage.success('Switch Language Successful!');
|
||||
} else {
|
||||
ElMessage.success('切换语言成功!');
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.lang-select__icon {
|
||||
line-height: 50px;
|
||||
line-height: 50px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -1,16 +1,16 @@
|
|||
<template>
|
||||
<div :class="{ hidden: hidden }" class="pagination-container">
|
||||
<el-pagination
|
||||
:background="background"
|
||||
v-model:current-page="currentPage"
|
||||
v-model:page-size="pageSize"
|
||||
:layout="layout"
|
||||
:page-sizes="pageSizes"
|
||||
:total="total"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
/>
|
||||
</div>
|
||||
<div :class="{ hidden: hidden }" class="pagination-container">
|
||||
<el-pagination
|
||||
:background="background"
|
||||
v-model:current-page="currentPage"
|
||||
v-model:page-size="pageSize"
|
||||
:layout="layout"
|
||||
:page-sizes="pageSizes"
|
||||
:total="total"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
|
@ -18,84 +18,84 @@ import { computed, PropType } from 'vue';
|
|||
import { scrollTo } from '@/utils/scroll-to';
|
||||
|
||||
const props = defineProps({
|
||||
total: {
|
||||
required: true,
|
||||
type: Number as PropType<number>,
|
||||
default: 0
|
||||
},
|
||||
page: {
|
||||
type: Number,
|
||||
default: 1
|
||||
},
|
||||
limit: {
|
||||
type: Number,
|
||||
default: 20
|
||||
},
|
||||
pageSizes: {
|
||||
type: Array as PropType<number[]>,
|
||||
default() {
|
||||
return [10, 20, 30, 50];
|
||||
}
|
||||
},
|
||||
layout: {
|
||||
type: String,
|
||||
default: 'total, sizes, prev, pager, next, jumper'
|
||||
},
|
||||
background: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
autoScroll: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
hidden: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
total: {
|
||||
required: true,
|
||||
type: Number as PropType<number>,
|
||||
default: 0
|
||||
},
|
||||
page: {
|
||||
type: Number,
|
||||
default: 1
|
||||
},
|
||||
limit: {
|
||||
type: Number,
|
||||
default: 20
|
||||
},
|
||||
pageSizes: {
|
||||
type: Array as PropType<number[]>,
|
||||
default() {
|
||||
return [10, 20, 30, 50];
|
||||
}
|
||||
},
|
||||
layout: {
|
||||
type: String,
|
||||
default: 'total, sizes, prev, pager, next, jumper'
|
||||
},
|
||||
background: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
autoScroll: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
hidden: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
});
|
||||
|
||||
const emit = defineEmits(['update:page', 'update:limit', 'pagination']);
|
||||
|
||||
const currentPage = computed<number | undefined>({
|
||||
get: () => props.page,
|
||||
set: value => {
|
||||
emit('update:page', value);
|
||||
}
|
||||
get: () => props.page,
|
||||
set: value => {
|
||||
emit('update:page', value);
|
||||
}
|
||||
});
|
||||
|
||||
const pageSize = computed<number | undefined>({
|
||||
get() {
|
||||
return props.limit;
|
||||
},
|
||||
set(val) {
|
||||
emit('update:limit', val);
|
||||
}
|
||||
get() {
|
||||
return props.limit;
|
||||
},
|
||||
set(val) {
|
||||
emit('update:limit', val);
|
||||
}
|
||||
});
|
||||
|
||||
function handleSizeChange(val: number) {
|
||||
emit('pagination', { page: currentPage, limit: val });
|
||||
if (props.autoScroll) {
|
||||
scrollTo(0, 800);
|
||||
}
|
||||
emit('pagination', { page: currentPage, limit: val });
|
||||
if (props.autoScroll) {
|
||||
scrollTo(0, 800);
|
||||
}
|
||||
}
|
||||
|
||||
function handleCurrentChange(val: number) {
|
||||
currentPage.value = val;
|
||||
emit('pagination', { page: val, limit: props.limit });
|
||||
if (props.autoScroll) {
|
||||
scrollTo(0, 800);
|
||||
}
|
||||
currentPage.value = val;
|
||||
emit('pagination', { page: val, limit: props.limit });
|
||||
if (props.autoScroll) {
|
||||
scrollTo(0, 800);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.pagination-container {
|
||||
background: #fff;
|
||||
padding: 32px 16px;
|
||||
background: #fff;
|
||||
padding: 32px 16px;
|
||||
}
|
||||
|
||||
.pagination-container.hidden {
|
||||
display: none;
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -1,26 +1,26 @@
|
|||
<template>
|
||||
<div ref="rightPanel" :class="{ show: show }" class="rightPanel-container">
|
||||
<div class="rightPanel-background" />
|
||||
<div class="rightPanel">
|
||||
<div
|
||||
class="handle-button"
|
||||
:style="{ top: buttonTop + 'px', 'background-color': theme }"
|
||||
@click="show = !show"
|
||||
>
|
||||
<Close
|
||||
style="width: 1em; height: 1em; vertical-align: middle"
|
||||
v-show="show"
|
||||
/>
|
||||
<Setting
|
||||
style="width: 1em; height: 1em; vertical-align: middle"
|
||||
v-show="!show"
|
||||
/>
|
||||
</div>
|
||||
<div class="rightPanel-items">
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div ref="rightPanel" :class="{ show: show }" class="rightPanel-container">
|
||||
<div class="rightPanel-background" />
|
||||
<div class="rightPanel">
|
||||
<div
|
||||
class="handle-button"
|
||||
:style="{ top: buttonTop + 'px', 'background-color': theme }"
|
||||
@click="show = !show"
|
||||
>
|
||||
<Close
|
||||
style="width: 1em; height: 1em; vertical-align: middle"
|
||||
v-show="show"
|
||||
/>
|
||||
<Setting
|
||||
style="width: 1em; height: 1em; vertical-align: middle"
|
||||
v-show="!show"
|
||||
/>
|
||||
</div>
|
||||
<div class="rightPanel-items">
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
|
@ -40,124 +40,124 @@ const theme = computed(() => setting.theme);
|
|||
const show = ref(false);
|
||||
|
||||
defineProps({
|
||||
buttonTop: {
|
||||
default: 250,
|
||||
type: Number
|
||||
}
|
||||
buttonTop: {
|
||||
default: 250,
|
||||
type: Number
|
||||
}
|
||||
});
|
||||
|
||||
watch(show, value => {
|
||||
if (value) {
|
||||
addEventClick();
|
||||
}
|
||||
if (value) {
|
||||
addClass(document.body, 'showRightPanel');
|
||||
} else {
|
||||
removeClass(document.body, 'showRightPanel');
|
||||
}
|
||||
if (value) {
|
||||
addEventClick();
|
||||
}
|
||||
if (value) {
|
||||
addClass(document.body, 'showRightPanel');
|
||||
} else {
|
||||
removeClass(document.body, 'showRightPanel');
|
||||
}
|
||||
});
|
||||
|
||||
function addEventClick() {
|
||||
window.addEventListener('click', closeSidebar);
|
||||
window.addEventListener('click', closeSidebar);
|
||||
}
|
||||
|
||||
function closeSidebar(evt: any) {
|
||||
// 主题选择点击不关闭
|
||||
let parent = evt.target.closest('.theme-picker-dropdown');
|
||||
if (parent) {
|
||||
return;
|
||||
}
|
||||
// 主题选择点击不关闭
|
||||
let parent = evt.target.closest('.theme-picker-dropdown');
|
||||
if (parent) {
|
||||
return;
|
||||
}
|
||||
|
||||
parent = evt.target.closest('.rightPanel');
|
||||
if (!parent) {
|
||||
show.value = false;
|
||||
window.removeEventListener('click', closeSidebar);
|
||||
}
|
||||
parent = evt.target.closest('.rightPanel');
|
||||
if (!parent) {
|
||||
show.value = false;
|
||||
window.removeEventListener('click', closeSidebar);
|
||||
}
|
||||
}
|
||||
|
||||
const rightPanel = ref(ElColorPicker);
|
||||
|
||||
function insertToBody() {
|
||||
const elx = rightPanel.value as any;
|
||||
const body = document.querySelector('body') as any;
|
||||
body.insertBefore(elx, body.firstChild);
|
||||
const elx = rightPanel.value as any;
|
||||
const body = document.querySelector('body') as any;
|
||||
body.insertBefore(elx, body.firstChild);
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
insertToBody();
|
||||
insertToBody();
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
const elx = rightPanel.value as any;
|
||||
elx.remove();
|
||||
const elx = rightPanel.value as any;
|
||||
elx.remove();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.showRightPanel {
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
width: calc(100% - 15px);
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
width: calc(100% - 15px);
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.rightPanel-background {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s cubic-bezier(0.7, 0.3, 0.1, 1);
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
z-index: -1;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s cubic-bezier(0.7, 0.3, 0.1, 1);
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.rightPanel {
|
||||
width: 100%;
|
||||
max-width: 260px;
|
||||
height: 100vh;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
box-shadow: 0px 0px 15px 0px rgba(0, 0, 0, 0.05);
|
||||
transition: all 0.25s cubic-bezier(0.7, 0.3, 0.1, 1);
|
||||
transform: translate(100%);
|
||||
background: #fff;
|
||||
z-index: 40000;
|
||||
width: 100%;
|
||||
max-width: 260px;
|
||||
height: 100vh;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
box-shadow: 0px 0px 15px 0px rgba(0, 0, 0, 0.05);
|
||||
transition: all 0.25s cubic-bezier(0.7, 0.3, 0.1, 1);
|
||||
transform: translate(100%);
|
||||
background: #fff;
|
||||
z-index: 40000;
|
||||
}
|
||||
|
||||
.show {
|
||||
transition: all 0.3s cubic-bezier(0.7, 0.3, 0.1, 1);
|
||||
transition: all 0.3s cubic-bezier(0.7, 0.3, 0.1, 1);
|
||||
|
||||
.rightPanel-background {
|
||||
z-index: 20000;
|
||||
opacity: 1;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.rightPanel-background {
|
||||
z-index: 20000;
|
||||
opacity: 1;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.rightPanel {
|
||||
transform: translate(0);
|
||||
}
|
||||
.rightPanel {
|
||||
transform: translate(0);
|
||||
}
|
||||
}
|
||||
|
||||
.handle-button {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
position: absolute;
|
||||
left: -48px;
|
||||
text-align: center;
|
||||
font-size: 24px;
|
||||
border-radius: 6px 0 0 6px !important;
|
||||
z-index: 0;
|
||||
pointer-events: auto;
|
||||
cursor: pointer;
|
||||
color: #fff;
|
||||
line-height: 48px;
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
position: absolute;
|
||||
left: -48px;
|
||||
text-align: center;
|
||||
font-size: 24px;
|
||||
border-radius: 6px 0 0 6px !important;
|
||||
z-index: 0;
|
||||
pointer-events: auto;
|
||||
cursor: pointer;
|
||||
color: #fff;
|
||||
line-height: 48px;
|
||||
|
||||
i {
|
||||
font-size: 24px;
|
||||
line-height: 48px;
|
||||
}
|
||||
i {
|
||||
font-size: 24px;
|
||||
line-height: 48px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
<template>
|
||||
<div>
|
||||
<svg-icon
|
||||
:icon-class="isFullscreen ? 'exit-fullscreen' : 'fullscreen'"
|
||||
@click="toggle"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<svg-icon
|
||||
:icon-class="isFullscreen ? 'exit-fullscreen' : 'fullscreen'"
|
||||
@click="toggle"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
|
|
|||
|
|
@ -1,21 +1,21 @@
|
|||
<template>
|
||||
<el-dropdown class="size-select" trigger="click" @command="handleSetSize">
|
||||
<div class="size-select__icon">
|
||||
<svg-icon class-name="size-icon" icon-class="size" />
|
||||
</div>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item
|
||||
v-for="item of sizeOptions"
|
||||
:key="item.value"
|
||||
:disabled="(size || 'default') == item.value"
|
||||
:command="item.value"
|
||||
>
|
||||
{{ item.label }}
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
<el-dropdown class="size-select" trigger="click" @command="handleSetSize">
|
||||
<div class="size-select__icon">
|
||||
<svg-icon class-name="size-icon" icon-class="size" />
|
||||
</div>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item
|
||||
v-for="item of sizeOptions"
|
||||
:key="item.value"
|
||||
:disabled="(size || 'default') == item.value"
|
||||
:command="item.value"
|
||||
>
|
||||
{{ item.label }}
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
|
@ -29,19 +29,19 @@ const { app } = useStore();
|
|||
const size = computed(() => app.size);
|
||||
|
||||
const sizeOptions = ref([
|
||||
{ label: '默认', value: 'default' },
|
||||
{ label: '大型', value: 'large' },
|
||||
{ label: '小型', value: 'small' }
|
||||
{ label: '默认', value: 'default' },
|
||||
{ label: '大型', value: 'large' },
|
||||
{ label: '小型', value: 'small' }
|
||||
]);
|
||||
|
||||
function handleSetSize(size: string) {
|
||||
app.setSize(size);
|
||||
ElMessage.success('切换布局大小成功');
|
||||
app.setSize(size);
|
||||
ElMessage.success('切换布局大小成功');
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.size-select__icon {
|
||||
line-height: 50px;
|
||||
line-height: 50px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -1,25 +1,25 @@
|
|||
<template>
|
||||
<svg aria-hidden="true" class="svg-icon">
|
||||
<use :xlink:href="symbolId" :fill="color" />
|
||||
</svg>
|
||||
<svg aria-hidden="true" class="svg-icon">
|
||||
<use :xlink:href="symbolId" :fill="color" />
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
|
||||
const props = defineProps({
|
||||
prefix: {
|
||||
type: String,
|
||||
default: 'icon'
|
||||
},
|
||||
iconClass: {
|
||||
type: String,
|
||||
required: false
|
||||
},
|
||||
color: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
prefix: {
|
||||
type: String,
|
||||
default: 'icon'
|
||||
},
|
||||
iconClass: {
|
||||
type: String,
|
||||
required: false
|
||||
},
|
||||
color: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
});
|
||||
|
||||
const symbolId = computed(() => `#${props.prefix}-${props.iconClass}`);
|
||||
|
|
@ -27,10 +27,10 @@ const symbolId = computed(() => `#${props.prefix}-${props.iconClass}`);
|
|||
|
||||
<style scoped>
|
||||
.svg-icon {
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
vertical-align: -0.15em;
|
||||
overflow: hidden;
|
||||
fill: currentColor;
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
vertical-align: -0.15em;
|
||||
overflow: hidden;
|
||||
fill: currentColor;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -1,19 +1,19 @@
|
|||
<template>
|
||||
<el-color-picker
|
||||
v-model="theme"
|
||||
:predefine="[
|
||||
'#409EFF',
|
||||
'#1890ff',
|
||||
'#304156',
|
||||
'#212121',
|
||||
'#11a983',
|
||||
'#13c2c2',
|
||||
'#6959CD',
|
||||
'#f5222d'
|
||||
]"
|
||||
class="theme-picker"
|
||||
popper-class="theme-picker-dropdown"
|
||||
/>
|
||||
<el-color-picker
|
||||
v-model="theme"
|
||||
:predefine="[
|
||||
'#409EFF',
|
||||
'#1890ff',
|
||||
'#304156',
|
||||
'#212121',
|
||||
'#11a983',
|
||||
'#13c2c2',
|
||||
'#6959CD',
|
||||
'#f5222d'
|
||||
]"
|
||||
class="theme-picker"
|
||||
popper-class="theme-picker-dropdown"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
|
@ -34,34 +34,34 @@ const { setting } = useStore();
|
|||
const theme = computed(() => setting.theme);
|
||||
|
||||
watch(theme, (color: string) => {
|
||||
node.style.setProperty('--el-color-primary', color);
|
||||
localStorage.set('theme', color);
|
||||
node.style.setProperty('--el-color-primary', color);
|
||||
localStorage.set('theme', color);
|
||||
|
||||
for (let i = 1; i < 10; i += 1) {
|
||||
node.style.setProperty(
|
||||
`--el-color-primary-light-${i}`,
|
||||
mix(color, mixWhite, i * 0.1)
|
||||
);
|
||||
}
|
||||
node.style.setProperty('--el-color-primary-dark', mix(color, mixBlack, 0.1));
|
||||
for (let i = 1; i < 10; i += 1) {
|
||||
node.style.setProperty(
|
||||
`--el-color-primary-light-${i}`,
|
||||
mix(color, mixWhite, i * 0.1)
|
||||
);
|
||||
}
|
||||
node.style.setProperty('--el-color-primary-dark', mix(color, mixBlack, 0.1));
|
||||
|
||||
localStorage.set('style', node.style.cssText);
|
||||
localStorage.set('style', node.style.cssText);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.theme-message,
|
||||
.theme-picker-dropdown {
|
||||
z-index: 99999 !important;
|
||||
z-index: 99999 !important;
|
||||
}
|
||||
|
||||
.theme-picker .el-color-picker__trigger {
|
||||
height: 26px !important;
|
||||
width: 26px !important;
|
||||
padding: 2px;
|
||||
height: 26px !important;
|
||||
width: 26px !important;
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
.theme-picker-dropdown .el-color-dropdown__link-btn {
|
||||
display: none;
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -1,67 +1,67 @@
|
|||
<template>
|
||||
<div>
|
||||
<!-- 上传组件 -->
|
||||
<el-upload
|
||||
ref="singleUploadRef"
|
||||
action=""
|
||||
class="single-uploader"
|
||||
:show-file-list="false"
|
||||
:before-upload="handleBeforeUpload"
|
||||
:http-request="uploadImage"
|
||||
>
|
||||
<img v-if="imgUrl" :src="imgUrl" class="single-uploader__image" />
|
||||
<div>
|
||||
<!-- 上传组件 -->
|
||||
<el-upload
|
||||
ref="singleUploadRef"
|
||||
action=""
|
||||
class="single-uploader"
|
||||
:show-file-list="false"
|
||||
:before-upload="handleBeforeUpload"
|
||||
:http-request="uploadImage"
|
||||
>
|
||||
<img v-if="imgUrl" :src="imgUrl" class="single-uploader__image" />
|
||||
|
||||
<el-icon v-else class="single-uploader__plus">
|
||||
<Plus />
|
||||
</el-icon>
|
||||
<el-icon v-else class="single-uploader__plus">
|
||||
<Plus />
|
||||
</el-icon>
|
||||
|
||||
<!-- 删除图标 -->
|
||||
<el-icon
|
||||
v-if="props.showClose && imgUrl"
|
||||
class="single-uploader__remove"
|
||||
@click.stop="handleRemove(imgUrl)"
|
||||
>
|
||||
<Close />
|
||||
</el-icon>
|
||||
</el-upload>
|
||||
</div>
|
||||
<!-- 删除图标 -->
|
||||
<el-icon
|
||||
v-if="props.showClose && imgUrl"
|
||||
class="single-uploader__remove"
|
||||
@click.stop="handleRemove(imgUrl)"
|
||||
>
|
||||
<Close />
|
||||
</el-icon>
|
||||
</el-upload>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
import { Plus, Close } from '@element-plus/icons-vue';
|
||||
import {
|
||||
ElMessage,
|
||||
ElUpload,
|
||||
UploadRawFile,
|
||||
UploadRequestOptions
|
||||
ElMessage,
|
||||
ElUpload,
|
||||
UploadRawFile,
|
||||
UploadRequestOptions
|
||||
} from 'element-plus';
|
||||
import { uploadFile, deleteFile } from '@/api/system/file';
|
||||
|
||||
const emit = defineEmits(['update:modelValue']);
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
/**
|
||||
* 是否显示右上角的删除图片按钮
|
||||
*/
|
||||
showClose: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
modelValue: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
/**
|
||||
* 是否显示右上角的删除图片按钮
|
||||
*/
|
||||
showClose: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
});
|
||||
|
||||
const imgUrl = computed<string | undefined>({
|
||||
get() {
|
||||
return props.modelValue;
|
||||
},
|
||||
set(val) {
|
||||
// imgUrl改变时触发修改父组件绑定的v-model的值
|
||||
emit('update:modelValue', val);
|
||||
}
|
||||
get() {
|
||||
return props.modelValue;
|
||||
},
|
||||
set(val) {
|
||||
// imgUrl改变时触发修改父组件绑定的v-model的值
|
||||
emit('update:modelValue', val);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
|
|
@ -70,8 +70,8 @@ const imgUrl = computed<string | undefined>({
|
|||
* @param params
|
||||
*/
|
||||
async function uploadImage(options: UploadRequestOptions): Promise<any> {
|
||||
const response = await uploadFile(options.file);
|
||||
imgUrl.value = response.data;
|
||||
const response = await uploadFile(options.file);
|
||||
imgUrl.value = response.data;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -80,60 +80,60 @@ async function uploadImage(options: UploadRequestOptions): Promise<any> {
|
|||
* @param fileUrl
|
||||
*/
|
||||
function handleRemove(fileUrl?: string) {
|
||||
if (fileUrl) {
|
||||
deleteFile(fileUrl);
|
||||
imgUrl.value = undefined; // 这里会触发imgUrl的computed的set方法
|
||||
}
|
||||
if (fileUrl) {
|
||||
deleteFile(fileUrl);
|
||||
imgUrl.value = undefined; // 这里会触发imgUrl的computed的set方法
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 在 before-upload 钩子中限制用户上传文件的格式和大小
|
||||
*/
|
||||
function handleBeforeUpload(file: UploadRawFile) {
|
||||
// const isJPG = file.type === "image/jpeg";
|
||||
const isLt2M = file.size / 1024 / 1024 < 2;
|
||||
// const isJPG = file.type === "image/jpeg";
|
||||
const isLt2M = file.size / 1024 / 1024 < 2;
|
||||
|
||||
if (!isLt2M) {
|
||||
ElMessage.warning('上传图片不能大于2M');
|
||||
}
|
||||
return true;
|
||||
if (!isLt2M) {
|
||||
ElMessage.warning('上传图片不能大于2M');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.single-uploader {
|
||||
border: 1px dashed #d9d9d9;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
transition: var(--el-transition-duration-fast);
|
||||
text-align: center;
|
||||
&:hover {
|
||||
border-color: var(--el-color-primary);
|
||||
}
|
||||
border: 1px dashed #d9d9d9;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
transition: var(--el-transition-duration-fast);
|
||||
text-align: center;
|
||||
&:hover {
|
||||
border-color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
&__image {
|
||||
width: 146px;
|
||||
height: 146px;
|
||||
display: block;
|
||||
}
|
||||
&__image {
|
||||
width: 146px;
|
||||
height: 146px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
&__plus {
|
||||
width: 146px;
|
||||
height: 157px;
|
||||
font-size: 28px;
|
||||
color: #8c939d;
|
||||
text-align: center;
|
||||
}
|
||||
&__plus {
|
||||
width: 146px;
|
||||
height: 157px;
|
||||
font-size: 28px;
|
||||
color: #8c939d;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&__remove {
|
||||
font-size: 12px;
|
||||
color: #ff4d51 !important;
|
||||
margin-top: 0px !important;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
color: #409eff;
|
||||
}
|
||||
&__remove {
|
||||
font-size: 12px;
|
||||
color: #ff4d51 !important;
|
||||
margin-top: 0px !important;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
color: #409eff;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -1,22 +1,22 @@
|
|||
<template>
|
||||
<div style="border: 1px solid #ccc">
|
||||
<!-- 工具栏 -->
|
||||
<Toolbar
|
||||
:editor="editorRef"
|
||||
:defaultConfig="toolbarConfig"
|
||||
style="border-bottom: 1px solid #ccc"
|
||||
:mode="mode"
|
||||
/>
|
||||
<!-- 编辑器 -->
|
||||
<Editor
|
||||
:defaultConfig="editorConfig"
|
||||
v-model="defaultHtml"
|
||||
@onChange="handleChange"
|
||||
style="height: 500px; overflow-y: hidden"
|
||||
:mode="mode"
|
||||
@onCreated="handleCreated"
|
||||
/>
|
||||
</div>
|
||||
<div style="border: 1px solid #ccc">
|
||||
<!-- 工具栏 -->
|
||||
<Toolbar
|
||||
:editor="editorRef"
|
||||
:defaultConfig="toolbarConfig"
|
||||
style="border-bottom: 1px solid #ccc"
|
||||
:mode="mode"
|
||||
/>
|
||||
<!-- 编辑器 -->
|
||||
<Editor
|
||||
:defaultConfig="editorConfig"
|
||||
v-model="defaultHtml"
|
||||
@onChange="handleChange"
|
||||
style="height: 500px; overflow-y: hidden"
|
||||
:mode="mode"
|
||||
@onCreated="handleCreated"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
|
@ -27,10 +27,10 @@ import { Editor, Toolbar } from '@wangeditor/editor-for-vue';
|
|||
import { uploadFile } from '@/api/system/file';
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: [String],
|
||||
default: ''
|
||||
}
|
||||
modelValue: {
|
||||
type: [String],
|
||||
default: ''
|
||||
}
|
||||
});
|
||||
|
||||
const emit = defineEmits(['update:modelValue']);
|
||||
|
|
@ -39,40 +39,40 @@ const emit = defineEmits(['update:modelValue']);
|
|||
const editorRef = shallowRef();
|
||||
|
||||
const state = reactive({
|
||||
toolbarConfig: {},
|
||||
editorConfig: {
|
||||
placeholder: '请输入内容...',
|
||||
MENU_CONF: {
|
||||
uploadImage: {
|
||||
// 自定义图片上传
|
||||
async customUpload(file: any, insertFn: any) {
|
||||
uploadFile(file).then(response => {
|
||||
const url = response.data;
|
||||
insertFn(url);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
defaultHtml: props.modelValue,
|
||||
mode: 'default'
|
||||
toolbarConfig: {},
|
||||
editorConfig: {
|
||||
placeholder: '请输入内容...',
|
||||
MENU_CONF: {
|
||||
uploadImage: {
|
||||
// 自定义图片上传
|
||||
async customUpload(file: any, insertFn: any) {
|
||||
uploadFile(file).then(response => {
|
||||
const url = response.data;
|
||||
insertFn(url);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
defaultHtml: props.modelValue,
|
||||
mode: 'default'
|
||||
});
|
||||
|
||||
const { toolbarConfig, editorConfig, defaultHtml, mode } = toRefs(state);
|
||||
|
||||
const handleCreated = (editor: any) => {
|
||||
editorRef.value = editor; // 记录 editor 实例,重要!
|
||||
editorRef.value = editor; // 记录 editor 实例,重要!
|
||||
};
|
||||
|
||||
function handleChange(editor: any) {
|
||||
emit('update:modelValue', editor.getHtml());
|
||||
emit('update:modelValue', editor.getHtml());
|
||||
}
|
||||
|
||||
// 组件销毁时,也及时销毁编辑器
|
||||
onBeforeUnmount(() => {
|
||||
const editor = editorRef.value;
|
||||
if (editor == null) return;
|
||||
editor.destroy();
|
||||
const editor = editorRef.value;
|
||||
if (editor == null) return;
|
||||
editor.destroy();
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -5,52 +5,52 @@ import { Directive, DirectiveBinding } from 'vue';
|
|||
* 按钮权限校验
|
||||
*/
|
||||
export const hasPerm: Directive = {
|
||||
mounted(el: HTMLElement, binding: DirectiveBinding) {
|
||||
// 「超级管理员」拥有所有的按钮权限
|
||||
const { user } = useStore();
|
||||
const roles = user.roles;
|
||||
if (roles.includes('ROOT')) {
|
||||
return true;
|
||||
}
|
||||
// 「其他角色」按钮权限校验
|
||||
const { value } = binding;
|
||||
if (value) {
|
||||
const requiredPerms = value; // DOM绑定需要的按钮权限标识
|
||||
mounted(el: HTMLElement, binding: DirectiveBinding) {
|
||||
// 「超级管理员」拥有所有的按钮权限
|
||||
const { user } = useStore();
|
||||
const roles = user.roles;
|
||||
if (roles.includes('ROOT')) {
|
||||
return true;
|
||||
}
|
||||
// 「其他角色」按钮权限校验
|
||||
const { value } = binding;
|
||||
if (value) {
|
||||
const requiredPerms = value; // DOM绑定需要的按钮权限标识
|
||||
|
||||
const hasPerm = user.perms?.some(perm => {
|
||||
return requiredPerms.includes(perm);
|
||||
});
|
||||
const hasPerm = user.perms?.some(perm => {
|
||||
return requiredPerms.includes(perm);
|
||||
});
|
||||
|
||||
if (!hasPerm) {
|
||||
el.parentNode && el.parentNode.removeChild(el);
|
||||
}
|
||||
} else {
|
||||
throw new Error(
|
||||
"need perms! Like v-has-perm=\"['sys:user:add','sys:user:edit']\""
|
||||
);
|
||||
}
|
||||
}
|
||||
if (!hasPerm) {
|
||||
el.parentNode && el.parentNode.removeChild(el);
|
||||
}
|
||||
} else {
|
||||
throw new Error(
|
||||
"need perms! Like v-has-perm=\"['sys:user:add','sys:user:edit']\""
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 角色权限校验
|
||||
*/
|
||||
export const hasRole: Directive = {
|
||||
mounted(el: HTMLElement, binding: DirectiveBinding) {
|
||||
const { value } = binding;
|
||||
mounted(el: HTMLElement, binding: DirectiveBinding) {
|
||||
const { value } = binding;
|
||||
|
||||
if (value) {
|
||||
const requiredRoles = value; // DOM绑定需要的角色编码
|
||||
const { user } = useStore();
|
||||
const hasRole = user.roles.some(perm => {
|
||||
return requiredRoles.includes(perm);
|
||||
});
|
||||
if (value) {
|
||||
const requiredRoles = value; // DOM绑定需要的角色编码
|
||||
const { user } = useStore();
|
||||
const hasRole = user.roles.some(perm => {
|
||||
return requiredRoles.includes(perm);
|
||||
});
|
||||
|
||||
if (!hasRole) {
|
||||
el.parentNode && el.parentNode.removeChild(el);
|
||||
}
|
||||
} else {
|
||||
throw new Error("need roles! Like v-has-role=\"['admin','test']\"");
|
||||
}
|
||||
}
|
||||
if (!hasRole) {
|
||||
el.parentNode && el.parentNode.removeChild(el);
|
||||
}
|
||||
} else {
|
||||
throw new Error("need roles! Like v-has-role=\"['admin','test']\"");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,19 +1,19 @@
|
|||
/// <reference types="vite/client" />
|
||||
|
||||
declare module '*.vue' {
|
||||
import { DefineComponent } from 'vue';
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types
|
||||
const component: DefineComponent<{}, {}, any>;
|
||||
export default component;
|
||||
import { DefineComponent } from 'vue';
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types
|
||||
const component: DefineComponent<{}, {}, any>;
|
||||
export default component;
|
||||
}
|
||||
|
||||
// 环境变量 TypeScript的智能提示
|
||||
interface ImportMetaEnv {
|
||||
VITE_APP_TITLE: string;
|
||||
VITE_APP_PORT: string;
|
||||
VITE_APP_BASE_API: string;
|
||||
VITE_APP_TITLE: string;
|
||||
VITE_APP_PORT: string;
|
||||
VITE_APP_BASE_API: string;
|
||||
}
|
||||
|
||||
interface ImportMeta {
|
||||
readonly env: ImportMetaEnv;
|
||||
readonly env: ImportMetaEnv;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,24 +1,24 @@
|
|||
export default {
|
||||
// 路由国际化
|
||||
route: {
|
||||
dashboard: 'Dashboard',
|
||||
document: 'Document'
|
||||
},
|
||||
// 登录页面国际化
|
||||
login: {
|
||||
title: 'vue3-element-admin',
|
||||
username: 'Username',
|
||||
password: 'Password',
|
||||
login: 'Login',
|
||||
code: 'Verification Code',
|
||||
copyright: '',
|
||||
icp: ''
|
||||
},
|
||||
// 导航栏国际化
|
||||
navbar: {
|
||||
dashboard: 'Dashboard',
|
||||
logout: 'Logout',
|
||||
document: 'Document',
|
||||
gitee: 'Gitee'
|
||||
}
|
||||
// 路由国际化
|
||||
route: {
|
||||
dashboard: 'Dashboard',
|
||||
document: 'Document'
|
||||
},
|
||||
// 登录页面国际化
|
||||
login: {
|
||||
title: 'vue3-element-admin',
|
||||
username: 'Username',
|
||||
password: 'Password',
|
||||
login: 'Login',
|
||||
code: 'Verification Code',
|
||||
copyright: '',
|
||||
icp: ''
|
||||
},
|
||||
// 导航栏国际化
|
||||
navbar: {
|
||||
dashboard: 'Dashboard',
|
||||
logout: 'Logout',
|
||||
document: 'Document',
|
||||
gitee: 'Gitee'
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -7,12 +7,12 @@ import enLocale from './en';
|
|||
import zhCnLocale from './zh-cn';
|
||||
|
||||
const messages = {
|
||||
'zh-cn': {
|
||||
...zhCnLocale
|
||||
},
|
||||
en: {
|
||||
...enLocale
|
||||
}
|
||||
'zh-cn': {
|
||||
...zhCnLocale
|
||||
},
|
||||
en: {
|
||||
...enLocale
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -21,25 +21,25 @@ const messages = {
|
|||
* @returns zh-cn|en ...
|
||||
*/
|
||||
export const getLanguage = () => {
|
||||
// 本地缓存获取
|
||||
let language = localStorage.get('language');
|
||||
if (language) {
|
||||
return language;
|
||||
}
|
||||
// 浏览器使用语言
|
||||
language = navigator.language.toLowerCase();
|
||||
const locales = Object.keys(messages);
|
||||
for (const locale of locales) {
|
||||
if (language.indexOf(locale) > -1) {
|
||||
return locale;
|
||||
}
|
||||
}
|
||||
return 'zh-cn';
|
||||
// 本地缓存获取
|
||||
let language = localStorage.get('language');
|
||||
if (language) {
|
||||
return language;
|
||||
}
|
||||
// 浏览器使用语言
|
||||
language = navigator.language.toLowerCase();
|
||||
const locales = Object.keys(messages);
|
||||
for (const locale of locales) {
|
||||
if (language.indexOf(locale) > -1) {
|
||||
return locale;
|
||||
}
|
||||
}
|
||||
return 'zh-cn';
|
||||
};
|
||||
|
||||
const i18n = createI18n({
|
||||
locale: getLanguage(),
|
||||
messages: messages
|
||||
locale: getLanguage(),
|
||||
messages: messages
|
||||
});
|
||||
|
||||
export default i18n;
|
||||
|
|
|
|||
|
|
@ -1,23 +1,23 @@
|
|||
export default {
|
||||
// 路由国际化
|
||||
route: {
|
||||
dashboard: '首页',
|
||||
document: '项目文档'
|
||||
},
|
||||
// 登录页面国际化
|
||||
login: {
|
||||
title: 'vue3-element-admin',
|
||||
username: '用户名',
|
||||
password: '密码',
|
||||
login: '登 录',
|
||||
code: '请输入验证码',
|
||||
copyright: '',
|
||||
icp: ''
|
||||
},
|
||||
navbar: {
|
||||
dashboard: '首页',
|
||||
logout: '注销',
|
||||
document: '项目文档',
|
||||
gitee: '码云'
|
||||
}
|
||||
// 路由国际化
|
||||
route: {
|
||||
dashboard: '首页',
|
||||
document: '项目文档'
|
||||
},
|
||||
// 登录页面国际化
|
||||
login: {
|
||||
title: 'vue3-element-admin',
|
||||
username: '用户名',
|
||||
password: '密码',
|
||||
login: '登 录',
|
||||
code: '请输入验证码',
|
||||
copyright: '',
|
||||
icp: ''
|
||||
},
|
||||
navbar: {
|
||||
dashboard: '首页',
|
||||
logout: '注销',
|
||||
document: '项目文档',
|
||||
gitee: '码云'
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
<template>
|
||||
<section class="app-main">
|
||||
<router-view v-slot="{ Component, route }">
|
||||
<transition name="router-fade" mode="out-in">
|
||||
<keep-alive :include="cachedViews">
|
||||
<component :is="Component" :key="route.fullPath" />
|
||||
</keep-alive>
|
||||
</transition>
|
||||
</router-view>
|
||||
</section>
|
||||
<section class="app-main">
|
||||
<router-view v-slot="{ Component, route }">
|
||||
<transition name="router-fade" mode="out-in">
|
||||
<keep-alive :include="cachedViews">
|
||||
<component :is="Component" :key="route.fullPath" />
|
||||
</keep-alive>
|
||||
</transition>
|
||||
</router-view>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
|
@ -21,34 +21,34 @@ const cachedViews = computed(() => tagsView.cachedViews);
|
|||
|
||||
<style lang="scss" scoped>
|
||||
.app-main {
|
||||
/* 50= navbar 50 */
|
||||
min-height: calc(100vh - 50px);
|
||||
width: 100%;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
/* 50= navbar 50 */
|
||||
min-height: calc(100vh - 50px);
|
||||
width: 100%;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.fixed-header + .app-main {
|
||||
padding-top: 50px;
|
||||
padding-top: 50px;
|
||||
}
|
||||
|
||||
.hasTagsView {
|
||||
.app-main {
|
||||
/* 84 = navbar + tags-view = 50 + 34 */
|
||||
min-height: calc(100vh - 84px);
|
||||
}
|
||||
.app-main {
|
||||
/* 84 = navbar + tags-view = 50 + 34 */
|
||||
min-height: calc(100vh - 84px);
|
||||
}
|
||||
|
||||
.fixed-header + .app-main {
|
||||
padding-top: 84px;
|
||||
}
|
||||
.fixed-header + .app-main {
|
||||
padding-top: 84px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="scss">
|
||||
// fix css style bug in open el-dialog
|
||||
.el-popup-parent--hidden {
|
||||
.fixed-header {
|
||||
padding-right: 15px;
|
||||
}
|
||||
.fixed-header {
|
||||
padding-right: 15px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -1,56 +1,56 @@
|
|||
<template>
|
||||
<div class="navbar">
|
||||
<hamburger
|
||||
id="hamburger-container"
|
||||
:is-active="sidebar.opened"
|
||||
class="hamburger-container"
|
||||
@toggleClick="toggleSideBar"
|
||||
/>
|
||||
<div class="navbar">
|
||||
<hamburger
|
||||
id="hamburger-container"
|
||||
:is-active="sidebar.opened"
|
||||
class="hamburger-container"
|
||||
@toggleClick="toggleSideBar"
|
||||
/>
|
||||
|
||||
<breadcrumb id="breadcrumb-container" class="breadcrumb-container" />
|
||||
<breadcrumb id="breadcrumb-container" class="breadcrumb-container" />
|
||||
|
||||
<div class="right-menu">
|
||||
<template v-if="device !== 'mobile'">
|
||||
<!-- <search id="header-search" class="right-menu-item" />
|
||||
<div class="right-menu">
|
||||
<template v-if="device !== 'mobile'">
|
||||
<!-- <search id="header-search" class="right-menu-item" />
|
||||
<error-log class="errLog-container right-menu-item hover-effect" />-->
|
||||
<screenfull id="screenfull" class="right-menu-item hover-effect" />
|
||||
<el-tooltip content="布局大小" effect="dark" placement="bottom">
|
||||
<size-select id="size-select" class="right-menu-item hover-effect" />
|
||||
</el-tooltip>
|
||||
<lang-select class="right-menu-item hover-effect" />
|
||||
</template>
|
||||
<screenfull id="screenfull" class="right-menu-item hover-effect" />
|
||||
<el-tooltip content="布局大小" effect="dark" placement="bottom">
|
||||
<size-select id="size-select" class="right-menu-item hover-effect" />
|
||||
</el-tooltip>
|
||||
<lang-select class="right-menu-item hover-effect" />
|
||||
</template>
|
||||
|
||||
<el-dropdown
|
||||
class="avatar-container right-menu-item hover-effect"
|
||||
trigger="click"
|
||||
>
|
||||
<div class="avatar-wrapper">
|
||||
<img :src="avatar + '?imageView2/1/w/80/h/80'" class="user-avatar" />
|
||||
<CaretBottom style="width: 0.6em; height: 0.6em; margin-left: 5px" />
|
||||
</div>
|
||||
<el-dropdown
|
||||
class="avatar-container right-menu-item hover-effect"
|
||||
trigger="click"
|
||||
>
|
||||
<div class="avatar-wrapper">
|
||||
<img :src="avatar + '?imageView2/1/w/80/h/80'" class="user-avatar" />
|
||||
<CaretBottom style="width: 0.6em; height: 0.6em; margin-left: 5px" />
|
||||
</div>
|
||||
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<router-link to="/">
|
||||
<el-dropdown-item>{{ $t('navbar.dashboard') }}</el-dropdown-item>
|
||||
</router-link>
|
||||
<a target="_blank" href="https://github.com/hxrui">
|
||||
<el-dropdown-item>Github</el-dropdown-item>
|
||||
</a>
|
||||
<a target="_blank" href="https://gitee.com/haoxr">
|
||||
<el-dropdown-item>{{ $t('navbar.gitee') }}</el-dropdown-item>
|
||||
</a>
|
||||
<a target="_blank" href="https://www.cnblogs.com/haoxianrui/">
|
||||
<el-dropdown-item>{{ $t('navbar.document') }}</el-dropdown-item>
|
||||
</a>
|
||||
<el-dropdown-item divided @click="logout">
|
||||
{{ $t('navbar.logout') }}
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
</div>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<router-link to="/">
|
||||
<el-dropdown-item>{{ $t('navbar.dashboard') }}</el-dropdown-item>
|
||||
</router-link>
|
||||
<a target="_blank" href="https://github.com/hxrui">
|
||||
<el-dropdown-item>Github</el-dropdown-item>
|
||||
</a>
|
||||
<a target="_blank" href="https://gitee.com/haoxr">
|
||||
<el-dropdown-item>{{ $t('navbar.gitee') }}</el-dropdown-item>
|
||||
</a>
|
||||
<a target="_blank" href="https://www.cnblogs.com/haoxianrui/">
|
||||
<el-dropdown-item>{{ $t('navbar.document') }}</el-dropdown-item>
|
||||
</a>
|
||||
<el-dropdown-item divided @click="logout">
|
||||
{{ $t('navbar.logout') }}
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
|
|
@ -79,102 +79,102 @@ const device = computed(() => app.device);
|
|||
const avatar = computed(() => user.avatar);
|
||||
|
||||
function toggleSideBar() {
|
||||
app.toggleSidebar();
|
||||
app.toggleSidebar();
|
||||
}
|
||||
|
||||
function logout() {
|
||||
ElMessageBox.confirm('确定注销并退出系统吗?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
user.logout().then(() => {
|
||||
router.push(`/login?redirect=${route.fullPath}`);
|
||||
});
|
||||
});
|
||||
ElMessageBox.confirm('确定注销并退出系统吗?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
user.logout().then(() => {
|
||||
router.push(`/login?redirect=${route.fullPath}`);
|
||||
});
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
ul {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.navbar {
|
||||
height: 50px;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
background: #fff;
|
||||
box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
|
||||
height: 50px;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
background: #fff;
|
||||
box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
|
||||
|
||||
.hamburger-container {
|
||||
line-height: 46px;
|
||||
height: 100%;
|
||||
float: left;
|
||||
cursor: pointer;
|
||||
transition: background 0.3s;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
.hamburger-container {
|
||||
line-height: 46px;
|
||||
height: 100%;
|
||||
float: left;
|
||||
cursor: pointer;
|
||||
transition: background 0.3s;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
|
||||
&:hover {
|
||||
background: rgba(0, 0, 0, 0.025);
|
||||
}
|
||||
}
|
||||
&:hover {
|
||||
background: rgba(0, 0, 0, 0.025);
|
||||
}
|
||||
}
|
||||
|
||||
.breadcrumb-container {
|
||||
float: left;
|
||||
}
|
||||
.breadcrumb-container {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.right-menu {
|
||||
float: right;
|
||||
height: 100%;
|
||||
line-height: 50px;
|
||||
.right-menu {
|
||||
float: right;
|
||||
height: 100%;
|
||||
line-height: 50px;
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.right-menu-item {
|
||||
display: inline-block;
|
||||
padding: 0 8px;
|
||||
height: 100%;
|
||||
font-size: 18px;
|
||||
color: #5a5e66;
|
||||
vertical-align: text-bottom;
|
||||
.right-menu-item {
|
||||
display: inline-block;
|
||||
padding: 0 8px;
|
||||
height: 100%;
|
||||
font-size: 18px;
|
||||
color: #5a5e66;
|
||||
vertical-align: text-bottom;
|
||||
|
||||
&.hover-effect {
|
||||
cursor: pointer;
|
||||
transition: background 0.3s;
|
||||
&.hover-effect {
|
||||
cursor: pointer;
|
||||
transition: background 0.3s;
|
||||
|
||||
&:hover {
|
||||
background: rgba(0, 0, 0, 0.025);
|
||||
}
|
||||
}
|
||||
}
|
||||
&:hover {
|
||||
background: rgba(0, 0, 0, 0.025);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.avatar-container {
|
||||
margin-right: 30px;
|
||||
.avatar-container {
|
||||
margin-right: 30px;
|
||||
|
||||
.avatar-wrapper {
|
||||
margin-top: 5px;
|
||||
position: relative;
|
||||
.avatar-wrapper {
|
||||
margin-top: 5px;
|
||||
position: relative;
|
||||
|
||||
.user-avatar {
|
||||
cursor: pointer;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 10px;
|
||||
}
|
||||
.user-avatar {
|
||||
cursor: pointer;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.el-icon-caret-bottom {
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
right: -20px;
|
||||
top: 25px;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.el-icon-caret-bottom {
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
right: -20px;
|
||||
top: 25px;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -1,28 +1,28 @@
|
|||
<template>
|
||||
<div class="drawer-container">
|
||||
<h3 class="drawer-title">系统布局配置</h3>
|
||||
<div class="drawer-item">
|
||||
<span>主题颜色</span>
|
||||
<div style="float: right; height: 26px; margin: -3px 8px 0 0">
|
||||
<theme-picker @change="themeChange" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="drawer-container">
|
||||
<h3 class="drawer-title">系统布局配置</h3>
|
||||
<div class="drawer-item">
|
||||
<span>主题颜色</span>
|
||||
<div style="float: right; height: 26px; margin: -3px 8px 0 0">
|
||||
<theme-picker @change="themeChange" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="drawer-item">
|
||||
<span>开启 Tags-View</span>
|
||||
<el-switch v-model="tagsView" class="drawer-switch" />
|
||||
</div>
|
||||
<div class="drawer-item">
|
||||
<span>开启 Tags-View</span>
|
||||
<el-switch v-model="tagsView" class="drawer-switch" />
|
||||
</div>
|
||||
|
||||
<div class="drawer-item">
|
||||
<span>固定 Header</span>
|
||||
<el-switch v-model="fixedHeader" class="drawer-switch" />
|
||||
</div>
|
||||
<div class="drawer-item">
|
||||
<span>固定 Header</span>
|
||||
<el-switch v-model="fixedHeader" class="drawer-switch" />
|
||||
</div>
|
||||
|
||||
<div class="drawer-item">
|
||||
<span>侧边栏 Logo</span>
|
||||
<el-switch v-model="sidebarLogo" class="drawer-switch" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="drawer-item">
|
||||
<span>侧边栏 Logo</span>
|
||||
<el-switch v-model="sidebarLogo" class="drawer-switch" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
|
@ -35,69 +35,69 @@ import useStore from '@/store';
|
|||
const { setting } = useStore();
|
||||
|
||||
const state = reactive({
|
||||
fixedHeader: setting.fixedHeader,
|
||||
tagsView: setting.tagsView,
|
||||
sidebarLogo: setting.sidebarLogo
|
||||
fixedHeader: setting.fixedHeader,
|
||||
tagsView: setting.tagsView,
|
||||
sidebarLogo: setting.sidebarLogo
|
||||
});
|
||||
|
||||
const { fixedHeader, tagsView, sidebarLogo } = toRefs(state);
|
||||
|
||||
function themeChange(val: any) {
|
||||
setting.changeSetting({ key: 'theme', value: val });
|
||||
setting.changeSetting({ key: 'theme', value: val });
|
||||
}
|
||||
|
||||
watch(
|
||||
() => state.fixedHeader,
|
||||
value => {
|
||||
setting.changeSetting({ key: 'fixedHeader', value: value });
|
||||
}
|
||||
() => state.fixedHeader,
|
||||
value => {
|
||||
setting.changeSetting({ key: 'fixedHeader', value: value });
|
||||
}
|
||||
);
|
||||
|
||||
watch(
|
||||
() => state.tagsView,
|
||||
value => {
|
||||
setting.changeSetting({ key: 'tagsView', value: value });
|
||||
}
|
||||
() => state.tagsView,
|
||||
value => {
|
||||
setting.changeSetting({ key: 'tagsView', value: value });
|
||||
}
|
||||
);
|
||||
|
||||
watch(
|
||||
() => state.sidebarLogo,
|
||||
value => {
|
||||
setting.changeSetting({ key: 'sidebarLogo', value: value });
|
||||
}
|
||||
() => state.sidebarLogo,
|
||||
value => {
|
||||
setting.changeSetting({ key: 'sidebarLogo', value: value });
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.drawer-container {
|
||||
padding: 24px;
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
word-wrap: break-word;
|
||||
padding: 24px;
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
word-wrap: break-word;
|
||||
|
||||
.drawer-title {
|
||||
margin-bottom: 12px;
|
||||
color: rgba(0, 0, 0, 0.85);
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
}
|
||||
.drawer-title {
|
||||
margin-bottom: 12px;
|
||||
color: rgba(0, 0, 0, 0.85);
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
}
|
||||
|
||||
.drawer-item {
|
||||
color: rgba(0, 0, 0, 0.65);
|
||||
font-size: 14px;
|
||||
padding: 12px 0;
|
||||
}
|
||||
.drawer-item {
|
||||
color: rgba(0, 0, 0, 0.65);
|
||||
font-size: 14px;
|
||||
padding: 12px 0;
|
||||
}
|
||||
|
||||
.drawer-switch {
|
||||
float: right;
|
||||
}
|
||||
.drawer-switch {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.job-link {
|
||||
display: block;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
.job-link {
|
||||
display: block;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
<template>
|
||||
<a v-if="isExternal(to)" :href="to" target="_blank" rel="noopener">
|
||||
<slot />
|
||||
</a>
|
||||
<div v-else @click="push">
|
||||
<slot />
|
||||
</div>
|
||||
<a v-if="isExternal(to)" :href="to" target="_blank" rel="noopener">
|
||||
<slot />
|
||||
</a>
|
||||
<div v-else @click="push">
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
|
|
@ -20,26 +20,26 @@ const sidebar = computed(() => app.sidebar);
|
|||
const device = computed(() => app.device);
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
to: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
setup(props) {
|
||||
const router = useRouter();
|
||||
const push = () => {
|
||||
if (device.value === 'mobile' && sidebar.value.opened == true) {
|
||||
app.closeSideBar(false);
|
||||
}
|
||||
router.push(props.to).catch(err => {
|
||||
console.log(err);
|
||||
});
|
||||
};
|
||||
return {
|
||||
push,
|
||||
isExternal
|
||||
};
|
||||
}
|
||||
props: {
|
||||
to: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
setup(props) {
|
||||
const router = useRouter();
|
||||
const push = () => {
|
||||
if (device.value === 'mobile' && sidebar.value.opened == true) {
|
||||
app.closeSideBar(false);
|
||||
}
|
||||
router.push(props.to).catch(err => {
|
||||
console.log(err);
|
||||
});
|
||||
};
|
||||
return {
|
||||
push,
|
||||
isExternal
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -1,35 +1,35 @@
|
|||
<template>
|
||||
<div class="sidebar-logo-container" :class="{ collapse: isCollapse }">
|
||||
<transition name="sidebarLogoFade">
|
||||
<router-link
|
||||
v-if="collapse"
|
||||
key="collapse"
|
||||
class="sidebar-logo-link"
|
||||
to="/"
|
||||
>
|
||||
<img v-if="logo" :src="logo" class="sidebar-logo" />
|
||||
<h1 v-else class="sidebar-title">{{ title }}</h1>
|
||||
</router-link>
|
||||
<router-link v-else key="expand" class="sidebar-logo-link" to="/">
|
||||
<img v-if="logo" :src="logo" class="sidebar-logo" />
|
||||
<h1 class="sidebar-title">{{ title }}</h1>
|
||||
</router-link>
|
||||
</transition>
|
||||
</div>
|
||||
<div class="sidebar-logo-container" :class="{ collapse: isCollapse }">
|
||||
<transition name="sidebarLogoFade">
|
||||
<router-link
|
||||
v-if="collapse"
|
||||
key="collapse"
|
||||
class="sidebar-logo-link"
|
||||
to="/"
|
||||
>
|
||||
<img v-if="logo" :src="logo" class="sidebar-logo" />
|
||||
<h1 v-else class="sidebar-title">{{ title }}</h1>
|
||||
</router-link>
|
||||
<router-link v-else key="expand" class="sidebar-logo-link" to="/">
|
||||
<img v-if="logo" :src="logo" class="sidebar-logo" />
|
||||
<h1 class="sidebar-title">{{ title }}</h1>
|
||||
</router-link>
|
||||
</transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, toRefs } from 'vue';
|
||||
|
||||
const props = defineProps({
|
||||
collapse: {
|
||||
type: Boolean,
|
||||
required: true
|
||||
}
|
||||
collapse: {
|
||||
type: Boolean,
|
||||
required: true
|
||||
}
|
||||
});
|
||||
|
||||
const state = reactive({
|
||||
isCollapse: props.collapse
|
||||
isCollapse: props.collapse
|
||||
});
|
||||
|
||||
const { isCollapse } = toRefs(state);
|
||||
|
|
@ -40,50 +40,50 @@ const logo = ref('https://www.youlai.tech/files/blog/logo.png');
|
|||
|
||||
<style lang="scss" scoped>
|
||||
.sidebarLogoFade-enter-active {
|
||||
transition: opacity 1.5s;
|
||||
transition: opacity 1.5s;
|
||||
}
|
||||
|
||||
.sidebarLogoFade-enter,
|
||||
.sidebarLogoFade-leave-to {
|
||||
opacity: 0;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.sidebar-logo-container {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
line-height: 50px;
|
||||
background: #2b2f3a;
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
line-height: 50px;
|
||||
background: #2b2f3a;
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
|
||||
& .sidebar-logo-link {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
& .sidebar-logo-link {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
|
||||
& .sidebar-logo {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
& .sidebar-logo {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
& .sidebar-title {
|
||||
display: inline-block;
|
||||
margin: 0;
|
||||
color: #fff;
|
||||
font-weight: 600;
|
||||
line-height: 50px;
|
||||
font-size: 14px;
|
||||
font-family: Avenir, Helvetica Neue, Arial, Helvetica, sans-serif;
|
||||
vertical-align: middle;
|
||||
margin-left: 12px;
|
||||
}
|
||||
}
|
||||
& .sidebar-title {
|
||||
display: inline-block;
|
||||
margin: 0;
|
||||
color: #fff;
|
||||
font-weight: 600;
|
||||
line-height: 50px;
|
||||
font-size: 14px;
|
||||
font-family: Avenir, Helvetica Neue, Arial, Helvetica, sans-serif;
|
||||
vertical-align: middle;
|
||||
margin-left: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
&.collapse {
|
||||
.sidebar-logo {
|
||||
margin-right: 0px;
|
||||
}
|
||||
}
|
||||
&.collapse {
|
||||
.sidebar-logo {
|
||||
margin-right: 0px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -1,49 +1,49 @@
|
|||
<template>
|
||||
<div v-if="!item.meta || !item.meta.hidden">
|
||||
<template
|
||||
v-if="
|
||||
hasOneShowingChild(item.children, item) &&
|
||||
(!onlyOneChild.children || onlyOneChild.noShowingChildren) &&
|
||||
(!item.meta || !item.meta.alwaysShow)
|
||||
"
|
||||
>
|
||||
<app-link v-if="onlyOneChild.meta" :to="resolvePath(onlyOneChild.path)">
|
||||
<el-menu-item
|
||||
:index="resolvePath(onlyOneChild.path)"
|
||||
:class="{ 'submenu-title-noDropdown': !isNest }"
|
||||
>
|
||||
<svg-icon
|
||||
v-if="onlyOneChild.meta && onlyOneChild.meta.icon"
|
||||
:icon-class="onlyOneChild.meta.icon"
|
||||
/>
|
||||
<template #title>
|
||||
{{ generateTitle(onlyOneChild.meta.title) }}
|
||||
</template>
|
||||
</el-menu-item>
|
||||
</app-link>
|
||||
</template>
|
||||
<el-sub-menu v-else :index="resolvePath(item.path)" popper-append-to-body>
|
||||
<!-- popper-append-to-body -->
|
||||
<template #title>
|
||||
<svg-icon
|
||||
v-if="item.meta && item.meta.icon"
|
||||
:icon-class="item.meta.icon"
|
||||
></svg-icon>
|
||||
<span v-if="item.meta && item.meta.title">{{
|
||||
generateTitle(item.meta.title)
|
||||
}}</span>
|
||||
</template>
|
||||
<div v-if="!item.meta || !item.meta.hidden">
|
||||
<template
|
||||
v-if="
|
||||
hasOneShowingChild(item.children, item) &&
|
||||
(!onlyOneChild.children || onlyOneChild.noShowingChildren) &&
|
||||
(!item.meta || !item.meta.alwaysShow)
|
||||
"
|
||||
>
|
||||
<app-link v-if="onlyOneChild.meta" :to="resolvePath(onlyOneChild.path)">
|
||||
<el-menu-item
|
||||
:index="resolvePath(onlyOneChild.path)"
|
||||
:class="{ 'submenu-title-noDropdown': !isNest }"
|
||||
>
|
||||
<svg-icon
|
||||
v-if="onlyOneChild.meta && onlyOneChild.meta.icon"
|
||||
:icon-class="onlyOneChild.meta.icon"
|
||||
/>
|
||||
<template #title>
|
||||
{{ generateTitle(onlyOneChild.meta.title) }}
|
||||
</template>
|
||||
</el-menu-item>
|
||||
</app-link>
|
||||
</template>
|
||||
<el-sub-menu v-else :index="resolvePath(item.path)" popper-append-to-body>
|
||||
<!-- popper-append-to-body -->
|
||||
<template #title>
|
||||
<svg-icon
|
||||
v-if="item.meta && item.meta.icon"
|
||||
:icon-class="item.meta.icon"
|
||||
></svg-icon>
|
||||
<span v-if="item.meta && item.meta.title">{{
|
||||
generateTitle(item.meta.title)
|
||||
}}</span>
|
||||
</template>
|
||||
|
||||
<sidebar-item
|
||||
v-for="child in item.children"
|
||||
:key="child.path"
|
||||
:item="child"
|
||||
:is-nest="true"
|
||||
:base-path="resolvePath(child.path)"
|
||||
class="nest-menu"
|
||||
/>
|
||||
</el-sub-menu>
|
||||
</div>
|
||||
<sidebar-item
|
||||
v-for="child in item.children"
|
||||
:key="child.path"
|
||||
:item="child"
|
||||
:is-nest="true"
|
||||
:base-path="resolvePath(child.path)"
|
||||
class="nest-menu"
|
||||
/>
|
||||
</el-sub-menu>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
|
@ -56,58 +56,58 @@ import { generateTitle } from '@/utils/i18n';
|
|||
import SvgIcon from '@/components/SvgIcon/index.vue';
|
||||
|
||||
const props = defineProps({
|
||||
item: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
isNest: {
|
||||
type: Boolean,
|
||||
required: false
|
||||
},
|
||||
basePath: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
item: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
isNest: {
|
||||
type: Boolean,
|
||||
required: false
|
||||
},
|
||||
basePath: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
});
|
||||
|
||||
const onlyOneChild = ref();
|
||||
|
||||
function hasOneShowingChild(children = [] as any, parent: any) {
|
||||
if (!children) {
|
||||
children = [];
|
||||
}
|
||||
const showingChildren = children.filter((item: any) => {
|
||||
if (item.meta && item.meta.hidden) {
|
||||
return false;
|
||||
} else {
|
||||
// Temp set(will be used if only has one showing child)
|
||||
onlyOneChild.value = item;
|
||||
return true;
|
||||
}
|
||||
});
|
||||
if (!children) {
|
||||
children = [];
|
||||
}
|
||||
const showingChildren = children.filter((item: any) => {
|
||||
if (item.meta && item.meta.hidden) {
|
||||
return false;
|
||||
} else {
|
||||
// Temp set(will be used if only has one showing child)
|
||||
onlyOneChild.value = item;
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
// When there is only one child router, the child router is displayed by default
|
||||
if (showingChildren.length === 1) {
|
||||
return true;
|
||||
}
|
||||
// When there is only one child router, the child router is displayed by default
|
||||
if (showingChildren.length === 1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Show parent if there are no child router to display
|
||||
if (showingChildren.length === 0) {
|
||||
onlyOneChild.value = { ...parent, path: '', noShowingChildren: true };
|
||||
return true;
|
||||
}
|
||||
// Show parent if there are no child router to display
|
||||
if (showingChildren.length === 0) {
|
||||
onlyOneChild.value = { ...parent, path: '', noShowingChildren: true };
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
function resolvePath(routePath: string) {
|
||||
if (isExternal(routePath)) {
|
||||
return routePath;
|
||||
}
|
||||
if (isExternal(props.basePath)) {
|
||||
return props.basePath;
|
||||
}
|
||||
return path.resolve(props.basePath, routePath);
|
||||
if (isExternal(routePath)) {
|
||||
return routePath;
|
||||
}
|
||||
if (isExternal(props.basePath)) {
|
||||
return props.basePath;
|
||||
}
|
||||
return path.resolve(props.basePath, routePath);
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,27 +1,27 @@
|
|||
<template>
|
||||
<div :class="{ 'has-logo': showLogo }">
|
||||
<logo v-if="showLogo" :collapse="isCollapse" />
|
||||
<el-scrollbar wrap-class="scrollbar-wrapper">
|
||||
<el-menu
|
||||
:default-active="activeMenu"
|
||||
:collapse="isCollapse"
|
||||
:background-color="variables.menuBg"
|
||||
:text-color="variables.menuText"
|
||||
:active-text-color="variables.menuActiveText"
|
||||
:unique-opened="false"
|
||||
:collapse-transition="false"
|
||||
mode="vertical"
|
||||
>
|
||||
<sidebar-item
|
||||
v-for="route in routes"
|
||||
:item="route"
|
||||
:key="route.path"
|
||||
:base-path="route.path"
|
||||
:is-collapse="isCollapse"
|
||||
/>
|
||||
</el-menu>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
<div :class="{ 'has-logo': showLogo }">
|
||||
<logo v-if="showLogo" :collapse="isCollapse" />
|
||||
<el-scrollbar wrap-class="scrollbar-wrapper">
|
||||
<el-menu
|
||||
:default-active="activeMenu"
|
||||
:collapse="isCollapse"
|
||||
:background-color="variables.menuBg"
|
||||
:text-color="variables.menuText"
|
||||
:active-text-color="variables.menuActiveText"
|
||||
:unique-opened="false"
|
||||
:collapse-transition="false"
|
||||
mode="vertical"
|
||||
>
|
||||
<sidebar-item
|
||||
v-for="route in routes"
|
||||
:item="route"
|
||||
:key="route.path"
|
||||
:base-path="route.path"
|
||||
:is-collapse="isCollapse"
|
||||
/>
|
||||
</el-menu>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
|
@ -41,11 +41,11 @@ const showLogo = computed(() => setting.sidebarLogo);
|
|||
const isCollapse = computed(() => !app.sidebar.opened);
|
||||
|
||||
const activeMenu = computed(() => {
|
||||
const { meta, path } = route;
|
||||
// if set path, the sidebar will highlight the path you set
|
||||
if (meta.activeMenu) {
|
||||
return meta.activeMenu as string;
|
||||
}
|
||||
return path;
|
||||
const { meta, path } = route;
|
||||
// if set path, the sidebar will highlight the path you set
|
||||
if (meta.activeMenu) {
|
||||
return meta.activeMenu as string;
|
||||
}
|
||||
return path;
|
||||
});
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -1,21 +1,21 @@
|
|||
<template>
|
||||
<el-scrollbar
|
||||
ref="scrollContainerRef"
|
||||
:vertical="false"
|
||||
class="scroll-container"
|
||||
@wheel.prevent="handleScroll"
|
||||
>
|
||||
<slot />
|
||||
</el-scrollbar>
|
||||
<el-scrollbar
|
||||
ref="scrollContainerRef"
|
||||
:vertical="false"
|
||||
class="scroll-container"
|
||||
@wheel.prevent="handleScroll"
|
||||
>
|
||||
<slot />
|
||||
</el-scrollbar>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
ref,
|
||||
computed,
|
||||
onMounted,
|
||||
onBeforeUnmount,
|
||||
getCurrentInstance
|
||||
ref,
|
||||
computed,
|
||||
onMounted,
|
||||
onBeforeUnmount,
|
||||
getCurrentInstance
|
||||
} from 'vue';
|
||||
import { TagView } from '@/types';
|
||||
import useStore from '@/store';
|
||||
|
|
@ -29,102 +29,102 @@ const visitedViews = computed(() => tagsView.visitedViews);
|
|||
|
||||
const { ctx } = getCurrentInstance() as any;
|
||||
const scrollWrapper = computed(() => {
|
||||
return (scrollContainerRef.value as any).$refs.wrap as HTMLElement;
|
||||
return (scrollContainerRef.value as any).$refs.wrap as HTMLElement;
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
//scrollWrapper.value.addEventListener('scroll', emitScroll, true);
|
||||
//scrollWrapper.value.addEventListener('scroll', emitScroll, true);
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
// scrollWrapper.value.removeEventListener('scroll', emitScroll);
|
||||
// scrollWrapper.value.removeEventListener('scroll', emitScroll);
|
||||
});
|
||||
|
||||
function handleScroll(e: WheelEvent) {
|
||||
const eventDelta = (e as any).wheelDelta || -e.deltaY * 40;
|
||||
scrollWrapper.value.scrollLeft =
|
||||
scrollWrapper.value.scrollLeft + eventDelta / 4;
|
||||
const eventDelta = (e as any).wheelDelta || -e.deltaY * 40;
|
||||
scrollWrapper.value.scrollLeft =
|
||||
scrollWrapper.value.scrollLeft + eventDelta / 4;
|
||||
}
|
||||
|
||||
function moveToTarget(currentTag: TagView) {
|
||||
const $container = ctx.$refs.scrollContainer.$el;
|
||||
const $containerWidth = $container.offsetWidth;
|
||||
const $scrollWrapper = scrollWrapper.value;
|
||||
const $container = ctx.$refs.scrollContainer.$el;
|
||||
const $containerWidth = $container.offsetWidth;
|
||||
const $scrollWrapper = scrollWrapper.value;
|
||||
|
||||
let firstTag = null;
|
||||
let lastTag = null;
|
||||
let firstTag = null;
|
||||
let lastTag = null;
|
||||
|
||||
// find first tag and last tag
|
||||
if (visitedViews.value.length > 0) {
|
||||
firstTag = visitedViews.value[0];
|
||||
lastTag = visitedViews.value[visitedViews.value.length - 1];
|
||||
}
|
||||
// find first tag and last tag
|
||||
if (visitedViews.value.length > 0) {
|
||||
firstTag = visitedViews.value[0];
|
||||
lastTag = visitedViews.value[visitedViews.value.length - 1];
|
||||
}
|
||||
|
||||
if (firstTag === currentTag) {
|
||||
$scrollWrapper.scrollLeft = 0;
|
||||
} else if (lastTag === currentTag) {
|
||||
$scrollWrapper.scrollLeft = $scrollWrapper.scrollWidth - $containerWidth;
|
||||
} else {
|
||||
const tagListDom = document.getElementsByClassName('tags-view__item');
|
||||
const currentIndex = visitedViews.value.findIndex(
|
||||
item => item === currentTag
|
||||
);
|
||||
let prevTag = null;
|
||||
let nextTag = null;
|
||||
for (const k in tagListDom) {
|
||||
if (k !== 'length' && Object.hasOwnProperty.call(tagListDom, k)) {
|
||||
if (
|
||||
(tagListDom[k] as any).dataset.path ===
|
||||
visitedViews.value[currentIndex - 1].path
|
||||
) {
|
||||
prevTag = tagListDom[k];
|
||||
}
|
||||
if (
|
||||
(tagListDom[k] as any).dataset.path ===
|
||||
visitedViews.value[currentIndex + 1].path
|
||||
) {
|
||||
nextTag = tagListDom[k];
|
||||
}
|
||||
}
|
||||
}
|
||||
if (firstTag === currentTag) {
|
||||
$scrollWrapper.scrollLeft = 0;
|
||||
} else if (lastTag === currentTag) {
|
||||
$scrollWrapper.scrollLeft = $scrollWrapper.scrollWidth - $containerWidth;
|
||||
} else {
|
||||
const tagListDom = document.getElementsByClassName('tags-view__item');
|
||||
const currentIndex = visitedViews.value.findIndex(
|
||||
item => item === currentTag
|
||||
);
|
||||
let prevTag = null;
|
||||
let nextTag = null;
|
||||
for (const k in tagListDom) {
|
||||
if (k !== 'length' && Object.hasOwnProperty.call(tagListDom, k)) {
|
||||
if (
|
||||
(tagListDom[k] as any).dataset.path ===
|
||||
visitedViews.value[currentIndex - 1].path
|
||||
) {
|
||||
prevTag = tagListDom[k];
|
||||
}
|
||||
if (
|
||||
(tagListDom[k] as any).dataset.path ===
|
||||
visitedViews.value[currentIndex + 1].path
|
||||
) {
|
||||
nextTag = tagListDom[k];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// the tag's offsetLeft after of nextTag
|
||||
const afterNextTagOffsetLeft =
|
||||
(nextTag as any).offsetLeft +
|
||||
(nextTag as any).offsetWidth +
|
||||
tagAndTagSpacing.value;
|
||||
// the tag's offsetLeft after of nextTag
|
||||
const afterNextTagOffsetLeft =
|
||||
(nextTag as any).offsetLeft +
|
||||
(nextTag as any).offsetWidth +
|
||||
tagAndTagSpacing.value;
|
||||
|
||||
// the tag's offsetLeft before of prevTag
|
||||
const beforePrevTagOffsetLeft =
|
||||
(prevTag as any).offsetLeft - tagAndTagSpacing.value;
|
||||
if (afterNextTagOffsetLeft > $scrollWrapper.scrollLeft + $containerWidth) {
|
||||
$scrollWrapper.scrollLeft = afterNextTagOffsetLeft - $containerWidth;
|
||||
} else if (beforePrevTagOffsetLeft < $scrollWrapper.scrollLeft) {
|
||||
$scrollWrapper.scrollLeft = beforePrevTagOffsetLeft;
|
||||
}
|
||||
}
|
||||
// the tag's offsetLeft before of prevTag
|
||||
const beforePrevTagOffsetLeft =
|
||||
(prevTag as any).offsetLeft - tagAndTagSpacing.value;
|
||||
if (afterNextTagOffsetLeft > $scrollWrapper.scrollLeft + $containerWidth) {
|
||||
$scrollWrapper.scrollLeft = afterNextTagOffsetLeft - $containerWidth;
|
||||
} else if (beforePrevTagOffsetLeft < $scrollWrapper.scrollLeft) {
|
||||
$scrollWrapper.scrollLeft = beforePrevTagOffsetLeft;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
moveToTarget
|
||||
moveToTarget
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.scroll-container {
|
||||
.el-scrollbar__bar {
|
||||
bottom: 0px;
|
||||
}
|
||||
.el-scrollbar__bar {
|
||||
bottom: 0px;
|
||||
}
|
||||
|
||||
.el-scrollbar__wrap {
|
||||
height: 49px;
|
||||
}
|
||||
.el-scrollbar__wrap {
|
||||
height: 49px;
|
||||
}
|
||||
}
|
||||
|
||||
.scroll-container {
|
||||
white-space: nowrap;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
white-space: nowrap;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -1,71 +1,71 @@
|
|||
<template>
|
||||
<div class="tags-view__container">
|
||||
<scroll-pane
|
||||
ref="scrollPaneRef"
|
||||
class="tags-view__wrapper"
|
||||
@scroll="handleScroll"
|
||||
>
|
||||
<router-link
|
||||
v-for="tag in visitedViews"
|
||||
:key="tag.path"
|
||||
:class="isActive(tag) ? 'active' : ''"
|
||||
:to="{ path: tag.path, query: tag.query, fullPath: tag.fullPath }"
|
||||
class="tags-view__item"
|
||||
@click.middle="!isAffix(tag) ? closeSelectedTag(tag) : ''"
|
||||
@contextmenu.prevent="openMenu(tag, $event)"
|
||||
>
|
||||
{{ generateTitle(tag.meta.title) }}
|
||||
<span
|
||||
v-if="!isAffix(tag)"
|
||||
class="icon-close"
|
||||
@click.prevent.stop="closeSelectedTag(tag)"
|
||||
>
|
||||
<svg-icon icon-class="close" />
|
||||
</span>
|
||||
</router-link>
|
||||
</scroll-pane>
|
||||
<ul
|
||||
v-show="visible"
|
||||
:style="{ left: left + 'px', top: top + 'px' }"
|
||||
class="tags-view__menu"
|
||||
>
|
||||
<li @click="refreshSelectedTag(selectedTag)">
|
||||
<svg-icon icon-class="refresh" />
|
||||
刷新
|
||||
</li>
|
||||
<li v-if="!isAffix(selectedTag)" @click="closeSelectedTag(selectedTag)">
|
||||
<svg-icon icon-class="close" />
|
||||
关闭
|
||||
</li>
|
||||
<li @click="closeOtherTags">
|
||||
<svg-icon icon-class="close_other" />
|
||||
关闭其它
|
||||
</li>
|
||||
<li v-if="!isFirstView()" @click="closeLeftTags">
|
||||
<svg-icon icon-class="close_left" />
|
||||
关闭左侧
|
||||
</li>
|
||||
<li v-if="!isLastView()" @click="closeRightTags">
|
||||
<svg-icon icon-class="close_right" />
|
||||
关闭右侧
|
||||
</li>
|
||||
<li @click="closeAllTags(selectedTag)">
|
||||
<svg-icon icon-class="close_all" />
|
||||
关闭所有
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="tags-view__container">
|
||||
<scroll-pane
|
||||
ref="scrollPaneRef"
|
||||
class="tags-view__wrapper"
|
||||
@scroll="handleScroll"
|
||||
>
|
||||
<router-link
|
||||
v-for="tag in visitedViews"
|
||||
:key="tag.path"
|
||||
:class="isActive(tag) ? 'active' : ''"
|
||||
:to="{ path: tag.path, query: tag.query, fullPath: tag.fullPath }"
|
||||
class="tags-view__item"
|
||||
@click.middle="!isAffix(tag) ? closeSelectedTag(tag) : ''"
|
||||
@contextmenu.prevent="openMenu(tag, $event)"
|
||||
>
|
||||
{{ generateTitle(tag.meta.title) }}
|
||||
<span
|
||||
v-if="!isAffix(tag)"
|
||||
class="icon-close"
|
||||
@click.prevent.stop="closeSelectedTag(tag)"
|
||||
>
|
||||
<svg-icon icon-class="close" />
|
||||
</span>
|
||||
</router-link>
|
||||
</scroll-pane>
|
||||
<ul
|
||||
v-show="visible"
|
||||
:style="{ left: left + 'px', top: top + 'px' }"
|
||||
class="tags-view__menu"
|
||||
>
|
||||
<li @click="refreshSelectedTag(selectedTag)">
|
||||
<svg-icon icon-class="refresh" />
|
||||
刷新
|
||||
</li>
|
||||
<li v-if="!isAffix(selectedTag)" @click="closeSelectedTag(selectedTag)">
|
||||
<svg-icon icon-class="close" />
|
||||
关闭
|
||||
</li>
|
||||
<li @click="closeOtherTags">
|
||||
<svg-icon icon-class="close_other" />
|
||||
关闭其它
|
||||
</li>
|
||||
<li v-if="!isFirstView()" @click="closeLeftTags">
|
||||
<svg-icon icon-class="close_left" />
|
||||
关闭左侧
|
||||
</li>
|
||||
<li v-if="!isLastView()" @click="closeRightTags">
|
||||
<svg-icon icon-class="close_right" />
|
||||
关闭右侧
|
||||
</li>
|
||||
<li @click="closeAllTags(selectedTag)">
|
||||
<svg-icon icon-class="close_all" />
|
||||
关闭所有
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
computed,
|
||||
getCurrentInstance,
|
||||
nextTick,
|
||||
ref,
|
||||
watch,
|
||||
onMounted,
|
||||
ComponentInternalInstance
|
||||
computed,
|
||||
getCurrentInstance,
|
||||
nextTick,
|
||||
ref,
|
||||
watch,
|
||||
onMounted,
|
||||
ComponentInternalInstance
|
||||
} from 'vue';
|
||||
|
||||
import path from 'path-browserify';
|
||||
|
|
@ -95,282 +95,282 @@ const left = ref(0);
|
|||
const top = ref(0);
|
||||
|
||||
watch(route, () => {
|
||||
addTags();
|
||||
moveToCurrentTag();
|
||||
addTags();
|
||||
moveToCurrentTag();
|
||||
});
|
||||
|
||||
watch(visible, value => {
|
||||
if (value) {
|
||||
document.body.addEventListener('click', closeMenu);
|
||||
} else {
|
||||
document.body.removeEventListener('click', closeMenu);
|
||||
}
|
||||
if (value) {
|
||||
document.body.addEventListener('click', closeMenu);
|
||||
} else {
|
||||
document.body.removeEventListener('click', closeMenu);
|
||||
}
|
||||
});
|
||||
|
||||
function filterAffixTags(routes: RouteRecordRaw[], basePath = '/') {
|
||||
let tags: TagView[] = [];
|
||||
let tags: TagView[] = [];
|
||||
|
||||
routes.forEach(route => {
|
||||
if (route.meta && route.meta.affix) {
|
||||
const tagPath = path.resolve(basePath, route.path);
|
||||
tags.push({
|
||||
fullPath: tagPath,
|
||||
path: tagPath,
|
||||
name: route.name,
|
||||
meta: { ...route.meta }
|
||||
});
|
||||
}
|
||||
routes.forEach(route => {
|
||||
if (route.meta && route.meta.affix) {
|
||||
const tagPath = path.resolve(basePath, route.path);
|
||||
tags.push({
|
||||
fullPath: tagPath,
|
||||
path: tagPath,
|
||||
name: route.name,
|
||||
meta: { ...route.meta }
|
||||
});
|
||||
}
|
||||
|
||||
if (route.children) {
|
||||
const childTags = filterAffixTags(route.children, route.path);
|
||||
if (childTags.length >= 1) {
|
||||
tags = tags.concat(childTags);
|
||||
}
|
||||
}
|
||||
});
|
||||
return tags;
|
||||
if (route.children) {
|
||||
const childTags = filterAffixTags(route.children, route.path);
|
||||
if (childTags.length >= 1) {
|
||||
tags = tags.concat(childTags);
|
||||
}
|
||||
}
|
||||
});
|
||||
return tags;
|
||||
}
|
||||
|
||||
function initTags() {
|
||||
const res = filterAffixTags(routes.value) as [];
|
||||
affixTags.value = res;
|
||||
for (const tag of res) {
|
||||
// Must have tag name
|
||||
if ((tag as TagView).name) {
|
||||
tagsView.addVisitedView(tag);
|
||||
}
|
||||
}
|
||||
const res = filterAffixTags(routes.value) as [];
|
||||
affixTags.value = res;
|
||||
for (const tag of res) {
|
||||
// Must have tag name
|
||||
if ((tag as TagView).name) {
|
||||
tagsView.addVisitedView(tag);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function addTags() {
|
||||
if (route.name) {
|
||||
tagsView.addView(route);
|
||||
}
|
||||
if (route.name) {
|
||||
tagsView.addView(route);
|
||||
}
|
||||
}
|
||||
|
||||
function moveToCurrentTag() {
|
||||
const tags = getCurrentInstance()?.refs.tag as any[];
|
||||
nextTick(() => {
|
||||
if (tags === null || tags === undefined || !Array.isArray(tags)) {
|
||||
return;
|
||||
}
|
||||
for (const tag of tags) {
|
||||
if ((tag.to as TagView).path === route.path) {
|
||||
(scrollPaneRef.value as any).value.moveToTarget(tag);
|
||||
// when query is different then update
|
||||
if ((tag.to as TagView).fullPath !== route.fullPath) {
|
||||
tagsView.updateVisitedView(route);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
const tags = getCurrentInstance()?.refs.tag as any[];
|
||||
nextTick(() => {
|
||||
if (tags === null || tags === undefined || !Array.isArray(tags)) {
|
||||
return;
|
||||
}
|
||||
for (const tag of tags) {
|
||||
if ((tag.to as TagView).path === route.path) {
|
||||
(scrollPaneRef.value as any).value.moveToTarget(tag);
|
||||
// when query is different then update
|
||||
if ((tag.to as TagView).fullPath !== route.fullPath) {
|
||||
tagsView.updateVisitedView(route);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function isActive(tag: TagView) {
|
||||
return tag.path === route.path;
|
||||
return tag.path === route.path;
|
||||
}
|
||||
|
||||
function isAffix(tag: TagView) {
|
||||
return tag.meta && tag.meta.affix;
|
||||
return tag.meta && tag.meta.affix;
|
||||
}
|
||||
|
||||
function isFirstView() {
|
||||
try {
|
||||
return (
|
||||
(selectedTag.value as TagView).fullPath ===
|
||||
visitedViews.value[1].fullPath ||
|
||||
(selectedTag.value as TagView).fullPath === '/index'
|
||||
);
|
||||
} catch (err) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
return (
|
||||
(selectedTag.value as TagView).fullPath ===
|
||||
visitedViews.value[1].fullPath ||
|
||||
(selectedTag.value as TagView).fullPath === '/index'
|
||||
);
|
||||
} catch (err) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function isLastView() {
|
||||
try {
|
||||
return (
|
||||
(selectedTag.value as TagView).fullPath ===
|
||||
visitedViews.value[visitedViews.value.length - 1].fullPath
|
||||
);
|
||||
} catch (err) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
return (
|
||||
(selectedTag.value as TagView).fullPath ===
|
||||
visitedViews.value[visitedViews.value.length - 1].fullPath
|
||||
);
|
||||
} catch (err) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function refreshSelectedTag(view: TagView) {
|
||||
tagsView.delCachedView(view);
|
||||
const { fullPath } = view;
|
||||
nextTick(() => {
|
||||
router.replace({ path: '/redirect' + fullPath }).catch(err => {
|
||||
console.warn(err);
|
||||
});
|
||||
});
|
||||
tagsView.delCachedView(view);
|
||||
const { fullPath } = view;
|
||||
nextTick(() => {
|
||||
router.replace({ path: '/redirect' + fullPath }).catch(err => {
|
||||
console.warn(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function toLastView(visitedViews: TagView[], view?: any) {
|
||||
const latestView = visitedViews.slice(-1)[0];
|
||||
if (latestView && latestView.fullPath) {
|
||||
router.push(latestView.fullPath);
|
||||
} else {
|
||||
// now the default is to redirect to the home page if there is no tags-view,
|
||||
// you can adjust it according to your needs.
|
||||
if (view.name === 'Dashboard') {
|
||||
// to reload home page
|
||||
router.replace({ path: '/redirect' + view.fullPath });
|
||||
} else {
|
||||
router.push('/');
|
||||
}
|
||||
}
|
||||
const latestView = visitedViews.slice(-1)[0];
|
||||
if (latestView && latestView.fullPath) {
|
||||
router.push(latestView.fullPath);
|
||||
} else {
|
||||
// now the default is to redirect to the home page if there is no tags-view,
|
||||
// you can adjust it according to your needs.
|
||||
if (view.name === 'Dashboard') {
|
||||
// to reload home page
|
||||
router.replace({ path: '/redirect' + view.fullPath });
|
||||
} else {
|
||||
router.push('/');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function closeSelectedTag(view: TagView) {
|
||||
tagsView.delView(view).then((res: any) => {
|
||||
if (isActive(view)) {
|
||||
toLastView(res.visitedViews, view);
|
||||
}
|
||||
});
|
||||
tagsView.delView(view).then((res: any) => {
|
||||
if (isActive(view)) {
|
||||
toLastView(res.visitedViews, view);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function closeLeftTags() {
|
||||
tagsView.delLeftViews(selectedTag.value).then((res: any) => {
|
||||
if (
|
||||
!res.visitedViews.find((item: any) => item.fullPath === route.fullPath)
|
||||
) {
|
||||
toLastView(res.visitedViews);
|
||||
}
|
||||
});
|
||||
tagsView.delLeftViews(selectedTag.value).then((res: any) => {
|
||||
if (
|
||||
!res.visitedViews.find((item: any) => item.fullPath === route.fullPath)
|
||||
) {
|
||||
toLastView(res.visitedViews);
|
||||
}
|
||||
});
|
||||
}
|
||||
function closeRightTags() {
|
||||
tagsView.delRightViews(selectedTag.value).then((res: any) => {
|
||||
if (
|
||||
!res.visitedViews.find((item: any) => item.fullPath === route.fullPath)
|
||||
) {
|
||||
toLastView(res.visitedViews);
|
||||
}
|
||||
});
|
||||
tagsView.delRightViews(selectedTag.value).then((res: any) => {
|
||||
if (
|
||||
!res.visitedViews.find((item: any) => item.fullPath === route.fullPath)
|
||||
) {
|
||||
toLastView(res.visitedViews);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function closeOtherTags() {
|
||||
tagsView.delOtherViews(selectedTag.value).then(() => {
|
||||
moveToCurrentTag();
|
||||
});
|
||||
tagsView.delOtherViews(selectedTag.value).then(() => {
|
||||
moveToCurrentTag();
|
||||
});
|
||||
}
|
||||
|
||||
function closeAllTags(view: TagView) {
|
||||
tagsView.delRightViews(selectedTag.value).then((res: any) => {
|
||||
if (affixTags.value.some((tag: any) => tag.path === route.path)) {
|
||||
return;
|
||||
}
|
||||
toLastView(res.visitedViews, view);
|
||||
});
|
||||
tagsView.delRightViews(selectedTag.value).then((res: any) => {
|
||||
if (affixTags.value.some((tag: any) => tag.path === route.path)) {
|
||||
return;
|
||||
}
|
||||
toLastView(res.visitedViews, view);
|
||||
});
|
||||
}
|
||||
|
||||
function openMenu(tag: TagView, e: MouseEvent) {
|
||||
const menuMinWidth = 105;
|
||||
const offsetLeft = proxy?.$el.getBoundingClientRect().left; // container margin left
|
||||
const offsetWidth = proxy?.$el.offsetWidth; // container width
|
||||
const maxLeft = offsetWidth - menuMinWidth; // left boundary
|
||||
const l = e.clientX - offsetLeft + 15; // 15: margin right
|
||||
const menuMinWidth = 105;
|
||||
const offsetLeft = proxy?.$el.getBoundingClientRect().left; // container margin left
|
||||
const offsetWidth = proxy?.$el.offsetWidth; // container width
|
||||
const maxLeft = offsetWidth - menuMinWidth; // left boundary
|
||||
const l = e.clientX - offsetLeft + 15; // 15: margin right
|
||||
|
||||
if (l > maxLeft) {
|
||||
left.value = maxLeft;
|
||||
} else {
|
||||
left.value = l;
|
||||
}
|
||||
if (l > maxLeft) {
|
||||
left.value = maxLeft;
|
||||
} else {
|
||||
left.value = l;
|
||||
}
|
||||
|
||||
top.value = e.clientY;
|
||||
visible.value = true;
|
||||
selectedTag.value = tag;
|
||||
top.value = e.clientY;
|
||||
visible.value = true;
|
||||
selectedTag.value = tag;
|
||||
}
|
||||
|
||||
function closeMenu() {
|
||||
visible.value = false;
|
||||
visible.value = false;
|
||||
}
|
||||
|
||||
function handleScroll() {
|
||||
closeMenu();
|
||||
closeMenu();
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
initTags();
|
||||
addTags();
|
||||
initTags();
|
||||
addTags();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.tags-view__container {
|
||||
height: 34px;
|
||||
width: 100%;
|
||||
background: #fff;
|
||||
border-bottom: 1px solid #d8dce5;
|
||||
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12), 0 0 3px 0 rgba(0, 0, 0, 0.04);
|
||||
height: 34px;
|
||||
width: 100%;
|
||||
background: #fff;
|
||||
border-bottom: 1px solid #d8dce5;
|
||||
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12), 0 0 3px 0 rgba(0, 0, 0, 0.04);
|
||||
|
||||
.tags-view__wrapper {
|
||||
.tags-view__item {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
height: 26px;
|
||||
line-height: 26px;
|
||||
border: 1px solid #d8dce5;
|
||||
color: #495060;
|
||||
background: #fff;
|
||||
padding: 0 8px;
|
||||
font-size: 12px;
|
||||
margin-left: 5px;
|
||||
margin-top: 4px;
|
||||
.tags-view__wrapper {
|
||||
.tags-view__item {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
height: 26px;
|
||||
line-height: 26px;
|
||||
border: 1px solid #d8dce5;
|
||||
color: #495060;
|
||||
background: #fff;
|
||||
padding: 0 8px;
|
||||
font-size: 12px;
|
||||
margin-left: 5px;
|
||||
margin-top: 4px;
|
||||
|
||||
&:first-of-type {
|
||||
margin-left: 15px;
|
||||
}
|
||||
&:first-of-type {
|
||||
margin-left: 15px;
|
||||
}
|
||||
|
||||
&:last-of-type {
|
||||
margin-right: 15px;
|
||||
}
|
||||
&:last-of-type {
|
||||
margin-right: 15px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
&:hover {
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
&.active {
|
||||
background-color: var(--el-color-primary-light-9);
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
&.active {
|
||||
background-color: var(--el-color-primary-light-9);
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
.icon-close {
|
||||
border-radius: 50%;
|
||||
text-align: center;
|
||||
.icon-close {
|
||||
border-radius: 50%;
|
||||
text-align: center;
|
||||
|
||||
&:hover {
|
||||
background-color: #ccc;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
&:hover {
|
||||
background-color: #ccc;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tags-view__menu {
|
||||
margin: 0;
|
||||
background: #fff;
|
||||
z-index: 3000;
|
||||
position: absolute;
|
||||
list-style-type: none;
|
||||
padding: 5px 0;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
color: #333;
|
||||
box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, 0.3);
|
||||
.tags-view__menu {
|
||||
margin: 0;
|
||||
background: #fff;
|
||||
z-index: 3000;
|
||||
position: absolute;
|
||||
list-style-type: none;
|
||||
padding: 5px 0;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
color: #333;
|
||||
box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, 0.3);
|
||||
|
||||
li {
|
||||
margin: 0;
|
||||
padding: 7px 16px;
|
||||
cursor: pointer;
|
||||
li {
|
||||
margin: 0;
|
||||
padding: 7px 16px;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background: #eee;
|
||||
}
|
||||
}
|
||||
}
|
||||
&:hover {
|
||||
background: #eee;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -1,22 +1,22 @@
|
|||
<template>
|
||||
<div :class="classObj" class="app-wrapper">
|
||||
<div
|
||||
v-if="device === 'mobile' && sidebar.opened"
|
||||
class="drawer-bg"
|
||||
@click="handleClickOutside"
|
||||
/>
|
||||
<Sidebar class="sidebar-container" />
|
||||
<div :class="{ hasTagsView: needTagsView }" class="main-container">
|
||||
<div :class="{ 'fixed-header': fixedHeader }">
|
||||
<navbar />
|
||||
<tags-view v-if="needTagsView" />
|
||||
</div>
|
||||
<app-main />
|
||||
<RightPanel v-if="showSettings">
|
||||
<settings />
|
||||
</RightPanel>
|
||||
</div>
|
||||
</div>
|
||||
<div :class="classObj" class="app-wrapper">
|
||||
<div
|
||||
v-if="device === 'mobile' && sidebar.opened"
|
||||
class="drawer-bg"
|
||||
@click="handleClickOutside"
|
||||
/>
|
||||
<Sidebar class="sidebar-container" />
|
||||
<div :class="{ hasTagsView: needTagsView }" class="main-container">
|
||||
<div :class="{ 'fixed-header': fixedHeader }">
|
||||
<navbar />
|
||||
<tags-view v-if="needTagsView" />
|
||||
</div>
|
||||
<app-main />
|
||||
<RightPanel v-if="showSettings">
|
||||
<settings />
|
||||
</RightPanel>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
|
@ -40,23 +40,23 @@ const fixedHeader = computed(() => setting.fixedHeader);
|
|||
const showSettings = computed(() => setting.showSettings);
|
||||
|
||||
const classObj = computed(() => ({
|
||||
hideSidebar: !sidebar.value.opened,
|
||||
openSidebar: sidebar.value.opened,
|
||||
withoutAnimation: sidebar.value.withoutAnimation,
|
||||
mobile: device.value === 'mobile'
|
||||
hideSidebar: !sidebar.value.opened,
|
||||
openSidebar: sidebar.value.opened,
|
||||
withoutAnimation: sidebar.value.withoutAnimation,
|
||||
mobile: device.value === 'mobile'
|
||||
}));
|
||||
|
||||
watchEffect(() => {
|
||||
if (width.value < WIDTH) {
|
||||
app.toggleDevice('mobile');
|
||||
app.closeSideBar(true);
|
||||
} else {
|
||||
app.toggleDevice('desktop');
|
||||
}
|
||||
if (width.value < WIDTH) {
|
||||
app.toggleDevice('mobile');
|
||||
app.closeSideBar(true);
|
||||
} else {
|
||||
app.toggleDevice('desktop');
|
||||
}
|
||||
});
|
||||
|
||||
function handleClickOutside() {
|
||||
app.closeSideBar(false);
|
||||
app.closeSideBar(false);
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
@ -65,41 +65,41 @@ function handleClickOutside() {
|
|||
@import '@/styles/variables.module.scss';
|
||||
|
||||
.app-wrapper {
|
||||
@include clearfix;
|
||||
position: relative;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
@include clearfix;
|
||||
position: relative;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
|
||||
&.mobile.openSidebar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
}
|
||||
&.mobile.openSidebar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.drawer-bg {
|
||||
background: #000;
|
||||
opacity: 0.3;
|
||||
width: 100%;
|
||||
top: 0;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
z-index: 999;
|
||||
background: #000;
|
||||
opacity: 0.3;
|
||||
width: 100%;
|
||||
top: 0;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
.fixed-header {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
z-index: 9;
|
||||
width: calc(100% - #{$sideBarWidth});
|
||||
transition: width 0.28s;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
z-index: 9;
|
||||
width: calc(100% - #{$sideBarWidth});
|
||||
transition: width 0.28s;
|
||||
}
|
||||
|
||||
.hideSidebar .fixed-header {
|
||||
width: calc(100% - 54px);
|
||||
width: calc(100% - 54px);
|
||||
}
|
||||
|
||||
.mobile .fixed-header {
|
||||
width: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
14
src/main.ts
14
src/main.ts
|
|
@ -27,7 +27,7 @@ const app = createApp(App);
|
|||
import * as directive from '@/directive';
|
||||
|
||||
Object.keys(directive).forEach(key => {
|
||||
app.directive(key, (directive as { [key: string]: Directive })[key]);
|
||||
app.directive(key, (directive as { [key: string]: Directive })[key]);
|
||||
});
|
||||
|
||||
// 全局方法
|
||||
|
|
@ -35,9 +35,9 @@ app.config.globalProperties.$listDictsByCode = listDictsByCode;
|
|||
|
||||
// 注册全局组件
|
||||
app
|
||||
.component('Pagination', Pagination)
|
||||
.use(createPinia())
|
||||
.use(router)
|
||||
.use(ElementPlus)
|
||||
.use(i18n)
|
||||
.mount('#app');
|
||||
.component('Pagination', Pagination)
|
||||
.use(createPinia())
|
||||
.use(router)
|
||||
.use(ElementPlus)
|
||||
.use(i18n)
|
||||
.mount('#app');
|
||||
|
|
|
|||
|
|
@ -9,47 +9,47 @@ NProgress.configure({ showSpinner: false }); // 进度环显示/隐藏
|
|||
const whiteList = ['/login', '/auth-redirect'];
|
||||
|
||||
router.beforeEach(async (to, form, next) => {
|
||||
NProgress.start();
|
||||
const { user, permission } = useStore();
|
||||
const hasToken = user.token;
|
||||
if (hasToken) {
|
||||
// 登录成功,跳转到首页
|
||||
if (to.path === '/login') {
|
||||
next({ path: '/' });
|
||||
NProgress.done();
|
||||
} else {
|
||||
const hasGetUserInfo = user.roles.length > 0;
|
||||
if (hasGetUserInfo) {
|
||||
next();
|
||||
} else {
|
||||
try {
|
||||
await user.getUserInfo();
|
||||
const roles = user.roles;
|
||||
const accessRoutes: any = await permission.generateRoutes(roles);
|
||||
accessRoutes.forEach((route: any) => {
|
||||
router.addRoute(route);
|
||||
});
|
||||
next({ ...to, replace: true });
|
||||
} catch (error) {
|
||||
// 移除 token 并跳转登录页
|
||||
await user.resetToken();
|
||||
ElMessage.error((error as any) || 'Has Error');
|
||||
next(`/login?redirect=${to.path}`);
|
||||
NProgress.done();
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 未登录可以访问白名单页面(登录页面)
|
||||
if (whiteList.indexOf(to.path) !== -1) {
|
||||
next();
|
||||
} else {
|
||||
next(`/login?redirect=${to.path}`);
|
||||
NProgress.done();
|
||||
}
|
||||
}
|
||||
NProgress.start();
|
||||
const { user, permission } = useStore();
|
||||
const hasToken = user.token;
|
||||
if (hasToken) {
|
||||
// 登录成功,跳转到首页
|
||||
if (to.path === '/login') {
|
||||
next({ path: '/' });
|
||||
NProgress.done();
|
||||
} else {
|
||||
const hasGetUserInfo = user.roles.length > 0;
|
||||
if (hasGetUserInfo) {
|
||||
next();
|
||||
} else {
|
||||
try {
|
||||
await user.getUserInfo();
|
||||
const roles = user.roles;
|
||||
const accessRoutes: any = await permission.generateRoutes(roles);
|
||||
accessRoutes.forEach((route: any) => {
|
||||
router.addRoute(route);
|
||||
});
|
||||
next({ ...to, replace: true });
|
||||
} catch (error) {
|
||||
// 移除 token 并跳转登录页
|
||||
await user.resetToken();
|
||||
ElMessage.error((error as any) || 'Has Error');
|
||||
next(`/login?redirect=${to.path}`);
|
||||
NProgress.done();
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 未登录可以访问白名单页面(登录页面)
|
||||
if (whiteList.indexOf(to.path) !== -1) {
|
||||
next();
|
||||
} else {
|
||||
next(`/login?redirect=${to.path}`);
|
||||
NProgress.done();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
router.afterEach(() => {
|
||||
NProgress.done();
|
||||
NProgress.done();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -6,48 +6,48 @@ export const Layout = () => import('@/layout/index.vue');
|
|||
// 参数说明: https://panjiachen.github.io/vue-element-admin-site/guide/essentials/router-and-nav.html
|
||||
// 静态路由
|
||||
export const constantRoutes: Array<RouteRecordRaw> = [
|
||||
{
|
||||
path: '/redirect',
|
||||
component: Layout,
|
||||
meta: { hidden: true },
|
||||
children: [
|
||||
{
|
||||
path: '/redirect/:path(.*)',
|
||||
component: () => import('@/views/redirect/index.vue')
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/login',
|
||||
component: () => import('@/views/login/index.vue'),
|
||||
meta: { hidden: true }
|
||||
},
|
||||
{
|
||||
path: '/404',
|
||||
component: () => import('@/views/error-page/404.vue'),
|
||||
meta: { hidden: true }
|
||||
},
|
||||
{
|
||||
path: '/401',
|
||||
component: () => import('@/views/error-page/401.vue'),
|
||||
meta: { hidden: true }
|
||||
},
|
||||
{
|
||||
path: '/',
|
||||
component: Layout,
|
||||
redirect: '/dashboard',
|
||||
children: [
|
||||
{
|
||||
path: 'dashboard',
|
||||
component: () => import('@/views/dashboard/index.vue'),
|
||||
name: 'Dashboard',
|
||||
meta: { title: 'dashboard', icon: 'homepage', affix: true }
|
||||
}
|
||||
]
|
||||
}
|
||||
{
|
||||
path: '/redirect',
|
||||
component: Layout,
|
||||
meta: { hidden: true },
|
||||
children: [
|
||||
{
|
||||
path: '/redirect/:path(.*)',
|
||||
component: () => import('@/views/redirect/index.vue')
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/login',
|
||||
component: () => import('@/views/login/index.vue'),
|
||||
meta: { hidden: true }
|
||||
},
|
||||
{
|
||||
path: '/404',
|
||||
component: () => import('@/views/error-page/404.vue'),
|
||||
meta: { hidden: true }
|
||||
},
|
||||
{
|
||||
path: '/401',
|
||||
component: () => import('@/views/error-page/401.vue'),
|
||||
meta: { hidden: true }
|
||||
},
|
||||
{
|
||||
path: '/',
|
||||
component: Layout,
|
||||
redirect: '/dashboard',
|
||||
children: [
|
||||
{
|
||||
path: 'dashboard',
|
||||
component: () => import('@/views/dashboard/index.vue'),
|
||||
name: 'Dashboard',
|
||||
meta: { title: 'dashboard', icon: 'homepage', affix: true }
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
// 外部链接
|
||||
/*{
|
||||
// 外部链接
|
||||
/*{
|
||||
path: '/external-link',
|
||||
component: Layout,
|
||||
children: [
|
||||
|
|
@ -57,8 +57,8 @@ export const constantRoutes: Array<RouteRecordRaw> = [
|
|||
}
|
||||
]
|
||||
}*/
|
||||
// 多级嵌套路由
|
||||
/* {
|
||||
// 多级嵌套路由
|
||||
/* {
|
||||
path: '/nested',
|
||||
component: Layout,
|
||||
redirect: '/nested/level1/level2',
|
||||
|
|
@ -101,21 +101,21 @@ export const constantRoutes: Array<RouteRecordRaw> = [
|
|||
|
||||
// 创建路由
|
||||
const router = createRouter({
|
||||
history: createWebHashHistory(),
|
||||
routes: constantRoutes as RouteRecordRaw[],
|
||||
// 刷新时,滚动条位置还原
|
||||
scrollBehavior: () => ({ left: 0, top: 0 })
|
||||
history: createWebHashHistory(),
|
||||
routes: constantRoutes as RouteRecordRaw[],
|
||||
// 刷新时,滚动条位置还原
|
||||
scrollBehavior: () => ({ left: 0, top: 0 })
|
||||
});
|
||||
|
||||
// 重置路由
|
||||
export function resetRouter() {
|
||||
const { permission } = useStore();
|
||||
permission.routes.forEach(route => {
|
||||
const name = route.name;
|
||||
if (name && router.hasRoute(name)) {
|
||||
router.removeRoute(name);
|
||||
}
|
||||
});
|
||||
const { permission } = useStore();
|
||||
permission.routes.forEach(route => {
|
||||
const name = route.name;
|
||||
if (name && router.hasRoute(name)) {
|
||||
router.removeRoute(name);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export default router;
|
||||
|
|
|
|||
|
|
@ -1,20 +1,20 @@
|
|||
interface DefaultSettings {
|
||||
title: string;
|
||||
showSettings: boolean;
|
||||
tagsView: boolean;
|
||||
fixedHeader: boolean;
|
||||
sidebarLogo: boolean;
|
||||
errorLog: string;
|
||||
title: string;
|
||||
showSettings: boolean;
|
||||
tagsView: boolean;
|
||||
fixedHeader: boolean;
|
||||
sidebarLogo: boolean;
|
||||
errorLog: string;
|
||||
}
|
||||
|
||||
const defaultSettings: DefaultSettings = {
|
||||
title: 'vue3-element-admin',
|
||||
showSettings: true,
|
||||
tagsView: true,
|
||||
fixedHeader: false,
|
||||
// 是否显示Logo
|
||||
sidebarLogo: true,
|
||||
errorLog: 'production'
|
||||
title: 'vue3-element-admin',
|
||||
showSettings: true,
|
||||
tagsView: true,
|
||||
fixedHeader: false,
|
||||
// 是否显示Logo
|
||||
sidebarLogo: true,
|
||||
errorLog: 'production'
|
||||
};
|
||||
|
||||
export default defaultSettings;
|
||||
|
|
|
|||
|
|
@ -5,11 +5,11 @@ import useSettingStore from './modules/settings';
|
|||
import useTagsViewStore from './modules/tagsView';
|
||||
|
||||
const useStore = () => ({
|
||||
user: useUserStore(),
|
||||
app: useAppStore(),
|
||||
permission: usePermissionStore(),
|
||||
setting: useSettingStore(),
|
||||
tagsView: useTagsViewStore()
|
||||
user: useUserStore(),
|
||||
app: useAppStore(),
|
||||
permission: usePermissionStore(),
|
||||
setting: useSettingStore(),
|
||||
tagsView: useTagsViewStore()
|
||||
});
|
||||
|
||||
export default useStore;
|
||||
|
|
|
|||
|
|
@ -4,45 +4,45 @@ import { defineStore } from 'pinia';
|
|||
import { getLanguage } from '@/lang/index';
|
||||
|
||||
const useAppStore = defineStore({
|
||||
id: 'app',
|
||||
state: (): AppState => ({
|
||||
device: 'desktop',
|
||||
sidebar: {
|
||||
opened: localStorage.get('sidebarStatus')
|
||||
? !!+localStorage.get('sidebarStatus')
|
||||
: true,
|
||||
withoutAnimation: false
|
||||
},
|
||||
language: getLanguage(),
|
||||
size: localStorage.get('size') || 'default'
|
||||
}),
|
||||
actions: {
|
||||
toggleSidebar() {
|
||||
this.sidebar.opened = !this.sidebar.opened;
|
||||
this.sidebar.withoutAnimation = false;
|
||||
if (this.sidebar.opened) {
|
||||
localStorage.set('sidebarStatus', 1);
|
||||
} else {
|
||||
localStorage.set('sidebarStatus', 0);
|
||||
}
|
||||
},
|
||||
closeSideBar(withoutAnimation: any) {
|
||||
localStorage.set('sidebarStatus', 0);
|
||||
this.sidebar.opened = false;
|
||||
this.sidebar.withoutAnimation = withoutAnimation;
|
||||
},
|
||||
toggleDevice(device: string) {
|
||||
this.device = device;
|
||||
},
|
||||
setSize(size: string) {
|
||||
this.size = size;
|
||||
localStorage.set('size', size);
|
||||
},
|
||||
setLanguage(language: string) {
|
||||
this.language = language;
|
||||
localStorage.set('language', language);
|
||||
}
|
||||
}
|
||||
id: 'app',
|
||||
state: (): AppState => ({
|
||||
device: 'desktop',
|
||||
sidebar: {
|
||||
opened: localStorage.get('sidebarStatus')
|
||||
? !!+localStorage.get('sidebarStatus')
|
||||
: true,
|
||||
withoutAnimation: false
|
||||
},
|
||||
language: getLanguage(),
|
||||
size: localStorage.get('size') || 'default'
|
||||
}),
|
||||
actions: {
|
||||
toggleSidebar() {
|
||||
this.sidebar.opened = !this.sidebar.opened;
|
||||
this.sidebar.withoutAnimation = false;
|
||||
if (this.sidebar.opened) {
|
||||
localStorage.set('sidebarStatus', 1);
|
||||
} else {
|
||||
localStorage.set('sidebarStatus', 0);
|
||||
}
|
||||
},
|
||||
closeSideBar(withoutAnimation: any) {
|
||||
localStorage.set('sidebarStatus', 0);
|
||||
this.sidebar.opened = false;
|
||||
this.sidebar.withoutAnimation = withoutAnimation;
|
||||
},
|
||||
toggleDevice(device: string) {
|
||||
this.device = device;
|
||||
},
|
||||
setSize(size: string) {
|
||||
this.size = size;
|
||||
localStorage.set('size', size);
|
||||
},
|
||||
setLanguage(language: string) {
|
||||
this.language = language;
|
||||
localStorage.set('language', language);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
export default useAppStore;
|
||||
|
|
|
|||
|
|
@ -8,73 +8,73 @@ const modules = import.meta.glob('../../views/**/**.vue');
|
|||
export const Layout = () => import('@/layout/index.vue');
|
||||
|
||||
const hasPermission = (roles: string[], route: RouteRecordRaw) => {
|
||||
if (route.meta && route.meta.roles) {
|
||||
if (roles.includes('ROOT')) {
|
||||
return true;
|
||||
}
|
||||
return roles.some(role => {
|
||||
if (route.meta?.roles !== undefined) {
|
||||
return (route.meta.roles as string[]).includes(role);
|
||||
}
|
||||
});
|
||||
}
|
||||
return false;
|
||||
if (route.meta && route.meta.roles) {
|
||||
if (roles.includes('ROOT')) {
|
||||
return true;
|
||||
}
|
||||
return roles.some(role => {
|
||||
if (route.meta?.roles !== undefined) {
|
||||
return (route.meta.roles as string[]).includes(role);
|
||||
}
|
||||
});
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
export const filterAsyncRoutes = (
|
||||
routes: RouteRecordRaw[],
|
||||
roles: string[]
|
||||
routes: RouteRecordRaw[],
|
||||
roles: string[]
|
||||
) => {
|
||||
const res: RouteRecordRaw[] = [];
|
||||
routes.forEach(route => {
|
||||
const tmp = { ...route } as any;
|
||||
if (hasPermission(roles, tmp)) {
|
||||
if (tmp.component == 'Layout') {
|
||||
tmp.component = Layout;
|
||||
} else {
|
||||
const component = modules[`../../views/${tmp.component}.vue`] as any;
|
||||
if (component) {
|
||||
tmp.component = modules[`../../views/${tmp.component}.vue`];
|
||||
} else {
|
||||
tmp.component = modules[`../../views/error-page/404.vue`];
|
||||
}
|
||||
}
|
||||
res.push(tmp);
|
||||
const res: RouteRecordRaw[] = [];
|
||||
routes.forEach(route => {
|
||||
const tmp = { ...route } as any;
|
||||
if (hasPermission(roles, tmp)) {
|
||||
if (tmp.component == 'Layout') {
|
||||
tmp.component = Layout;
|
||||
} else {
|
||||
const component = modules[`../../views/${tmp.component}.vue`] as any;
|
||||
if (component) {
|
||||
tmp.component = modules[`../../views/${tmp.component}.vue`];
|
||||
} else {
|
||||
tmp.component = modules[`../../views/error-page/404.vue`];
|
||||
}
|
||||
}
|
||||
res.push(tmp);
|
||||
|
||||
if (tmp.children) {
|
||||
tmp.children = filterAsyncRoutes(tmp.children, roles);
|
||||
}
|
||||
}
|
||||
});
|
||||
return res;
|
||||
if (tmp.children) {
|
||||
tmp.children = filterAsyncRoutes(tmp.children, roles);
|
||||
}
|
||||
}
|
||||
});
|
||||
return res;
|
||||
};
|
||||
|
||||
const usePermissionStore = defineStore({
|
||||
id: 'permission',
|
||||
state: (): PermissionState => ({
|
||||
routes: [],
|
||||
addRoutes: []
|
||||
}),
|
||||
actions: {
|
||||
setRoutes(routes: RouteRecordRaw[]) {
|
||||
this.addRoutes = routes;
|
||||
this.routes = constantRoutes.concat(routes);
|
||||
},
|
||||
generateRoutes(roles: string[]) {
|
||||
return new Promise((resolve, reject) => {
|
||||
listRoutes()
|
||||
.then(response => {
|
||||
const asyncRoutes = response.data;
|
||||
const accessedRoutes = filterAsyncRoutes(asyncRoutes, roles);
|
||||
this.setRoutes(accessedRoutes);
|
||||
resolve(accessedRoutes);
|
||||
})
|
||||
.catch(error => {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
id: 'permission',
|
||||
state: (): PermissionState => ({
|
||||
routes: [],
|
||||
addRoutes: []
|
||||
}),
|
||||
actions: {
|
||||
setRoutes(routes: RouteRecordRaw[]) {
|
||||
this.addRoutes = routes;
|
||||
this.routes = constantRoutes.concat(routes);
|
||||
},
|
||||
generateRoutes(roles: string[]) {
|
||||
return new Promise((resolve, reject) => {
|
||||
listRoutes()
|
||||
.then(response => {
|
||||
const asyncRoutes = response.data;
|
||||
const accessedRoutes = filterAsyncRoutes(asyncRoutes, roles);
|
||||
this.setRoutes(accessedRoutes);
|
||||
resolve(accessedRoutes);
|
||||
})
|
||||
.catch(error => {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
export default usePermissionStore;
|
||||
|
|
|
|||
|
|
@ -7,44 +7,44 @@ const { showSettings, tagsView, fixedHeader, sidebarLogo } = defaultSettings;
|
|||
const el = document.documentElement;
|
||||
|
||||
export const useSettingStore = defineStore({
|
||||
id: 'setting',
|
||||
state: (): SettingState => ({
|
||||
theme:
|
||||
localStorage.get('theme') ||
|
||||
getComputedStyle(el).getPropertyValue(`--el-color-primary`),
|
||||
showSettings: showSettings,
|
||||
tagsView:
|
||||
localStorage.get('tagsView') != null
|
||||
? localStorage.get('tagsView')
|
||||
: tagsView,
|
||||
fixedHeader: fixedHeader,
|
||||
sidebarLogo: sidebarLogo
|
||||
}),
|
||||
actions: {
|
||||
async changeSetting(payload: { key: string; value: any }) {
|
||||
const { key, value } = payload;
|
||||
switch (key) {
|
||||
case 'theme':
|
||||
this.theme = value;
|
||||
break;
|
||||
case 'showSettings':
|
||||
this.showSettings = value;
|
||||
break;
|
||||
case 'fixedHeader':
|
||||
this.fixedHeader = value;
|
||||
break;
|
||||
case 'tagsView':
|
||||
this.tagsView = value;
|
||||
localStorage.set('tagsView', value);
|
||||
break;
|
||||
case 'sidebarLogo':
|
||||
this.sidebarLogo = value;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
id: 'setting',
|
||||
state: (): SettingState => ({
|
||||
theme:
|
||||
localStorage.get('theme') ||
|
||||
getComputedStyle(el).getPropertyValue(`--el-color-primary`),
|
||||
showSettings: showSettings,
|
||||
tagsView:
|
||||
localStorage.get('tagsView') != null
|
||||
? localStorage.get('tagsView')
|
||||
: tagsView,
|
||||
fixedHeader: fixedHeader,
|
||||
sidebarLogo: sidebarLogo
|
||||
}),
|
||||
actions: {
|
||||
async changeSetting(payload: { key: string; value: any }) {
|
||||
const { key, value } = payload;
|
||||
switch (key) {
|
||||
case 'theme':
|
||||
this.theme = value;
|
||||
break;
|
||||
case 'showSettings':
|
||||
this.showSettings = value;
|
||||
break;
|
||||
case 'fixedHeader':
|
||||
this.fixedHeader = value;
|
||||
break;
|
||||
case 'tagsView':
|
||||
this.tagsView = value;
|
||||
localStorage.set('tagsView', value);
|
||||
break;
|
||||
case 'sidebarLogo':
|
||||
this.sidebarLogo = value;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
export default useSettingStore;
|
||||
|
|
|
|||
|
|
@ -2,174 +2,174 @@ import { defineStore } from 'pinia';
|
|||
import { TagsViewState } from '@/types';
|
||||
|
||||
const useTagsViewStore = defineStore({
|
||||
id: 'tagsView',
|
||||
state: (): TagsViewState => ({
|
||||
visitedViews: [],
|
||||
cachedViews: []
|
||||
}),
|
||||
actions: {
|
||||
addVisitedView(view: any) {
|
||||
if (this.visitedViews.some(v => v.path === view.path)) return;
|
||||
this.visitedViews.push(
|
||||
Object.assign({}, view, {
|
||||
title: view.meta?.title || 'no-name'
|
||||
})
|
||||
);
|
||||
},
|
||||
addCachedView(view: any) {
|
||||
if (this.cachedViews.includes(view.name)) return;
|
||||
if (!view.meta.noCache) {
|
||||
this.cachedViews.push(view.name);
|
||||
}
|
||||
},
|
||||
id: 'tagsView',
|
||||
state: (): TagsViewState => ({
|
||||
visitedViews: [],
|
||||
cachedViews: []
|
||||
}),
|
||||
actions: {
|
||||
addVisitedView(view: any) {
|
||||
if (this.visitedViews.some(v => v.path === view.path)) return;
|
||||
this.visitedViews.push(
|
||||
Object.assign({}, view, {
|
||||
title: view.meta?.title || 'no-name'
|
||||
})
|
||||
);
|
||||
},
|
||||
addCachedView(view: any) {
|
||||
if (this.cachedViews.includes(view.name)) return;
|
||||
if (!view.meta.noCache) {
|
||||
this.cachedViews.push(view.name);
|
||||
}
|
||||
},
|
||||
|
||||
delVisitedView(view: any) {
|
||||
return new Promise(resolve => {
|
||||
for (const [i, v] of this.visitedViews.entries()) {
|
||||
if (v.path === view.path) {
|
||||
this.visitedViews.splice(i, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
resolve([...this.visitedViews]);
|
||||
});
|
||||
},
|
||||
delCachedView(view: any) {
|
||||
return new Promise(resolve => {
|
||||
const index = this.cachedViews.indexOf(view.name);
|
||||
index > -1 && this.cachedViews.splice(index, 1);
|
||||
resolve([...this.cachedViews]);
|
||||
});
|
||||
},
|
||||
delVisitedView(view: any) {
|
||||
return new Promise(resolve => {
|
||||
for (const [i, v] of this.visitedViews.entries()) {
|
||||
if (v.path === view.path) {
|
||||
this.visitedViews.splice(i, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
resolve([...this.visitedViews]);
|
||||
});
|
||||
},
|
||||
delCachedView(view: any) {
|
||||
return new Promise(resolve => {
|
||||
const index = this.cachedViews.indexOf(view.name);
|
||||
index > -1 && this.cachedViews.splice(index, 1);
|
||||
resolve([...this.cachedViews]);
|
||||
});
|
||||
},
|
||||
|
||||
delOtherVisitedViews(view: any) {
|
||||
return new Promise(resolve => {
|
||||
this.visitedViews = this.visitedViews.filter(v => {
|
||||
return v.meta?.affix || v.path === view.path;
|
||||
});
|
||||
resolve([...this.visitedViews]);
|
||||
});
|
||||
},
|
||||
delOtherCachedViews(view: any) {
|
||||
return new Promise(resolve => {
|
||||
const index = this.cachedViews.indexOf(view.name);
|
||||
if (index > -1) {
|
||||
this.cachedViews = this.cachedViews.slice(index, index + 1);
|
||||
} else {
|
||||
// if index = -1, there is no cached tags
|
||||
this.cachedViews = [];
|
||||
}
|
||||
resolve([...this.cachedViews]);
|
||||
});
|
||||
},
|
||||
delOtherVisitedViews(view: any) {
|
||||
return new Promise(resolve => {
|
||||
this.visitedViews = this.visitedViews.filter(v => {
|
||||
return v.meta?.affix || v.path === view.path;
|
||||
});
|
||||
resolve([...this.visitedViews]);
|
||||
});
|
||||
},
|
||||
delOtherCachedViews(view: any) {
|
||||
return new Promise(resolve => {
|
||||
const index = this.cachedViews.indexOf(view.name);
|
||||
if (index > -1) {
|
||||
this.cachedViews = this.cachedViews.slice(index, index + 1);
|
||||
} else {
|
||||
// if index = -1, there is no cached tags
|
||||
this.cachedViews = [];
|
||||
}
|
||||
resolve([...this.cachedViews]);
|
||||
});
|
||||
},
|
||||
|
||||
updateVisitedView(view: any) {
|
||||
for (let v of this.visitedViews) {
|
||||
if (v.path === view.path) {
|
||||
v = Object.assign(v, view);
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
addView(view: any) {
|
||||
this.addVisitedView(view);
|
||||
this.addCachedView(view);
|
||||
},
|
||||
delView(view: any) {
|
||||
return new Promise(resolve => {
|
||||
this.delVisitedView(view);
|
||||
this.delCachedView(view);
|
||||
resolve({
|
||||
visitedViews: [...this.visitedViews],
|
||||
cachedViews: [...this.cachedViews]
|
||||
});
|
||||
});
|
||||
},
|
||||
delOtherViews(view: any) {
|
||||
return new Promise(resolve => {
|
||||
this.delOtherVisitedViews(view);
|
||||
this.delOtherCachedViews(view);
|
||||
resolve({
|
||||
visitedViews: [...this.visitedViews],
|
||||
cachedViews: [...this.cachedViews]
|
||||
});
|
||||
});
|
||||
},
|
||||
delLeftViews(view: any) {
|
||||
return new Promise(resolve => {
|
||||
const currIndex = this.visitedViews.findIndex(
|
||||
v => v.path === view.path
|
||||
);
|
||||
if (currIndex === -1) {
|
||||
return;
|
||||
}
|
||||
this.visitedViews = this.visitedViews.filter((item, index) => {
|
||||
// affix:true 固定tag,例如“首页”
|
||||
if (index >= currIndex || (item.meta && item.meta.affix)) {
|
||||
return true;
|
||||
}
|
||||
updateVisitedView(view: any) {
|
||||
for (let v of this.visitedViews) {
|
||||
if (v.path === view.path) {
|
||||
v = Object.assign(v, view);
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
addView(view: any) {
|
||||
this.addVisitedView(view);
|
||||
this.addCachedView(view);
|
||||
},
|
||||
delView(view: any) {
|
||||
return new Promise(resolve => {
|
||||
this.delVisitedView(view);
|
||||
this.delCachedView(view);
|
||||
resolve({
|
||||
visitedViews: [...this.visitedViews],
|
||||
cachedViews: [...this.cachedViews]
|
||||
});
|
||||
});
|
||||
},
|
||||
delOtherViews(view: any) {
|
||||
return new Promise(resolve => {
|
||||
this.delOtherVisitedViews(view);
|
||||
this.delOtherCachedViews(view);
|
||||
resolve({
|
||||
visitedViews: [...this.visitedViews],
|
||||
cachedViews: [...this.cachedViews]
|
||||
});
|
||||
});
|
||||
},
|
||||
delLeftViews(view: any) {
|
||||
return new Promise(resolve => {
|
||||
const currIndex = this.visitedViews.findIndex(
|
||||
v => v.path === view.path
|
||||
);
|
||||
if (currIndex === -1) {
|
||||
return;
|
||||
}
|
||||
this.visitedViews = this.visitedViews.filter((item, index) => {
|
||||
// affix:true 固定tag,例如“首页”
|
||||
if (index >= currIndex || (item.meta && item.meta.affix)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const cacheIndex = this.cachedViews.indexOf(item.name as string);
|
||||
if (cacheIndex > -1) {
|
||||
this.cachedViews.splice(cacheIndex, 1);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
resolve({
|
||||
visitedViews: [...this.visitedViews]
|
||||
});
|
||||
});
|
||||
},
|
||||
delRightViews(view: any) {
|
||||
return new Promise(resolve => {
|
||||
const currIndex = this.visitedViews.findIndex(
|
||||
v => v.path === view.path
|
||||
);
|
||||
if (currIndex === -1) {
|
||||
return;
|
||||
}
|
||||
this.visitedViews = this.visitedViews.filter((item, index) => {
|
||||
// affix:true 固定tag,例如“首页”
|
||||
if (index <= currIndex || (item.meta && item.meta.affix)) {
|
||||
return true;
|
||||
}
|
||||
const cacheIndex = this.cachedViews.indexOf(item.name as string);
|
||||
if (cacheIndex > -1) {
|
||||
this.cachedViews.splice(cacheIndex, 1);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
resolve({
|
||||
visitedViews: [...this.visitedViews]
|
||||
});
|
||||
});
|
||||
},
|
||||
delRightViews(view: any) {
|
||||
return new Promise(resolve => {
|
||||
const currIndex = this.visitedViews.findIndex(
|
||||
v => v.path === view.path
|
||||
);
|
||||
if (currIndex === -1) {
|
||||
return;
|
||||
}
|
||||
this.visitedViews = this.visitedViews.filter((item, index) => {
|
||||
// affix:true 固定tag,例如“首页”
|
||||
if (index <= currIndex || (item.meta && item.meta.affix)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const cacheIndex = this.cachedViews.indexOf(item.name as string);
|
||||
if (cacheIndex > -1) {
|
||||
this.cachedViews.splice(cacheIndex, 1);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
resolve({
|
||||
visitedViews: [...this.visitedViews]
|
||||
});
|
||||
});
|
||||
},
|
||||
delAllViews() {
|
||||
return new Promise(resolve => {
|
||||
const affixTags = this.visitedViews.filter(tag => tag.meta?.affix);
|
||||
this.visitedViews = affixTags;
|
||||
this.cachedViews = [];
|
||||
resolve({
|
||||
visitedViews: [...this.visitedViews],
|
||||
cachedViews: [...this.cachedViews]
|
||||
});
|
||||
});
|
||||
},
|
||||
delAllVisitedViews() {
|
||||
return new Promise(resolve => {
|
||||
const affixTags = this.visitedViews.filter(tag => tag.meta?.affix);
|
||||
this.visitedViews = affixTags;
|
||||
resolve([...this.visitedViews]);
|
||||
});
|
||||
},
|
||||
delAllCachedViews() {
|
||||
return new Promise(resolve => {
|
||||
this.cachedViews = [];
|
||||
resolve([...this.cachedViews]);
|
||||
});
|
||||
}
|
||||
}
|
||||
const cacheIndex = this.cachedViews.indexOf(item.name as string);
|
||||
if (cacheIndex > -1) {
|
||||
this.cachedViews.splice(cacheIndex, 1);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
resolve({
|
||||
visitedViews: [...this.visitedViews]
|
||||
});
|
||||
});
|
||||
},
|
||||
delAllViews() {
|
||||
return new Promise(resolve => {
|
||||
const affixTags = this.visitedViews.filter(tag => tag.meta?.affix);
|
||||
this.visitedViews = affixTags;
|
||||
this.cachedViews = [];
|
||||
resolve({
|
||||
visitedViews: [...this.visitedViews],
|
||||
cachedViews: [...this.cachedViews]
|
||||
});
|
||||
});
|
||||
},
|
||||
delAllVisitedViews() {
|
||||
return new Promise(resolve => {
|
||||
const affixTags = this.visitedViews.filter(tag => tag.meta?.affix);
|
||||
this.visitedViews = affixTags;
|
||||
resolve([...this.visitedViews]);
|
||||
});
|
||||
},
|
||||
delAllCachedViews() {
|
||||
return new Promise(resolve => {
|
||||
this.cachedViews = [];
|
||||
resolve([...this.cachedViews]);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
export default useTagsViewStore;
|
||||
|
|
|
|||
|
|
@ -6,103 +6,103 @@ import { getUserInfo } from '@/api/system/user';
|
|||
import { resetRouter } from '@/router';
|
||||
|
||||
const useUserStore = defineStore({
|
||||
id: 'user',
|
||||
state: (): UserState => ({
|
||||
token: localStorage.get('token') || '',
|
||||
nickname: '',
|
||||
avatar: '',
|
||||
roles: [],
|
||||
perms: []
|
||||
}),
|
||||
actions: {
|
||||
async RESET_STATE() {
|
||||
this.$reset();
|
||||
},
|
||||
/**
|
||||
* 用户登录请求
|
||||
* @param userInfo 登录用户信息
|
||||
* username: 用户名
|
||||
* password: 密码
|
||||
* code: 验证码
|
||||
* uuid: 匹配正确验证码的 key
|
||||
*/
|
||||
login(userInfo: LoginFormData) {
|
||||
const { username, password, code, uuid } = userInfo;
|
||||
return new Promise((resolve, reject) => {
|
||||
login({
|
||||
username: username.trim(),
|
||||
password: password,
|
||||
grant_type: 'captcha',
|
||||
code: code,
|
||||
uuid: uuid
|
||||
})
|
||||
.then(response => {
|
||||
const { access_token, token_type } = response.data;
|
||||
const accessToken = token_type + ' ' + access_token;
|
||||
localStorage.set('token', accessToken);
|
||||
this.token = accessToken;
|
||||
resolve(access_token);
|
||||
})
|
||||
.catch(error => {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
},
|
||||
/**
|
||||
* 获取用户信息(昵称、头像、角色集合、权限集合)
|
||||
*/
|
||||
getUserInfo() {
|
||||
return new Promise((resolve, reject) => {
|
||||
getUserInfo()
|
||||
.then(({ data }) => {
|
||||
if (!data) {
|
||||
return reject('Verification failed, please Login again.');
|
||||
}
|
||||
const { nickname, avatar, roles, perms } = data;
|
||||
if (!roles || roles.length <= 0) {
|
||||
reject('getUserInfo: roles must be a non-null array!');
|
||||
}
|
||||
this.nickname = nickname;
|
||||
this.avatar = avatar;
|
||||
this.roles = roles;
|
||||
this.perms = perms;
|
||||
resolve(data);
|
||||
})
|
||||
.catch(error => {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
},
|
||||
id: 'user',
|
||||
state: (): UserState => ({
|
||||
token: localStorage.get('token') || '',
|
||||
nickname: '',
|
||||
avatar: '',
|
||||
roles: [],
|
||||
perms: []
|
||||
}),
|
||||
actions: {
|
||||
async RESET_STATE() {
|
||||
this.$reset();
|
||||
},
|
||||
/**
|
||||
* 用户登录请求
|
||||
* @param userInfo 登录用户信息
|
||||
* username: 用户名
|
||||
* password: 密码
|
||||
* code: 验证码
|
||||
* uuid: 匹配正确验证码的 key
|
||||
*/
|
||||
login(userInfo: LoginFormData) {
|
||||
const { username, password, code, uuid } = userInfo;
|
||||
return new Promise((resolve, reject) => {
|
||||
login({
|
||||
username: username.trim(),
|
||||
password: password,
|
||||
grant_type: 'captcha',
|
||||
code: code,
|
||||
uuid: uuid
|
||||
})
|
||||
.then(response => {
|
||||
const { access_token, token_type } = response.data;
|
||||
const accessToken = token_type + ' ' + access_token;
|
||||
localStorage.set('token', accessToken);
|
||||
this.token = accessToken;
|
||||
resolve(access_token);
|
||||
})
|
||||
.catch(error => {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
},
|
||||
/**
|
||||
* 获取用户信息(昵称、头像、角色集合、权限集合)
|
||||
*/
|
||||
getUserInfo() {
|
||||
return new Promise((resolve, reject) => {
|
||||
getUserInfo()
|
||||
.then(({ data }) => {
|
||||
if (!data) {
|
||||
return reject('Verification failed, please Login again.');
|
||||
}
|
||||
const { nickname, avatar, roles, perms } = data;
|
||||
if (!roles || roles.length <= 0) {
|
||||
reject('getUserInfo: roles must be a non-null array!');
|
||||
}
|
||||
this.nickname = nickname;
|
||||
this.avatar = avatar;
|
||||
this.roles = roles;
|
||||
this.perms = perms;
|
||||
resolve(data);
|
||||
})
|
||||
.catch(error => {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 注销
|
||||
*/
|
||||
logout() {
|
||||
return new Promise((resolve, reject) => {
|
||||
logout()
|
||||
.then(() => {
|
||||
localStorage.remove('token');
|
||||
this.RESET_STATE();
|
||||
resetRouter();
|
||||
resolve(null);
|
||||
})
|
||||
.catch(error => {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
},
|
||||
/**
|
||||
* 注销
|
||||
*/
|
||||
logout() {
|
||||
return new Promise((resolve, reject) => {
|
||||
logout()
|
||||
.then(() => {
|
||||
localStorage.remove('token');
|
||||
this.RESET_STATE();
|
||||
resetRouter();
|
||||
resolve(null);
|
||||
})
|
||||
.catch(error => {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 清除 Token
|
||||
*/
|
||||
resetToken() {
|
||||
return new Promise(resolve => {
|
||||
localStorage.remove('token');
|
||||
this.RESET_STATE();
|
||||
resolve(null);
|
||||
});
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 清除 Token
|
||||
*/
|
||||
resetToken() {
|
||||
return new Promise(resolve => {
|
||||
localStorage.remove('token');
|
||||
this.RESET_STATE();
|
||||
resolve(null);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
export default useUserStore;
|
||||
|
|
|
|||
|
|
@ -1,50 +1,50 @@
|
|||
:root {
|
||||
// 这里可以设置你自定义的颜色变量
|
||||
// 这个是element主要按钮:active的颜色,当主题更改后此变量的值也随之更改
|
||||
--el-color-primary-dark: #0d84ff;
|
||||
// element plus 2.1.0 禁用文本色值和正常文本色值无法区分问题
|
||||
--el-text-color-disabled: #ccc;
|
||||
// 这里可以设置你自定义的颜色变量
|
||||
// 这个是element主要按钮:active的颜色,当主题更改后此变量的值也随之更改
|
||||
--el-color-primary-dark: #0d84ff;
|
||||
// element plus 2.1.0 禁用文本色值和正常文本色值无法区分问题
|
||||
--el-text-color-disabled: #ccc;
|
||||
}
|
||||
|
||||
// 覆盖 element-plus 的样式
|
||||
.el-breadcrumb__inner,
|
||||
.el-breadcrumb__inner a {
|
||||
font-weight: 400 !important;
|
||||
font-weight: 400 !important;
|
||||
}
|
||||
|
||||
.el-upload {
|
||||
input[type='file'] {
|
||||
display: none !important;
|
||||
}
|
||||
input[type='file'] {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
.el-upload__input {
|
||||
display: none;
|
||||
display: none;
|
||||
}
|
||||
|
||||
// dropdown
|
||||
.el-dropdown-menu {
|
||||
a {
|
||||
display: block;
|
||||
}
|
||||
a {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
// to fix el-date-picker css style
|
||||
.el-range-separator {
|
||||
box-sizing: content-box;
|
||||
box-sizing: content-box;
|
||||
}
|
||||
|
||||
// 选中行背景色值
|
||||
.el-table__body tr.current-row td {
|
||||
background-color: #e1f3d8b5 !important;
|
||||
background-color: #e1f3d8b5 !important;
|
||||
}
|
||||
|
||||
// card 的header统一高度
|
||||
.el-card__header {
|
||||
height: 60px !important;
|
||||
height: 60px !important;
|
||||
}
|
||||
|
||||
// 表格表头和表体未对齐
|
||||
.el-table__header col[name='gutter'] {
|
||||
display: table-cell !important;
|
||||
display: table-cell !important;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,64 +5,64 @@
|
|||
@import './sidebar.scss';
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
height: 100%;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
text-rendering: optimizeLegibility;
|
||||
font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB,
|
||||
Microsoft YaHei, Arial, sans-serif;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
height: 100%;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
text-rendering: optimizeLegibility;
|
||||
font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB,
|
||||
Microsoft YaHei, Arial, sans-serif;
|
||||
}
|
||||
|
||||
label {
|
||||
font-weight: 700;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
html {
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
#app {
|
||||
height: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
*,
|
||||
*:before,
|
||||
*:after {
|
||||
box-sizing: inherit;
|
||||
box-sizing: inherit;
|
||||
}
|
||||
|
||||
a:focus,
|
||||
a:active {
|
||||
outline: none;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
a,
|
||||
a:focus,
|
||||
a:hover {
|
||||
cursor: pointer;
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
div:focus {
|
||||
outline: none;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.clearfix {
|
||||
&:after {
|
||||
visibility: hidden;
|
||||
display: block;
|
||||
font-size: 0;
|
||||
content: ' ';
|
||||
clear: both;
|
||||
height: 0;
|
||||
}
|
||||
&:after {
|
||||
visibility: hidden;
|
||||
display: block;
|
||||
font-size: 0;
|
||||
content: ' ';
|
||||
clear: both;
|
||||
height: 0;
|
||||
}
|
||||
}
|
||||
|
||||
// main-container global css
|
||||
.app-container {
|
||||
padding: 20px;
|
||||
padding: 20px;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,28 +1,28 @@
|
|||
@mixin clearfix {
|
||||
&:after {
|
||||
content: '';
|
||||
display: table;
|
||||
clear: both;
|
||||
}
|
||||
&:after {
|
||||
content: '';
|
||||
display: table;
|
||||
clear: both;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin scrollBar {
|
||||
&::-webkit-scrollbar-track-piece {
|
||||
background: #d3dce6;
|
||||
}
|
||||
&::-webkit-scrollbar-track-piece {
|
||||
background: #d3dce6;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
&::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: #99a9bf;
|
||||
border-radius: 20px;
|
||||
}
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: #99a9bf;
|
||||
border-radius: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin relative {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,228 +1,228 @@
|
|||
#app {
|
||||
.main-container {
|
||||
min-height: 100%;
|
||||
transition: margin-left 0.28s;
|
||||
margin-left: $sideBarWidth;
|
||||
position: relative;
|
||||
}
|
||||
.main-container {
|
||||
min-height: 100%;
|
||||
transition: margin-left 0.28s;
|
||||
margin-left: $sideBarWidth;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.sidebar-container {
|
||||
transition: width 0.28s;
|
||||
width: $sideBarWidth !important;
|
||||
background-color: $menuBg;
|
||||
height: 100%;
|
||||
position: fixed;
|
||||
font-size: 0px;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: 1001;
|
||||
overflow: hidden;
|
||||
.sidebar-container {
|
||||
transition: width 0.28s;
|
||||
width: $sideBarWidth !important;
|
||||
background-color: $menuBg;
|
||||
height: 100%;
|
||||
position: fixed;
|
||||
font-size: 0px;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: 1001;
|
||||
overflow: hidden;
|
||||
|
||||
// reset element-ui css
|
||||
.horizontal-collapse-transition {
|
||||
transition: 0s width ease-in-out, 0s padding-left ease-in-out,
|
||||
0s padding-right ease-in-out;
|
||||
}
|
||||
// reset element-ui css
|
||||
.horizontal-collapse-transition {
|
||||
transition: 0s width ease-in-out, 0s padding-left ease-in-out,
|
||||
0s padding-right ease-in-out;
|
||||
}
|
||||
|
||||
.scrollbar-wrapper {
|
||||
overflow-x: hidden !important;
|
||||
}
|
||||
.scrollbar-wrapper {
|
||||
overflow-x: hidden !important;
|
||||
}
|
||||
|
||||
.el-scrollbar__bar.is-vertical {
|
||||
right: 0px;
|
||||
}
|
||||
.el-scrollbar__bar.is-vertical {
|
||||
right: 0px;
|
||||
}
|
||||
|
||||
.el-scrollbar {
|
||||
height: 100%;
|
||||
}
|
||||
.el-scrollbar {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
&.has-logo {
|
||||
.el-scrollbar {
|
||||
height: calc(100% - 50px);
|
||||
}
|
||||
}
|
||||
&.has-logo {
|
||||
.el-scrollbar {
|
||||
height: calc(100% - 50px);
|
||||
}
|
||||
}
|
||||
|
||||
.is-horizontal {
|
||||
display: none;
|
||||
}
|
||||
.is-horizontal {
|
||||
display: none;
|
||||
}
|
||||
|
||||
a {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
a {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.svg-icon {
|
||||
margin-right: 16px;
|
||||
}
|
||||
.svg-icon {
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
.sub-el-icon {
|
||||
margin-right: 12px;
|
||||
margin-left: -2px;
|
||||
}
|
||||
.sub-el-icon {
|
||||
margin-right: 12px;
|
||||
margin-left: -2px;
|
||||
}
|
||||
|
||||
.el-menu {
|
||||
border: none;
|
||||
height: 100%;
|
||||
width: 100% !important;
|
||||
}
|
||||
.el-menu {
|
||||
border: none;
|
||||
height: 100%;
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
// menu hover
|
||||
.submenu-title-noDropdown,
|
||||
.el-sub-menu__title {
|
||||
&:hover {
|
||||
background-color: $menuHover !important;
|
||||
}
|
||||
}
|
||||
// menu hover
|
||||
.submenu-title-noDropdown,
|
||||
.el-sub-menu__title {
|
||||
&:hover {
|
||||
background-color: $menuHover !important;
|
||||
}
|
||||
}
|
||||
|
||||
.is-active > .el-sub-menu__title {
|
||||
color: $subMenuActiveText !important;
|
||||
}
|
||||
.is-active > .el-sub-menu__title {
|
||||
color: $subMenuActiveText !important;
|
||||
}
|
||||
|
||||
& .nest-menu .el-sub-menu > .el-sub-menu__title,
|
||||
& .el-sub-menu .el-menu-item {
|
||||
min-width: $sideBarWidth !important;
|
||||
background-color: $subMenuBg !important;
|
||||
& .nest-menu .el-sub-menu > .el-sub-menu__title,
|
||||
& .el-sub-menu .el-menu-item {
|
||||
min-width: $sideBarWidth !important;
|
||||
background-color: $subMenuBg !important;
|
||||
|
||||
&:hover {
|
||||
background-color: $subMenuHover !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
&:hover {
|
||||
background-color: $subMenuHover !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.hideSidebar {
|
||||
.sidebar-container {
|
||||
width: 54px !important;
|
||||
.svg-icon {
|
||||
margin-right: 0px;
|
||||
}
|
||||
}
|
||||
.hideSidebar {
|
||||
.sidebar-container {
|
||||
width: 54px !important;
|
||||
.svg-icon {
|
||||
margin-right: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
.main-container {
|
||||
margin-left: 54px;
|
||||
}
|
||||
.main-container {
|
||||
margin-left: 54px;
|
||||
}
|
||||
|
||||
.submenu-title-noDropdown {
|
||||
padding: 0 !important;
|
||||
position: relative;
|
||||
.submenu-title-noDropdown {
|
||||
padding: 0 !important;
|
||||
position: relative;
|
||||
|
||||
.el-tooltip {
|
||||
padding: 0 !important;
|
||||
.el-tooltip {
|
||||
padding: 0 !important;
|
||||
|
||||
.svg-icon {
|
||||
margin-left: 20px;
|
||||
}
|
||||
.svg-icon {
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
.sub-el-icon {
|
||||
margin-left: 19px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.sub-el-icon {
|
||||
margin-left: 19px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.el-sub-menu {
|
||||
overflow: hidden;
|
||||
.el-sub-menu {
|
||||
overflow: hidden;
|
||||
|
||||
& > .el-sub-menu__title {
|
||||
padding: 0 !important;
|
||||
& > .el-sub-menu__title {
|
||||
padding: 0 !important;
|
||||
|
||||
.svg-icon {
|
||||
margin-left: 20px;
|
||||
}
|
||||
.svg-icon {
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
.sub-el-icon {
|
||||
margin-left: 19px;
|
||||
}
|
||||
.sub-el-icon {
|
||||
margin-left: 19px;
|
||||
}
|
||||
|
||||
.el-sub-menu__icon-arrow {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
.el-sub-menu__icon-arrow {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.el-menu--collapse {
|
||||
.el-sub-menu {
|
||||
& > .el-sub-menu__title {
|
||||
& > span {
|
||||
height: 0;
|
||||
width: 0;
|
||||
overflow: hidden;
|
||||
visibility: hidden;
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.el-menu--collapse {
|
||||
.el-sub-menu {
|
||||
& > .el-sub-menu__title {
|
||||
& > span {
|
||||
height: 0;
|
||||
width: 0;
|
||||
overflow: hidden;
|
||||
visibility: hidden;
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.el-menu--collapse .el-menu .el-sub-menu {
|
||||
min-width: $sideBarWidth !important;
|
||||
}
|
||||
.el-menu--collapse .el-menu .el-sub-menu {
|
||||
min-width: $sideBarWidth !important;
|
||||
}
|
||||
|
||||
// mobile responsive
|
||||
.mobile {
|
||||
.main-container {
|
||||
margin-left: 0px;
|
||||
}
|
||||
// mobile responsive
|
||||
.mobile {
|
||||
.main-container {
|
||||
margin-left: 0px;
|
||||
}
|
||||
|
||||
.sidebar-container {
|
||||
transition: transform 0.28s;
|
||||
width: $sideBarWidth !important;
|
||||
}
|
||||
.sidebar-container {
|
||||
transition: transform 0.28s;
|
||||
width: $sideBarWidth !important;
|
||||
}
|
||||
|
||||
&.hideSidebar {
|
||||
.sidebar-container {
|
||||
pointer-events: none;
|
||||
transition-duration: 0.3s;
|
||||
transform: translate3d(-$sideBarWidth, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
&.hideSidebar {
|
||||
.sidebar-container {
|
||||
pointer-events: none;
|
||||
transition-duration: 0.3s;
|
||||
transform: translate3d(-$sideBarWidth, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.withoutAnimation {
|
||||
.main-container,
|
||||
.sidebar-container {
|
||||
transition: none;
|
||||
}
|
||||
}
|
||||
.withoutAnimation {
|
||||
.main-container,
|
||||
.sidebar-container {
|
||||
transition: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// when menu collapsed
|
||||
.el-menu--vertical {
|
||||
& > .el-menu {
|
||||
.svg-icon {
|
||||
margin-right: 16px;
|
||||
}
|
||||
.sub-el-icon {
|
||||
margin-right: 12px;
|
||||
margin-left: -2px;
|
||||
}
|
||||
}
|
||||
& > .el-menu {
|
||||
.svg-icon {
|
||||
margin-right: 16px;
|
||||
}
|
||||
.sub-el-icon {
|
||||
margin-right: 12px;
|
||||
margin-left: -2px;
|
||||
}
|
||||
}
|
||||
|
||||
.nest-menu .el-sub-menu > .el-sub-menu__title,
|
||||
.el-menu-item {
|
||||
&:hover {
|
||||
// you can use $subMenuHover
|
||||
background-color: $menuHover !important;
|
||||
}
|
||||
}
|
||||
.nest-menu .el-sub-menu > .el-sub-menu__title,
|
||||
.el-menu-item {
|
||||
&:hover {
|
||||
// you can use $subMenuHover
|
||||
background-color: $menuHover !important;
|
||||
}
|
||||
}
|
||||
|
||||
// the scroll bar appears when the subMenu is too long
|
||||
> .el-menu--popup {
|
||||
max-height: 100vh;
|
||||
overflow-y: auto;
|
||||
// the scroll bar appears when the subMenu is too long
|
||||
> .el-menu--popup {
|
||||
max-height: 100vh;
|
||||
overflow-y: auto;
|
||||
|
||||
&::-webkit-scrollbar-track-piece {
|
||||
background: #d3dce6;
|
||||
}
|
||||
&::-webkit-scrollbar-track-piece {
|
||||
background: #d3dce6;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
&::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: #99a9bf;
|
||||
border-radius: 20px;
|
||||
}
|
||||
}
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: #99a9bf;
|
||||
border-radius: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,46 +3,46 @@
|
|||
/* fade */
|
||||
.fade-enter-active,
|
||||
.fade-leave-active {
|
||||
transition: opacity 0.28s;
|
||||
transition: opacity 0.28s;
|
||||
}
|
||||
|
||||
.fade-enter,
|
||||
.fade-leave-active {
|
||||
opacity: 0;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
/* fade-transform */
|
||||
.fade-transform-leave-active,
|
||||
.fade-transform-enter-active {
|
||||
transition: all 0.5s;
|
||||
transition: all 0.5s;
|
||||
}
|
||||
|
||||
.fade-transform-enter {
|
||||
opacity: 0;
|
||||
transform: translateX(-30px);
|
||||
opacity: 0;
|
||||
transform: translateX(-30px);
|
||||
}
|
||||
|
||||
.fade-transform-leave-to {
|
||||
opacity: 0;
|
||||
transform: translateX(30px);
|
||||
opacity: 0;
|
||||
transform: translateX(30px);
|
||||
}
|
||||
|
||||
/* breadcrumb transition */
|
||||
.breadcrumb-enter-active,
|
||||
.breadcrumb-leave-active {
|
||||
transition: all 0.5s;
|
||||
transition: all 0.5s;
|
||||
}
|
||||
|
||||
.breadcrumb-enter,
|
||||
.breadcrumb-leave-active {
|
||||
opacity: 0;
|
||||
transform: translateX(20px);
|
||||
opacity: 0;
|
||||
transform: translateX(20px);
|
||||
}
|
||||
|
||||
.breadcrumb-move {
|
||||
transition: all 0.5s;
|
||||
transition: all 0.5s;
|
||||
}
|
||||
|
||||
.breadcrumb-leave-active {
|
||||
position: absolute;
|
||||
position: absolute;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,12 +14,12 @@ $sideBarWidth: 210px;
|
|||
// the :export directive is the magic sauce for webpack
|
||||
// https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass
|
||||
:export {
|
||||
menuText: $menuText;
|
||||
menuActiveText: $menuActiveText;
|
||||
subMenuActiveText: $subMenuActiveText;
|
||||
menuBg: $menuBg;
|
||||
menuHover: $menuHover;
|
||||
subMenuBg: $subMenuBg;
|
||||
subMenuHover: $subMenuHover;
|
||||
sideBarWidth: $sideBarWidth;
|
||||
menuText: $menuText;
|
||||
menuActiveText: $menuActiveText;
|
||||
subMenuActiveText: $subMenuActiveText;
|
||||
menuBg: $menuBg;
|
||||
menuHover: $menuHover;
|
||||
subMenuBg: $subMenuBg;
|
||||
subMenuHover: $subMenuHover;
|
||||
sideBarWidth: $sideBarWidth;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
export interface PageQueryParam {
|
||||
pageNum: number;
|
||||
pageSize: number;
|
||||
pageNum: number;
|
||||
pageSize: number;
|
||||
}
|
||||
|
||||
export interface PageResult<T> {
|
||||
list: T;
|
||||
total: number;
|
||||
list: T;
|
||||
total: number;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@
|
|||
* Seata表单类型声明
|
||||
*/
|
||||
export interface SeataFormData {
|
||||
openTx: boolean;
|
||||
stockEx: boolean;
|
||||
accountEx: boolean;
|
||||
orderEx: boolean;
|
||||
openTx: boolean;
|
||||
stockEx: boolean;
|
||||
accountEx: boolean;
|
||||
orderEx: boolean;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,35 +4,35 @@ import { PageQueryParam, PageResult } from '../base';
|
|||
* 订单查询参数类型声明
|
||||
*/
|
||||
export interface OrderQueryParam extends PageQueryParam {
|
||||
orderSn: string | undefined;
|
||||
status: number | undefined;
|
||||
orderSn: string | undefined;
|
||||
status: number | undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* 订单分页列表项声明
|
||||
*/
|
||||
export interface Order {
|
||||
id: string;
|
||||
orderSn: string;
|
||||
totalAmount: string;
|
||||
payAmount: string;
|
||||
payType: number;
|
||||
status: number;
|
||||
totalQuantity: number;
|
||||
gmtCreate: string;
|
||||
memberId: string;
|
||||
sourceType: number;
|
||||
orderItems: OrderItem[];
|
||||
id: string;
|
||||
orderSn: string;
|
||||
totalAmount: string;
|
||||
payAmount: string;
|
||||
payType: number;
|
||||
status: number;
|
||||
totalQuantity: number;
|
||||
gmtCreate: string;
|
||||
memberId: string;
|
||||
sourceType: number;
|
||||
orderItems: OrderItem[];
|
||||
}
|
||||
export interface OrderItem {
|
||||
id: string;
|
||||
orderId: string;
|
||||
skuId: string;
|
||||
skuName: string;
|
||||
picUrl: string;
|
||||
price: string;
|
||||
count: number;
|
||||
totalAmount: number;
|
||||
id: string;
|
||||
orderId: string;
|
||||
skuId: string;
|
||||
skuName: string;
|
||||
picUrl: string;
|
||||
price: string;
|
||||
count: number;
|
||||
totalAmount: number;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -44,13 +44,13 @@ export type OrderPageResult = PageResult<Order[]>;
|
|||
* 订单表单类型声明
|
||||
*/
|
||||
export interface OrderDetail {
|
||||
id: number | undefined;
|
||||
title: string;
|
||||
picUrl: string;
|
||||
beginTime: string;
|
||||
endTime: string;
|
||||
status: number;
|
||||
sort: number;
|
||||
url: string;
|
||||
remark: string;
|
||||
id: number | undefined;
|
||||
title: string;
|
||||
picUrl: string;
|
||||
beginTime: string;
|
||||
endTime: string;
|
||||
status: number;
|
||||
sort: number;
|
||||
url: string;
|
||||
remark: string;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,17 +4,17 @@ import { PageQueryParam, PageResult } from '../base';
|
|||
* 品牌查询参数类型声明
|
||||
*/
|
||||
export interface BrandQueryParam extends PageQueryParam {
|
||||
name?: string;
|
||||
name?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 品牌分页列表项声明
|
||||
*/
|
||||
export interface BrandItem {
|
||||
id: string;
|
||||
name: string;
|
||||
logoUrl: string;
|
||||
sort: number;
|
||||
id: string;
|
||||
name: string;
|
||||
logoUrl: string;
|
||||
sort: number;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -26,8 +26,8 @@ export type BrandPageResult = PageResult<BrandItem[]>;
|
|||
* 品牌表单类型声明
|
||||
*/
|
||||
export interface BrandFormData {
|
||||
id: number | undefined;
|
||||
name: string;
|
||||
logoUrl: string;
|
||||
sort: number;
|
||||
id: number | undefined;
|
||||
name: string;
|
||||
logoUrl: string;
|
||||
sort: number;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,45 +4,45 @@ import { PageQueryParam, PageResult } from '../base';
|
|||
* 商品查询参数类型声明
|
||||
*/
|
||||
export interface GoodsQueryParam extends PageQueryParam {
|
||||
name?: stirng;
|
||||
categoryId?: number;
|
||||
name?: stirng;
|
||||
categoryId?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 商品列表项类型声明
|
||||
*/
|
||||
export interface GoodsItem {
|
||||
id: string;
|
||||
name: string;
|
||||
categoryId?: any;
|
||||
brandId?: any;
|
||||
originPrice: string;
|
||||
price: string;
|
||||
sales: number;
|
||||
picUrl?: any;
|
||||
album?: any;
|
||||
unit?: any;
|
||||
description: string;
|
||||
detail: string;
|
||||
status?: any;
|
||||
categoryName: string;
|
||||
brandName: string;
|
||||
skuList: SkuItem[];
|
||||
id: string;
|
||||
name: string;
|
||||
categoryId?: any;
|
||||
brandId?: any;
|
||||
originPrice: string;
|
||||
price: string;
|
||||
sales: number;
|
||||
picUrl?: any;
|
||||
album?: any;
|
||||
unit?: any;
|
||||
description: string;
|
||||
detail: string;
|
||||
status?: any;
|
||||
categoryName: string;
|
||||
brandName: string;
|
||||
skuList: SkuItem[];
|
||||
}
|
||||
|
||||
/**
|
||||
* 商品规格项类型声明
|
||||
*/
|
||||
export interface SkuItem {
|
||||
id: string;
|
||||
skuSn?: any;
|
||||
name: string;
|
||||
spuId?: any;
|
||||
specIds: string;
|
||||
price: string;
|
||||
stockNum: number;
|
||||
lockedStockNum?: any;
|
||||
picUrl?: any;
|
||||
id: string;
|
||||
skuSn?: any;
|
||||
name: string;
|
||||
spuId?: any;
|
||||
specIds: string;
|
||||
price: string;
|
||||
stockNum: number;
|
||||
lockedStockNum?: any;
|
||||
picUrl?: any;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -54,17 +54,17 @@ export type GoodsPageResult = PageResult<GoodsItem[]>;
|
|||
* 商品表单数据类型声明
|
||||
*/
|
||||
export interface GoodsDetail {
|
||||
id?: string;
|
||||
name?: string;
|
||||
categoryId?: string;
|
||||
brandId?: string;
|
||||
originPrice?: number;
|
||||
price?: number;
|
||||
picUrl?: string;
|
||||
album: string[];
|
||||
description?: string;
|
||||
detail?: string;
|
||||
attrList: any[];
|
||||
specList: any[];
|
||||
skuList: any[];
|
||||
id?: string;
|
||||
name?: string;
|
||||
categoryId?: string;
|
||||
brandId?: string;
|
||||
originPrice?: number;
|
||||
price?: number;
|
||||
picUrl?: string;
|
||||
album: string[];
|
||||
description?: string;
|
||||
detail?: string;
|
||||
attrList: any[];
|
||||
specList: any[];
|
||||
skuList: any[];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,17 +4,17 @@ import { PageQueryParam, PageResult } from '../base';
|
|||
* 广告查询参数类型声明
|
||||
*/
|
||||
export interface AdvertQueryParam extends PageQueryParam {
|
||||
title?: string;
|
||||
title?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 广告分页列表项声明
|
||||
*/
|
||||
export interface AdvertItem {
|
||||
id: string;
|
||||
name: string;
|
||||
logoUrl: string;
|
||||
sort: number;
|
||||
id: string;
|
||||
name: string;
|
||||
logoUrl: string;
|
||||
sort: number;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -26,13 +26,13 @@ export type AdvertPageResult = PageResult<AdvertItem[]>;
|
|||
* 广告表单类型声明
|
||||
*/
|
||||
export interface AdvertFormData {
|
||||
id?: number;
|
||||
title: string;
|
||||
picUrl: string;
|
||||
beginTime: string;
|
||||
endTime: string;
|
||||
status: number;
|
||||
sort: number;
|
||||
url: string;
|
||||
remark: string;
|
||||
id?: number;
|
||||
title: string;
|
||||
picUrl: string;
|
||||
beginTime: string;
|
||||
endTime: string;
|
||||
status: number;
|
||||
sort: number;
|
||||
url: string;
|
||||
remark: string;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,27 +4,27 @@ import { PageQueryParam, PageResult } from '../base';
|
|||
* 客户端查询参数类型声明
|
||||
*/
|
||||
export interface ClientQueryParam extends PageQueryParam {
|
||||
/**
|
||||
* 客户端名称
|
||||
*/
|
||||
clientId: string | undefined;
|
||||
/**
|
||||
* 客户端名称
|
||||
*/
|
||||
clientId: string | undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* 客户端分页列表项声明
|
||||
*/
|
||||
export interface ClientItem {
|
||||
clientId: string;
|
||||
clientSecret: string;
|
||||
resourceIds: string;
|
||||
scope: string;
|
||||
authorizedGrantTypes: string;
|
||||
webServerRedirectUri?: any;
|
||||
authorities?: any;
|
||||
accessTokenValidity: number;
|
||||
refreshTokenValidity: number;
|
||||
additionalInformation?: any;
|
||||
autoapprove: string;
|
||||
clientId: string;
|
||||
clientSecret: string;
|
||||
resourceIds: string;
|
||||
scope: string;
|
||||
authorizedGrantTypes: string;
|
||||
webServerRedirectUri?: any;
|
||||
authorities?: any;
|
||||
accessTokenValidity: number;
|
||||
refreshTokenValidity: number;
|
||||
additionalInformation?: any;
|
||||
autoapprove: string;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -36,14 +36,14 @@ export type ClientPageResult = PageResult<ClientItem[]>;
|
|||
* 客户端表单类型声明
|
||||
*/
|
||||
export interface ClientFormData {
|
||||
authorizedGrantTypes: string;
|
||||
clientId: string;
|
||||
clientSecret: string;
|
||||
accessTokenValidity: string;
|
||||
refreshTokenValidity: string;
|
||||
webServerRedirectUri: string;
|
||||
authorities: string;
|
||||
additionalInformation: string;
|
||||
autoapprove: string;
|
||||
scope: string;
|
||||
authorizedGrantTypes: string;
|
||||
clientId: string;
|
||||
clientSecret: string;
|
||||
accessTokenValidity: string;
|
||||
refreshTokenValidity: string;
|
||||
webServerRedirectUri: string;
|
||||
authorities: string;
|
||||
additionalInformation: string;
|
||||
autoapprove: string;
|
||||
scope: string;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@
|
|||
* 部门查询参数类型声明
|
||||
*/
|
||||
export interface DeptQueryParam {
|
||||
name: string | undefined;
|
||||
status: number | undefined;
|
||||
name: string | undefined;
|
||||
status: number | undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -11,25 +11,25 @@ export interface DeptQueryParam {
|
|||
*/
|
||||
|
||||
export interface DeptItem {
|
||||
id: string;
|
||||
name: string;
|
||||
parentId: string;
|
||||
treePath: string;
|
||||
sort: number;
|
||||
status: number;
|
||||
leader?: string;
|
||||
mobile?: string;
|
||||
email?: string;
|
||||
children: DeptItem[];
|
||||
id: string;
|
||||
name: string;
|
||||
parentId: string;
|
||||
treePath: string;
|
||||
sort: number;
|
||||
status: number;
|
||||
leader?: string;
|
||||
mobile?: string;
|
||||
email?: string;
|
||||
children: DeptItem[];
|
||||
}
|
||||
|
||||
/**
|
||||
* 部门表单类型声明
|
||||
*/
|
||||
export interface DeptFormData {
|
||||
id?: string;
|
||||
parentId: string;
|
||||
name: string;
|
||||
sort: number;
|
||||
status: number;
|
||||
id?: string;
|
||||
parentId: string;
|
||||
name: string;
|
||||
sort: number;
|
||||
status: number;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,21 +4,21 @@ import { PageQueryParam, PageResult } from '../base';
|
|||
* 字典查询参数类型声明
|
||||
*/
|
||||
export interface DictQueryParam extends PageQueryParam {
|
||||
/**
|
||||
* 字典名称
|
||||
*/
|
||||
name: string | undefined;
|
||||
/**
|
||||
* 字典名称
|
||||
*/
|
||||
name: string | undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* 字典分页列表项声明
|
||||
*/
|
||||
export interface Dict {
|
||||
id: number;
|
||||
code: string;
|
||||
name: string;
|
||||
status: number;
|
||||
remark: string;
|
||||
id: number;
|
||||
code: string;
|
||||
name: string;
|
||||
status: number;
|
||||
remark: string;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -30,39 +30,39 @@ export type DictPageResult = PageResult<Dict[]>;
|
|||
* 字典表单类型声明
|
||||
*/
|
||||
export interface DictFormData {
|
||||
id: number | undefined;
|
||||
name: string;
|
||||
code: string;
|
||||
status: number;
|
||||
remark: string;
|
||||
id: number | undefined;
|
||||
name: string;
|
||||
code: string;
|
||||
status: number;
|
||||
remark: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 字典项查询参数类型声明
|
||||
*/
|
||||
export interface DictItemQueryParam extends PageQueryParam {
|
||||
/**
|
||||
* 字典项名称
|
||||
*/
|
||||
name: string | undefined;
|
||||
/**
|
||||
* 字典编码
|
||||
*/
|
||||
dictCode: string | undefined;
|
||||
/**
|
||||
* 字典项名称
|
||||
*/
|
||||
name: string | undefined;
|
||||
/**
|
||||
* 字典编码
|
||||
*/
|
||||
dictCode: string | undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* 字典分页列表项声明
|
||||
*/
|
||||
export interface DictItem {
|
||||
id: number;
|
||||
name: string;
|
||||
value: string;
|
||||
dictCode: string;
|
||||
sort: number;
|
||||
status: number;
|
||||
defaulted: number;
|
||||
remark?: string;
|
||||
id: number;
|
||||
name: string;
|
||||
value: string;
|
||||
dictCode: string;
|
||||
sort: number;
|
||||
status: number;
|
||||
defaulted: number;
|
||||
remark?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -74,13 +74,13 @@ export type DictItemPageResult = PageResult<DictItem[]>;
|
|||
* 字典表单类型声明
|
||||
*/
|
||||
export interface DictItemFormData {
|
||||
id?: number;
|
||||
dictCode?: string;
|
||||
dictName?: string;
|
||||
name: string;
|
||||
code: string;
|
||||
value: string;
|
||||
status: number;
|
||||
sort: number;
|
||||
remark: string;
|
||||
id?: number;
|
||||
dictCode?: string;
|
||||
dictName?: string;
|
||||
name: string;
|
||||
code: string;
|
||||
value: string;
|
||||
status: number;
|
||||
sort: number;
|
||||
remark: string;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,25 +2,25 @@
|
|||
* 登录表单类型声明
|
||||
*/
|
||||
export interface LoginFormData {
|
||||
username: string;
|
||||
password: string;
|
||||
grant_type: string;
|
||||
code: string;
|
||||
uuid: string;
|
||||
username: string;
|
||||
password: string;
|
||||
grant_type: string;
|
||||
code: string;
|
||||
uuid: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 登录响应类型声明
|
||||
*/
|
||||
export interface LoginResponseData {
|
||||
access_token: string;
|
||||
token_type: string;
|
||||
access_token: string;
|
||||
token_type: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证码类型声明
|
||||
*/
|
||||
export interface Captcha {
|
||||
img: string;
|
||||
uuid: string;
|
||||
img: string;
|
||||
uuid: string;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
* 菜单查询参数类型声明
|
||||
*/
|
||||
export interface MenuQueryParam {
|
||||
name?: string;
|
||||
name?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -10,58 +10,58 @@ export interface MenuQueryParam {
|
|||
*/
|
||||
|
||||
export interface MenuItem {
|
||||
id: number;
|
||||
parentId: number;
|
||||
gmtCreate: string;
|
||||
gmtModified: string;
|
||||
name: string;
|
||||
icon: string;
|
||||
component: string;
|
||||
sort: number;
|
||||
visible: number;
|
||||
children: MenuItem[];
|
||||
id: number;
|
||||
parentId: number;
|
||||
gmtCreate: string;
|
||||
gmtModified: string;
|
||||
name: string;
|
||||
icon: string;
|
||||
component: string;
|
||||
sort: number;
|
||||
visible: number;
|
||||
children: MenuItem[];
|
||||
}
|
||||
|
||||
/**
|
||||
* 菜单表单类型声明
|
||||
*/
|
||||
export interface MenuFormData {
|
||||
/**
|
||||
* 菜单ID
|
||||
*/
|
||||
id?: string;
|
||||
/**
|
||||
* 父菜单ID
|
||||
*/
|
||||
parentId: string;
|
||||
/**
|
||||
* 菜单名称
|
||||
*/
|
||||
name: string;
|
||||
/**
|
||||
* 菜单是否可见(1:是;0:否;)
|
||||
*/
|
||||
visible: number;
|
||||
icon?: string;
|
||||
/**
|
||||
* 排序
|
||||
*/
|
||||
sort: number;
|
||||
/**
|
||||
* 组件路径
|
||||
*/
|
||||
component?: string;
|
||||
/**
|
||||
* 路由路径
|
||||
*/
|
||||
path: string;
|
||||
/**
|
||||
* 跳转路由路径
|
||||
*/
|
||||
redirect?: string;
|
||||
/**
|
||||
* 菜单ID
|
||||
*/
|
||||
id?: string;
|
||||
/**
|
||||
* 父菜单ID
|
||||
*/
|
||||
parentId: string;
|
||||
/**
|
||||
* 菜单名称
|
||||
*/
|
||||
name: string;
|
||||
/**
|
||||
* 菜单是否可见(1:是;0:否;)
|
||||
*/
|
||||
visible: number;
|
||||
icon?: string;
|
||||
/**
|
||||
* 排序
|
||||
*/
|
||||
sort: number;
|
||||
/**
|
||||
* 组件路径
|
||||
*/
|
||||
component?: string;
|
||||
/**
|
||||
* 路由路径
|
||||
*/
|
||||
path: string;
|
||||
/**
|
||||
* 跳转路由路径
|
||||
*/
|
||||
redirect?: string;
|
||||
|
||||
/**
|
||||
* 菜单类型(1:菜单;2:目录;3:外链)
|
||||
*/
|
||||
type: string;
|
||||
/**
|
||||
* 菜单类型(1:菜单;2:目录;3:外链)
|
||||
*/
|
||||
type: string;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,20 +4,20 @@ import { PageQueryParam, PageResult } from '../base';
|
|||
* 权限查询参数类型声明
|
||||
*/
|
||||
export interface PermQueryParam extends PageQueryParam {
|
||||
menuId: any;
|
||||
name: string | undefined;
|
||||
menuId: any;
|
||||
name: string | undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* 权限分页列表项声明
|
||||
*/
|
||||
export interface PermItem {
|
||||
id: number;
|
||||
name: string;
|
||||
menuId: string;
|
||||
urlPerm: string;
|
||||
btnPerm: string;
|
||||
roles?: string[];
|
||||
id: number;
|
||||
name: string;
|
||||
menuId: string;
|
||||
urlPerm: string;
|
||||
btnPerm: string;
|
||||
roles?: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -29,9 +29,9 @@ export type PermPageResult = PageResult<PermItem[]>;
|
|||
* 权限表单类型声明
|
||||
*/
|
||||
export interface PermFormData {
|
||||
id: number | undefined;
|
||||
name: string;
|
||||
urlPerm: string;
|
||||
btnPerm: string;
|
||||
menuId: string;
|
||||
id: number | undefined;
|
||||
name: string;
|
||||
urlPerm: string;
|
||||
btnPerm: string;
|
||||
menuId: string;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,21 +4,21 @@ import { PageQueryParam, PageResult } from '../base';
|
|||
* 角色查询参数类型声明
|
||||
*/
|
||||
export interface RoleQueryParam extends PageQueryParam {
|
||||
name?: string;
|
||||
name?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 角色分页列表项声明
|
||||
*/
|
||||
export interface RoleItem {
|
||||
id: string;
|
||||
name: string;
|
||||
code: string;
|
||||
sort: number;
|
||||
status: number;
|
||||
deleted: number;
|
||||
menuIds?: any;
|
||||
permissionIds?: any;
|
||||
id: string;
|
||||
name: string;
|
||||
code: string;
|
||||
sort: number;
|
||||
status: number;
|
||||
deleted: number;
|
||||
menuIds?: any;
|
||||
permissionIds?: any;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -30,9 +30,9 @@ export type RolePageResult = PageResult<RoleItem[]>;
|
|||
* 角色表单类型声明
|
||||
*/
|
||||
export interface RoleFormData {
|
||||
id: number | undefined;
|
||||
name: string;
|
||||
code: string;
|
||||
sort: number;
|
||||
status: number;
|
||||
id: number | undefined;
|
||||
name: string;
|
||||
code: string;
|
||||
sort: number;
|
||||
status: number;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,36 +4,36 @@ import { PageQueryParam, PageResult } from '../base';
|
|||
* 登录用户类型声明
|
||||
*/
|
||||
export interface UserInfo {
|
||||
nickname: string;
|
||||
avatar: string;
|
||||
roles: string[];
|
||||
perms: string[];
|
||||
nickname: string;
|
||||
avatar: string;
|
||||
roles: string[];
|
||||
perms: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户查询参数类型声明
|
||||
*/
|
||||
export interface UserQueryParam extends PageQueryParam {
|
||||
keywords: string;
|
||||
status: number;
|
||||
deptId: number;
|
||||
keywords: string;
|
||||
status: number;
|
||||
deptId: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户分页列表项声明
|
||||
*/
|
||||
export interface UserItem {
|
||||
id: string;
|
||||
username: string;
|
||||
nickname: string;
|
||||
mobile: string;
|
||||
gender: number;
|
||||
avatar: string;
|
||||
email: string;
|
||||
status: number;
|
||||
deptName: string;
|
||||
roleNames: string;
|
||||
gmtCreate: string;
|
||||
id: string;
|
||||
username: string;
|
||||
nickname: string;
|
||||
mobile: string;
|
||||
gender: number;
|
||||
avatar: string;
|
||||
email: string;
|
||||
status: number;
|
||||
deptName: string;
|
||||
roleNames: string;
|
||||
gmtCreate: string;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -45,23 +45,23 @@ export type UserPageResult = PageResult<UserItem[]>;
|
|||
* 用户表单类型声明
|
||||
*/
|
||||
export interface UserFormData {
|
||||
id: number | undefined;
|
||||
deptId: number;
|
||||
username: string;
|
||||
nickname: string;
|
||||
password: string;
|
||||
mobile: string;
|
||||
email: string;
|
||||
gender: number;
|
||||
status: number;
|
||||
remark: string;
|
||||
roleIds: number[];
|
||||
id: number | undefined;
|
||||
deptId: number;
|
||||
username: string;
|
||||
nickname: string;
|
||||
password: string;
|
||||
mobile: string;
|
||||
email: string;
|
||||
gender: number;
|
||||
status: number;
|
||||
remark: string;
|
||||
roleIds: number[];
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户导入表单类型声明
|
||||
*/
|
||||
export interface UserImportFormData {
|
||||
deptId: number;
|
||||
roleIds: number[];
|
||||
deptId: number;
|
||||
roleIds: number[];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,43 +4,43 @@ import { PageQueryParam, PageResult } from '../base';
|
|||
* 会员查询参数类型声明
|
||||
*/
|
||||
export interface MemberQueryParam extends PageQueryParam {
|
||||
nickName?: string;
|
||||
nickName?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 会员分页列表项声明
|
||||
*/
|
||||
export interface MemberItem {
|
||||
id: string;
|
||||
gender: number;
|
||||
nickName: string;
|
||||
mobile: string;
|
||||
birthday?: any;
|
||||
avatarUrl: string;
|
||||
openid: string;
|
||||
sessionKey?: any;
|
||||
city: string;
|
||||
country: string;
|
||||
language: string;
|
||||
province: string;
|
||||
status: number;
|
||||
balance: string;
|
||||
deleted: number;
|
||||
point: number;
|
||||
addressList: AddressItem[];
|
||||
id: string;
|
||||
gender: number;
|
||||
nickName: string;
|
||||
mobile: string;
|
||||
birthday?: any;
|
||||
avatarUrl: string;
|
||||
openid: string;
|
||||
sessionKey?: any;
|
||||
city: string;
|
||||
country: string;
|
||||
language: string;
|
||||
province: string;
|
||||
status: number;
|
||||
balance: string;
|
||||
deleted: number;
|
||||
point: number;
|
||||
addressList: AddressItem[];
|
||||
}
|
||||
|
||||
export interface AddressItem {
|
||||
id: string;
|
||||
memberId: string;
|
||||
consigneeName: string;
|
||||
consigneeMobile: string;
|
||||
province: string;
|
||||
city: string;
|
||||
area: string;
|
||||
detailAddress: string;
|
||||
zipCode?: any;
|
||||
defaulted: number;
|
||||
id: string;
|
||||
memberId: string;
|
||||
consigneeName: string;
|
||||
consigneeMobile: string;
|
||||
province: string;
|
||||
city: string;
|
||||
area: string;
|
||||
detailAddress: string;
|
||||
zipCode?: any;
|
||||
defaulted: number;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -52,13 +52,13 @@ export type MemberPageResult = PageResult<MemberItem[]>;
|
|||
* 会员表单类型声明
|
||||
*/
|
||||
export interface MemberFormData {
|
||||
id: number | undefined;
|
||||
title: string;
|
||||
picUrl: string;
|
||||
beginTime: string;
|
||||
endTime: string;
|
||||
status: number;
|
||||
sort: number;
|
||||
url: string;
|
||||
remark: string;
|
||||
id: number | undefined;
|
||||
title: string;
|
||||
picUrl: string;
|
||||
beginTime: string;
|
||||
endTime: string;
|
||||
status: number;
|
||||
sort: number;
|
||||
url: string;
|
||||
remark: string;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,15 +6,15 @@
|
|||
* 弹窗属性类型声明
|
||||
*/
|
||||
export interface Dialog {
|
||||
title: string;
|
||||
visible: boolean;
|
||||
title: string;
|
||||
visible: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通用组件选择项类型声明
|
||||
*/
|
||||
export interface Option {
|
||||
value: string;
|
||||
label: string;
|
||||
children?: Option[];
|
||||
value: string;
|
||||
label: string;
|
||||
children?: Option[];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,53 +3,53 @@ import { RouteRecordRaw, RouteLocationNormalized } from 'vue-router';
|
|||
* 用户状态类型声明
|
||||
*/
|
||||
export interface AppState {
|
||||
device: string;
|
||||
sidebar: {
|
||||
opened: boolean;
|
||||
withoutAnimation: boolean;
|
||||
};
|
||||
language: string;
|
||||
size: string;
|
||||
device: string;
|
||||
sidebar: {
|
||||
opened: boolean;
|
||||
withoutAnimation: boolean;
|
||||
};
|
||||
language: string;
|
||||
size: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 权限类型声明
|
||||
*/
|
||||
export interface PermissionState {
|
||||
routes: RouteRecordRaw[];
|
||||
addRoutes: RouteRecordRaw[];
|
||||
routes: RouteRecordRaw[];
|
||||
addRoutes: RouteRecordRaw[];
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置状态类型声明
|
||||
*/
|
||||
export interface SettingState {
|
||||
theme: string;
|
||||
tagsView: boolean;
|
||||
fixedHeader: boolean;
|
||||
showSettings: boolean;
|
||||
sidebarLogo: boolean;
|
||||
theme: string;
|
||||
tagsView: boolean;
|
||||
fixedHeader: boolean;
|
||||
showSettings: boolean;
|
||||
sidebarLogo: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* 标签状态类型声明
|
||||
*/
|
||||
export interface TagView extends Partial<RouteLocationNormalized> {
|
||||
title?: string;
|
||||
title?: string;
|
||||
}
|
||||
|
||||
export interface TagsViewState {
|
||||
visitedViews: TagView[];
|
||||
cachedViews: string[];
|
||||
visitedViews: TagView[];
|
||||
cachedViews: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户状态类型声明
|
||||
*/
|
||||
export interface UserState {
|
||||
token: string;
|
||||
nickname: string;
|
||||
avatar: string;
|
||||
roles: string[];
|
||||
perms: string[];
|
||||
token: string;
|
||||
nickname: string;
|
||||
avatar: string;
|
||||
roles: string[];
|
||||
perms: string[];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,24 +5,24 @@
|
|||
* @return {string}
|
||||
*/
|
||||
function pluralize(time: number, label: string) {
|
||||
if (time === 1) {
|
||||
return time + label;
|
||||
}
|
||||
return time + label + 's';
|
||||
if (time === 1) {
|
||||
return time + label;
|
||||
}
|
||||
return time + label + 's';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} time
|
||||
*/
|
||||
export function timeAgo(time: number) {
|
||||
const between = Date.now() / 1000 - Number(time);
|
||||
if (between < 3600) {
|
||||
return pluralize(~~(between / 60), ' minute');
|
||||
} else if (between < 86400) {
|
||||
return pluralize(~~(between / 3600), ' hour');
|
||||
} else {
|
||||
return pluralize(~~(between / 86400), ' day');
|
||||
}
|
||||
const between = Date.now() / 1000 - Number(time);
|
||||
if (between < 3600) {
|
||||
return pluralize(~~(between / 60), ' minute');
|
||||
} else if (between < 86400) {
|
||||
return pluralize(~~(between / 3600), ' hour');
|
||||
} else {
|
||||
return pluralize(~~(between / 86400), ' day');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -32,24 +32,24 @@ export function timeAgo(time: number) {
|
|||
* @param {number} digits
|
||||
*/
|
||||
export function numberFormatter(num: number, digits: number) {
|
||||
const si = [
|
||||
{ value: 1e18, symbol: 'E' },
|
||||
{ value: 1e15, symbol: 'P' },
|
||||
{ value: 1e12, symbol: 'T' },
|
||||
{ value: 1e9, symbol: 'G' },
|
||||
{ value: 1e6, symbol: 'M' },
|
||||
{ value: 1e3, symbol: 'k' }
|
||||
];
|
||||
for (let i = 0; i < si.length; i++) {
|
||||
if (num >= si[i].value) {
|
||||
return (
|
||||
(num / si[i].value)
|
||||
.toFixed(digits)
|
||||
.replace(/\.0+$|(\.[0-9]*[1-9])0+$/, '$1') + si[i].symbol
|
||||
);
|
||||
}
|
||||
}
|
||||
return num.toString();
|
||||
const si = [
|
||||
{ value: 1e18, symbol: 'E' },
|
||||
{ value: 1e15, symbol: 'P' },
|
||||
{ value: 1e12, symbol: 'T' },
|
||||
{ value: 1e9, symbol: 'G' },
|
||||
{ value: 1e6, symbol: 'M' },
|
||||
{ value: 1e3, symbol: 'k' }
|
||||
];
|
||||
for (let i = 0; i < si.length; i++) {
|
||||
if (num >= si[i].value) {
|
||||
return (
|
||||
(num / si[i].value)
|
||||
.toFixed(digits)
|
||||
.replace(/\.0+$|(\.[0-9]*[1-9])0+$/, '$1') + si[i].symbol
|
||||
);
|
||||
}
|
||||
}
|
||||
return num.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -57,9 +57,9 @@ export function numberFormatter(num: number, digits: number) {
|
|||
* @param {number} num
|
||||
*/
|
||||
export function toThousandFilter(num: number) {
|
||||
return (+num || 0)
|
||||
.toString()
|
||||
.replace(/^-?\d+/g, m => m.replace(/(?=(?!\b)(\d{3})+$)/g, ','));
|
||||
return (+num || 0)
|
||||
.toString()
|
||||
.replace(/^-?\d+/g, m => m.replace(/(?=(?!\b)(\d{3})+$)/g, ','));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -67,7 +67,7 @@ export function toThousandFilter(num: number) {
|
|||
* @param {String} string
|
||||
*/
|
||||
export function uppercaseFirst(string: string) {
|
||||
return string.charAt(0).toUpperCase() + string.slice(1);
|
||||
return string.charAt(0).toUpperCase() + string.slice(1);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -76,5 +76,5 @@ export function uppercaseFirst(string: string) {
|
|||
* @param {number} num
|
||||
*/
|
||||
export function moneyFormatter(num: number) {
|
||||
return '¥' + (isNaN(num) ? 0.0 : parseFloat((num / 100).toFixed(2)));
|
||||
return '¥' + (isNaN(num) ? 0.0 : parseFloat((num / 100).toFixed(2)));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,11 +2,11 @@
|
|||
import i18n from '@/lang/index';
|
||||
|
||||
export function generateTitle(title: any) {
|
||||
// 判断是否存在国际化配置,如果没有原生返回
|
||||
const hasKey = i18n.global.te('route.' + title);
|
||||
if (hasKey) {
|
||||
const translatedTitle = i18n.global.t('route.' + title);
|
||||
return translatedTitle;
|
||||
}
|
||||
return title;
|
||||
// 判断是否存在国际化配置,如果没有原生返回
|
||||
const hasKey = i18n.global.te('route.' + title);
|
||||
if (hasKey) {
|
||||
const translatedTitle = i18n.global.t('route.' + title);
|
||||
return translatedTitle;
|
||||
}
|
||||
return title;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
* @returns {boolean}
|
||||
*/
|
||||
export function hasClass(ele: HTMLElement, cls: string) {
|
||||
return !!ele.className.match(new RegExp('(\\s|^)' + cls + '(\\s|$)'));
|
||||
return !!ele.className.match(new RegExp('(\\s|^)' + cls + '(\\s|$)'));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -14,7 +14,7 @@ export function hasClass(ele: HTMLElement, cls: string) {
|
|||
* @param {string} cls
|
||||
*/
|
||||
export function addClass(ele: HTMLElement, cls: string) {
|
||||
if (!hasClass(ele, cls)) ele.className += ' ' + cls;
|
||||
if (!hasClass(ele, cls)) ele.className += ' ' + cls;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -23,25 +23,25 @@ export function addClass(ele: HTMLElement, cls: string) {
|
|||
* @param {string} cls
|
||||
*/
|
||||
export function removeClass(ele: HTMLElement, cls: string) {
|
||||
if (hasClass(ele, cls)) {
|
||||
const reg = new RegExp('(\\s|^)' + cls + '(\\s|$)');
|
||||
ele.className = ele.className.replace(reg, ' ');
|
||||
}
|
||||
if (hasClass(ele, cls)) {
|
||||
const reg = new RegExp('(\\s|^)' + cls + '(\\s|$)');
|
||||
ele.className = ele.className.replace(reg, ' ');
|
||||
}
|
||||
}
|
||||
|
||||
export function mix(color1: string, color2: string, weight: number) {
|
||||
weight = Math.max(Math.min(Number(weight), 1), 0);
|
||||
const r1 = parseInt(color1.substring(1, 3), 16);
|
||||
const g1 = parseInt(color1.substring(3, 5), 16);
|
||||
const b1 = parseInt(color1.substring(5, 7), 16);
|
||||
const r2 = parseInt(color2.substring(1, 3), 16);
|
||||
const g2 = parseInt(color2.substring(3, 5), 16);
|
||||
const b2 = parseInt(color2.substring(5, 7), 16);
|
||||
const r = Math.round(r1 * (1 - weight) + r2 * weight);
|
||||
const g = Math.round(g1 * (1 - weight) + g2 * weight);
|
||||
const b = Math.round(b1 * (1 - weight) + b2 * weight);
|
||||
const rStr = ('0' + (r || 0).toString(16)).slice(-2);
|
||||
const gStr = ('0' + (g || 0).toString(16)).slice(-2);
|
||||
const bStr = ('0' + (b || 0).toString(16)).slice(-2);
|
||||
return '#' + rStr + gStr + bStr;
|
||||
weight = Math.max(Math.min(Number(weight), 1), 0);
|
||||
const r1 = parseInt(color1.substring(1, 3), 16);
|
||||
const g1 = parseInt(color1.substring(3, 5), 16);
|
||||
const b1 = parseInt(color1.substring(5, 7), 16);
|
||||
const r2 = parseInt(color2.substring(1, 3), 16);
|
||||
const g2 = parseInt(color2.substring(3, 5), 16);
|
||||
const b2 = parseInt(color2.substring(5, 7), 16);
|
||||
const r = Math.round(r1 * (1 - weight) + r2 * weight);
|
||||
const g = Math.round(g1 * (1 - weight) + g2 * weight);
|
||||
const b = Math.round(b1 * (1 - weight) + b2 * weight);
|
||||
const rStr = ('0' + (r || 0).toString(16)).slice(-2);
|
||||
const gStr = ('0' + (g || 0).toString(16)).slice(-2);
|
||||
const bStr = ('0' + (b || 0).toString(16)).slice(-2);
|
||||
return '#' + rStr + gStr + bStr;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,64 +5,64 @@ import useStore from '@/store';
|
|||
|
||||
// 创建 axios 实例
|
||||
const service = axios.create({
|
||||
baseURL: import.meta.env.VITE_APP_BASE_API,
|
||||
timeout: 50000,
|
||||
headers: { 'Content-Type': 'application/json;charset=utf-8' }
|
||||
baseURL: import.meta.env.VITE_APP_BASE_API,
|
||||
timeout: 50000,
|
||||
headers: { 'Content-Type': 'application/json;charset=utf-8' }
|
||||
});
|
||||
|
||||
// 请求拦截器
|
||||
service.interceptors.request.use(
|
||||
(config: AxiosRequestConfig) => {
|
||||
if (!config.headers) {
|
||||
throw new Error(
|
||||
`Expected 'config' and 'config.headers' not to be undefined`
|
||||
);
|
||||
}
|
||||
const { user } = useStore();
|
||||
if (user.token) {
|
||||
config.headers.Authorization = `${localStorage.get('token')}`;
|
||||
}
|
||||
return config;
|
||||
},
|
||||
error => {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
(config: AxiosRequestConfig) => {
|
||||
if (!config.headers) {
|
||||
throw new Error(
|
||||
`Expected 'config' and 'config.headers' not to be undefined`
|
||||
);
|
||||
}
|
||||
const { user } = useStore();
|
||||
if (user.token) {
|
||||
config.headers.Authorization = `${localStorage.get('token')}`;
|
||||
}
|
||||
return config;
|
||||
},
|
||||
error => {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
|
||||
// 响应拦截器
|
||||
service.interceptors.response.use(
|
||||
(response: AxiosResponse) => {
|
||||
const { code, msg } = response.data;
|
||||
if (code === '00000') {
|
||||
return response.data;
|
||||
} else {
|
||||
// 响应数据为二进制流处理(Excel导出)
|
||||
if (response.data instanceof ArrayBuffer) {
|
||||
return response;
|
||||
}
|
||||
(response: AxiosResponse) => {
|
||||
const { code, msg } = response.data;
|
||||
if (code === '00000') {
|
||||
return response.data;
|
||||
} else {
|
||||
// 响应数据为二进制流处理(Excel导出)
|
||||
if (response.data instanceof ArrayBuffer) {
|
||||
return response;
|
||||
}
|
||||
|
||||
ElMessage({
|
||||
message: msg || '系统出错',
|
||||
type: 'error'
|
||||
});
|
||||
return Promise.reject(new Error(msg || 'Error'));
|
||||
}
|
||||
},
|
||||
error => {
|
||||
const { code, msg } = error.response.data;
|
||||
if (code === 'A0230') {
|
||||
// token 过期
|
||||
localStorage.clear(); // 清除浏览器全部缓存
|
||||
window.location.href = '/'; // 跳转登录页
|
||||
ElMessageBox.alert('当前页面已失效,请重新登录', '提示', {});
|
||||
} else {
|
||||
ElMessage({
|
||||
message: msg || '系统出错',
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
return Promise.reject(new Error(msg || 'Error'));
|
||||
}
|
||||
ElMessage({
|
||||
message: msg || '系统出错',
|
||||
type: 'error'
|
||||
});
|
||||
return Promise.reject(new Error(msg || 'Error'));
|
||||
}
|
||||
},
|
||||
error => {
|
||||
const { code, msg } = error.response.data;
|
||||
if (code === 'A0230') {
|
||||
// token 过期
|
||||
localStorage.clear(); // 清除浏览器全部缓存
|
||||
window.location.href = '/'; // 跳转登录页
|
||||
ElMessageBox.alert('当前页面已失效,请重新登录', '提示', {});
|
||||
} else {
|
||||
ElMessage({
|
||||
message: msg || '系统出错',
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
return Promise.reject(new Error(msg || 'Error'));
|
||||
}
|
||||
);
|
||||
|
||||
// 导出 axios 实例
|
||||
|
|
|
|||
|
|
@ -1,72 +1,72 @@
|
|||
import { ref } from 'vue';
|
||||
export default function () {
|
||||
const chart = ref<any>();
|
||||
const sidebarElm = ref<Element>();
|
||||
const chart = ref<any>();
|
||||
const sidebarElm = ref<Element>();
|
||||
|
||||
const chartResizeHandler = () => {
|
||||
if (chart.value) {
|
||||
chart.value.resize();
|
||||
}
|
||||
};
|
||||
const chartResizeHandler = () => {
|
||||
if (chart.value) {
|
||||
chart.value.resize();
|
||||
}
|
||||
};
|
||||
|
||||
const sidebarResizeHandler = (e: TransitionEvent) => {
|
||||
if (e.propertyName === 'width') {
|
||||
chartResizeHandler();
|
||||
}
|
||||
};
|
||||
const sidebarResizeHandler = (e: TransitionEvent) => {
|
||||
if (e.propertyName === 'width') {
|
||||
chartResizeHandler();
|
||||
}
|
||||
};
|
||||
|
||||
const initResizeEvent = () => {
|
||||
window.addEventListener('resize', chartResizeHandler);
|
||||
};
|
||||
const initResizeEvent = () => {
|
||||
window.addEventListener('resize', chartResizeHandler);
|
||||
};
|
||||
|
||||
const destroyResizeEvent = () => {
|
||||
window.removeEventListener('resize', chartResizeHandler);
|
||||
};
|
||||
const destroyResizeEvent = () => {
|
||||
window.removeEventListener('resize', chartResizeHandler);
|
||||
};
|
||||
|
||||
const initSidebarResizeEvent = () => {
|
||||
sidebarElm.value = document.getElementsByClassName('sidebar-container')[0];
|
||||
if (sidebarElm.value) {
|
||||
sidebarElm.value.addEventListener(
|
||||
'transitionend',
|
||||
sidebarResizeHandler as EventListener
|
||||
);
|
||||
}
|
||||
};
|
||||
const initSidebarResizeEvent = () => {
|
||||
sidebarElm.value = document.getElementsByClassName('sidebar-container')[0];
|
||||
if (sidebarElm.value) {
|
||||
sidebarElm.value.addEventListener(
|
||||
'transitionend',
|
||||
sidebarResizeHandler as EventListener
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const destroySidebarResizeEvent = () => {
|
||||
if (sidebarElm.value) {
|
||||
sidebarElm.value.removeEventListener(
|
||||
'transitionend',
|
||||
sidebarResizeHandler as EventListener
|
||||
);
|
||||
}
|
||||
};
|
||||
const destroySidebarResizeEvent = () => {
|
||||
if (sidebarElm.value) {
|
||||
sidebarElm.value.removeEventListener(
|
||||
'transitionend',
|
||||
sidebarResizeHandler as EventListener
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const mounted = () => {
|
||||
initResizeEvent();
|
||||
initSidebarResizeEvent();
|
||||
};
|
||||
const mounted = () => {
|
||||
initResizeEvent();
|
||||
initSidebarResizeEvent();
|
||||
};
|
||||
|
||||
const beforeDestroy = () => {
|
||||
destroyResizeEvent();
|
||||
destroySidebarResizeEvent();
|
||||
};
|
||||
const beforeDestroy = () => {
|
||||
destroyResizeEvent();
|
||||
destroySidebarResizeEvent();
|
||||
};
|
||||
|
||||
const activated = () => {
|
||||
initResizeEvent();
|
||||
initSidebarResizeEvent();
|
||||
};
|
||||
const activated = () => {
|
||||
initResizeEvent();
|
||||
initSidebarResizeEvent();
|
||||
};
|
||||
|
||||
const deactivated = () => {
|
||||
destroyResizeEvent();
|
||||
destroySidebarResizeEvent();
|
||||
};
|
||||
const deactivated = () => {
|
||||
destroyResizeEvent();
|
||||
destroySidebarResizeEvent();
|
||||
};
|
||||
|
||||
return {
|
||||
chart,
|
||||
mounted,
|
||||
beforeDestroy,
|
||||
activated,
|
||||
deactivated
|
||||
};
|
||||
return {
|
||||
chart,
|
||||
mounted,
|
||||
beforeDestroy,
|
||||
activated,
|
||||
deactivated
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,22 +1,22 @@
|
|||
const easeInOutQuad = (t: number, b: number, c: number, d: number) => {
|
||||
t /= d / 2;
|
||||
if (t < 1) {
|
||||
return (c / 2) * t * t + b;
|
||||
}
|
||||
t--;
|
||||
return (-c / 2) * (t * (t - 2) - 1) + b;
|
||||
t /= d / 2;
|
||||
if (t < 1) {
|
||||
return (c / 2) * t * t + b;
|
||||
}
|
||||
t--;
|
||||
return (-c / 2) * (t * (t - 2) - 1) + b;
|
||||
};
|
||||
|
||||
// requestAnimationFrame for Smart Animating http://goo.gl/sx5sts
|
||||
const requestAnimFrame = (function () {
|
||||
return (
|
||||
window.requestAnimationFrame ||
|
||||
(window as any).webkitRequestAnimationFrame ||
|
||||
(window as any).mozRequestAnimationFrame ||
|
||||
function (callback) {
|
||||
window.setTimeout(callback, 1000 / 60);
|
||||
}
|
||||
);
|
||||
return (
|
||||
window.requestAnimationFrame ||
|
||||
(window as any).webkitRequestAnimationFrame ||
|
||||
(window as any).mozRequestAnimationFrame ||
|
||||
function (callback) {
|
||||
window.setTimeout(callback, 1000 / 60);
|
||||
}
|
||||
);
|
||||
})();
|
||||
|
||||
/**
|
||||
|
|
@ -24,17 +24,17 @@ const requestAnimFrame = (function () {
|
|||
* @param {number} amount
|
||||
*/
|
||||
const move = (amount: number) => {
|
||||
document.documentElement.scrollTop = amount;
|
||||
(document.body.parentNode as HTMLElement).scrollTop = amount;
|
||||
document.body.scrollTop = amount;
|
||||
document.documentElement.scrollTop = amount;
|
||||
(document.body.parentNode as HTMLElement).scrollTop = amount;
|
||||
document.body.scrollTop = amount;
|
||||
};
|
||||
|
||||
const position = () => {
|
||||
return (
|
||||
document.documentElement.scrollTop ||
|
||||
(document.body.parentNode as HTMLElement).scrollTop ||
|
||||
document.body.scrollTop
|
||||
);
|
||||
return (
|
||||
document.documentElement.scrollTop ||
|
||||
(document.body.parentNode as HTMLElement).scrollTop ||
|
||||
document.body.scrollTop
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -43,27 +43,27 @@ const position = () => {
|
|||
* @param {Function} callback
|
||||
*/
|
||||
export const scrollTo = (to: number, duration: number, callback?: any) => {
|
||||
const start = position();
|
||||
const change = to - start;
|
||||
const increment = 20;
|
||||
let currentTime = 0;
|
||||
duration = typeof duration === 'undefined' ? 500 : duration;
|
||||
const animateScroll = function () {
|
||||
// increment the time
|
||||
currentTime += increment;
|
||||
// find the value with the quadratic in-out easing function
|
||||
const val = easeInOutQuad(currentTime, start, change, duration);
|
||||
// move the document.body
|
||||
move(val);
|
||||
// do the animation unless its over
|
||||
if (currentTime < duration) {
|
||||
requestAnimFrame(animateScroll);
|
||||
} else {
|
||||
if (callback && typeof callback === 'function') {
|
||||
// the animation is done so lets callback
|
||||
callback();
|
||||
}
|
||||
}
|
||||
};
|
||||
animateScroll();
|
||||
const start = position();
|
||||
const change = to - start;
|
||||
const increment = 20;
|
||||
let currentTime = 0;
|
||||
duration = typeof duration === 'undefined' ? 500 : duration;
|
||||
const animateScroll = function () {
|
||||
// increment the time
|
||||
currentTime += increment;
|
||||
// find the value with the quadratic in-out easing function
|
||||
const val = easeInOutQuad(currentTime, start, change, duration);
|
||||
// move the document.body
|
||||
move(val);
|
||||
// do the animation unless its over
|
||||
if (currentTime < duration) {
|
||||
requestAnimFrame(animateScroll);
|
||||
} else {
|
||||
if (callback && typeof callback === 'function') {
|
||||
// the animation is done so lets callback
|
||||
callback();
|
||||
}
|
||||
}
|
||||
};
|
||||
animateScroll();
|
||||
};
|
||||
|
|
|
|||
|
|
@ -2,44 +2,44 @@
|
|||
* window.localStorage 浏览器永久缓存
|
||||
*/
|
||||
export const localStorage = {
|
||||
// 设置永久缓存
|
||||
set(key: string, val: any) {
|
||||
window.localStorage.setItem(key, JSON.stringify(val));
|
||||
},
|
||||
// 获取永久缓存
|
||||
get(key: string) {
|
||||
const json: any = window.localStorage.getItem(key);
|
||||
return JSON.parse(json);
|
||||
},
|
||||
// 移除永久缓存
|
||||
remove(key: string) {
|
||||
window.localStorage.removeItem(key);
|
||||
},
|
||||
// 移除全部永久缓存
|
||||
clear() {
|
||||
window.localStorage.clear();
|
||||
}
|
||||
// 设置永久缓存
|
||||
set(key: string, val: any) {
|
||||
window.localStorage.setItem(key, JSON.stringify(val));
|
||||
},
|
||||
// 获取永久缓存
|
||||
get(key: string) {
|
||||
const json: any = window.localStorage.getItem(key);
|
||||
return JSON.parse(json);
|
||||
},
|
||||
// 移除永久缓存
|
||||
remove(key: string) {
|
||||
window.localStorage.removeItem(key);
|
||||
},
|
||||
// 移除全部永久缓存
|
||||
clear() {
|
||||
window.localStorage.clear();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* window.sessionStorage 浏览器临时缓存
|
||||
*/
|
||||
export const sessionStorage = {
|
||||
// 设置临时缓存
|
||||
set(key: string, val: any) {
|
||||
window.sessionStorage.setItem(key, JSON.stringify(val));
|
||||
},
|
||||
// 获取临时缓存
|
||||
get(key: string) {
|
||||
const json: any = window.sessionStorage.getItem(key);
|
||||
return JSON.parse(json);
|
||||
},
|
||||
// 移除临时缓存
|
||||
remove(key: string) {
|
||||
window.sessionStorage.removeItem(key);
|
||||
},
|
||||
// 移除全部临时缓存
|
||||
clear() {
|
||||
window.sessionStorage.clear();
|
||||
}
|
||||
// 设置临时缓存
|
||||
set(key: string, val: any) {
|
||||
window.sessionStorage.setItem(key, JSON.stringify(val));
|
||||
},
|
||||
// 获取临时缓存
|
||||
get(key: string) {
|
||||
const json: any = window.sessionStorage.getItem(key);
|
||||
return JSON.parse(json);
|
||||
},
|
||||
// 移除临时缓存
|
||||
remove(key: string) {
|
||||
window.sessionStorage.removeItem(key);
|
||||
},
|
||||
// 移除全部临时缓存
|
||||
clear() {
|
||||
window.sessionStorage.clear();
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -7,6 +7,6 @@
|
|||
* @returns {Boolean}
|
||||
*/
|
||||
export function isExternal(path: string) {
|
||||
const isExternal = /^(https?:|http?:|mailto:|tel:)/.test(path);
|
||||
return isExternal;
|
||||
const isExternal = /^(https?:|http?:|mailto:|tel:)/.test(path);
|
||||
return isExternal;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,174 +1,174 @@
|
|||
<!-- 线 + 柱混合图 -->
|
||||
<template>
|
||||
<div :id="id" :class="className" :style="{ height, width }" />
|
||||
<div :id="id" :class="className" :style="{ height, width }" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
nextTick,
|
||||
onActivated,
|
||||
onBeforeUnmount,
|
||||
onDeactivated,
|
||||
onMounted
|
||||
nextTick,
|
||||
onActivated,
|
||||
onBeforeUnmount,
|
||||
onDeactivated,
|
||||
onMounted
|
||||
} from 'vue';
|
||||
import { init, EChartsOption } from 'echarts';
|
||||
import * as echarts from 'echarts';
|
||||
import resize from '@/utils/resize';
|
||||
|
||||
const props = defineProps({
|
||||
id: {
|
||||
type: String,
|
||||
default: 'barChart'
|
||||
},
|
||||
className: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
width: {
|
||||
type: String,
|
||||
default: '200px',
|
||||
required: true
|
||||
},
|
||||
height: {
|
||||
type: String,
|
||||
default: '200px',
|
||||
required: true
|
||||
}
|
||||
id: {
|
||||
type: String,
|
||||
default: 'barChart'
|
||||
},
|
||||
className: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
width: {
|
||||
type: String,
|
||||
default: '200px',
|
||||
required: true
|
||||
},
|
||||
height: {
|
||||
type: String,
|
||||
default: '200px',
|
||||
required: true
|
||||
}
|
||||
});
|
||||
|
||||
const { mounted, chart, beforeDestroy, activated, deactivated } = resize();
|
||||
|
||||
function initChart() {
|
||||
const barChart = init(document.getElementById(props.id) as HTMLDivElement);
|
||||
const barChart = init(document.getElementById(props.id) as HTMLDivElement);
|
||||
|
||||
barChart.setOption({
|
||||
title: {
|
||||
show: true,
|
||||
text: '业绩总览',
|
||||
x: 'center',
|
||||
padding: 15,
|
||||
textStyle: {
|
||||
fontSize: 18,
|
||||
fontStyle: 'normal',
|
||||
fontWeight: 'bold',
|
||||
color: '#337ecc'
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
left: '2%',
|
||||
right: '2%',
|
||||
bottom: '10%',
|
||||
containLabel: true
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'cross',
|
||||
crossStyle: {
|
||||
color: '#999'
|
||||
}
|
||||
}
|
||||
},
|
||||
legend: {
|
||||
x: 'center',
|
||||
y: 'bottom',
|
||||
data: ['收入', '毛利润', '收入增长率', '利润增长率']
|
||||
},
|
||||
xAxis: [
|
||||
{
|
||||
type: 'category',
|
||||
data: ['上海', '北京', '浙江', '广东', '深圳', '四川', '湖北', '安徽'],
|
||||
axisPointer: {
|
||||
type: 'shadow'
|
||||
}
|
||||
}
|
||||
],
|
||||
yAxis: [
|
||||
{
|
||||
type: 'value',
|
||||
min: 0,
|
||||
max: 10000,
|
||||
interval: 2000,
|
||||
axisLabel: {
|
||||
formatter: '{value} '
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'value',
|
||||
min: 0,
|
||||
max: 100,
|
||||
interval: 20,
|
||||
axisLabel: {
|
||||
formatter: '{value}%'
|
||||
}
|
||||
}
|
||||
],
|
||||
series: [
|
||||
{
|
||||
name: '收入',
|
||||
type: 'bar',
|
||||
data: [8000, 8200, 7000, 6200, 6500, 5500, 4500, 4200, 3800],
|
||||
barWidth: 20,
|
||||
itemStyle: {
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{ offset: 0, color: '#83bff6' },
|
||||
{ offset: 0.5, color: '#188df0' },
|
||||
{ offset: 1, color: '#188df0' }
|
||||
])
|
||||
}
|
||||
},
|
||||
{
|
||||
name: '毛利润',
|
||||
type: 'bar',
|
||||
data: [6700, 6800, 6300, 5213, 4500, 4200, 4200, 3800],
|
||||
barWidth: 20,
|
||||
itemStyle: {
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{ offset: 0, color: '#25d73c' },
|
||||
{ offset: 0.5, color: '#1bc23d' },
|
||||
{ offset: 1, color: '#179e61' }
|
||||
])
|
||||
}
|
||||
},
|
||||
{
|
||||
name: '收入增长率',
|
||||
type: 'line',
|
||||
yAxisIndex: 1,
|
||||
data: [65, 67, 65, 53, 47, 45, 43, 42, 41],
|
||||
itemStyle: {
|
||||
color: '#67C23A'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: '利润增长率',
|
||||
type: 'line',
|
||||
yAxisIndex: 1,
|
||||
data: [80, 81, 78, 67, 65, 60, 56, 51, 45],
|
||||
itemStyle: {
|
||||
color: '#409EFF'
|
||||
}
|
||||
}
|
||||
]
|
||||
} as EChartsOption);
|
||||
chart.value = barChart;
|
||||
barChart.setOption({
|
||||
title: {
|
||||
show: true,
|
||||
text: '业绩总览',
|
||||
x: 'center',
|
||||
padding: 15,
|
||||
textStyle: {
|
||||
fontSize: 18,
|
||||
fontStyle: 'normal',
|
||||
fontWeight: 'bold',
|
||||
color: '#337ecc'
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
left: '2%',
|
||||
right: '2%',
|
||||
bottom: '10%',
|
||||
containLabel: true
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'cross',
|
||||
crossStyle: {
|
||||
color: '#999'
|
||||
}
|
||||
}
|
||||
},
|
||||
legend: {
|
||||
x: 'center',
|
||||
y: 'bottom',
|
||||
data: ['收入', '毛利润', '收入增长率', '利润增长率']
|
||||
},
|
||||
xAxis: [
|
||||
{
|
||||
type: 'category',
|
||||
data: ['上海', '北京', '浙江', '广东', '深圳', '四川', '湖北', '安徽'],
|
||||
axisPointer: {
|
||||
type: 'shadow'
|
||||
}
|
||||
}
|
||||
],
|
||||
yAxis: [
|
||||
{
|
||||
type: 'value',
|
||||
min: 0,
|
||||
max: 10000,
|
||||
interval: 2000,
|
||||
axisLabel: {
|
||||
formatter: '{value} '
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'value',
|
||||
min: 0,
|
||||
max: 100,
|
||||
interval: 20,
|
||||
axisLabel: {
|
||||
formatter: '{value}%'
|
||||
}
|
||||
}
|
||||
],
|
||||
series: [
|
||||
{
|
||||
name: '收入',
|
||||
type: 'bar',
|
||||
data: [8000, 8200, 7000, 6200, 6500, 5500, 4500, 4200, 3800],
|
||||
barWidth: 20,
|
||||
itemStyle: {
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{ offset: 0, color: '#83bff6' },
|
||||
{ offset: 0.5, color: '#188df0' },
|
||||
{ offset: 1, color: '#188df0' }
|
||||
])
|
||||
}
|
||||
},
|
||||
{
|
||||
name: '毛利润',
|
||||
type: 'bar',
|
||||
data: [6700, 6800, 6300, 5213, 4500, 4200, 4200, 3800],
|
||||
barWidth: 20,
|
||||
itemStyle: {
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{ offset: 0, color: '#25d73c' },
|
||||
{ offset: 0.5, color: '#1bc23d' },
|
||||
{ offset: 1, color: '#179e61' }
|
||||
])
|
||||
}
|
||||
},
|
||||
{
|
||||
name: '收入增长率',
|
||||
type: 'line',
|
||||
yAxisIndex: 1,
|
||||
data: [65, 67, 65, 53, 47, 45, 43, 42, 41],
|
||||
itemStyle: {
|
||||
color: '#67C23A'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: '利润增长率',
|
||||
type: 'line',
|
||||
yAxisIndex: 1,
|
||||
data: [80, 81, 78, 67, 65, 60, 56, 51, 45],
|
||||
itemStyle: {
|
||||
color: '#409EFF'
|
||||
}
|
||||
}
|
||||
]
|
||||
} as EChartsOption);
|
||||
chart.value = barChart;
|
||||
}
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
beforeDestroy();
|
||||
beforeDestroy();
|
||||
});
|
||||
|
||||
onActivated(() => {
|
||||
activated();
|
||||
activated();
|
||||
});
|
||||
|
||||
onDeactivated(() => {
|
||||
deactivated();
|
||||
deactivated();
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
mounted();
|
||||
nextTick(() => {
|
||||
initChart();
|
||||
});
|
||||
mounted();
|
||||
nextTick(() => {
|
||||
initChart();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -1,134 +1,134 @@
|
|||
<!-- 漏斗图 -->
|
||||
<template>
|
||||
<div :id="id" :class="className" :style="{ height, width }" />
|
||||
<div :id="id" :class="className" :style="{ height, width }" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
nextTick,
|
||||
onActivated,
|
||||
onBeforeUnmount,
|
||||
onDeactivated,
|
||||
onMounted
|
||||
nextTick,
|
||||
onActivated,
|
||||
onBeforeUnmount,
|
||||
onDeactivated,
|
||||
onMounted
|
||||
} from 'vue';
|
||||
import { init, EChartsOption } from 'echarts';
|
||||
import resize from '@/utils/resize';
|
||||
|
||||
const props = defineProps({
|
||||
id: {
|
||||
type: String,
|
||||
default: 'funnelChart'
|
||||
},
|
||||
className: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
width: {
|
||||
type: String,
|
||||
default: '200px',
|
||||
required: true
|
||||
},
|
||||
height: {
|
||||
type: String,
|
||||
default: '200px',
|
||||
required: true
|
||||
}
|
||||
id: {
|
||||
type: String,
|
||||
default: 'funnelChart'
|
||||
},
|
||||
className: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
width: {
|
||||
type: String,
|
||||
default: '200px',
|
||||
required: true
|
||||
},
|
||||
height: {
|
||||
type: String,
|
||||
default: '200px',
|
||||
required: true
|
||||
}
|
||||
});
|
||||
|
||||
const { mounted, chart, beforeDestroy, activated, deactivated } = resize();
|
||||
|
||||
function initChart() {
|
||||
const funnelChart = init(document.getElementById(props.id) as HTMLDivElement);
|
||||
const funnelChart = init(document.getElementById(props.id) as HTMLDivElement);
|
||||
|
||||
funnelChart.setOption({
|
||||
title: {
|
||||
show: true,
|
||||
text: '订单线索转化漏斗图',
|
||||
x: 'center',
|
||||
padding: 15,
|
||||
textStyle: {
|
||||
fontSize: 18,
|
||||
fontStyle: 'normal',
|
||||
fontWeight: 'bold',
|
||||
color: '#337ecc'
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
left: '2%',
|
||||
right: '2%',
|
||||
bottom: '10%',
|
||||
containLabel: true
|
||||
},
|
||||
legend: {
|
||||
x: 'center',
|
||||
y: 'bottom',
|
||||
data: ['Show', 'Click', 'Visit', 'Inquiry', 'Order']
|
||||
},
|
||||
funnelChart.setOption({
|
||||
title: {
|
||||
show: true,
|
||||
text: '订单线索转化漏斗图',
|
||||
x: 'center',
|
||||
padding: 15,
|
||||
textStyle: {
|
||||
fontSize: 18,
|
||||
fontStyle: 'normal',
|
||||
fontWeight: 'bold',
|
||||
color: '#337ecc'
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
left: '2%',
|
||||
right: '2%',
|
||||
bottom: '10%',
|
||||
containLabel: true
|
||||
},
|
||||
legend: {
|
||||
x: 'center',
|
||||
y: 'bottom',
|
||||
data: ['Show', 'Click', 'Visit', 'Inquiry', 'Order']
|
||||
},
|
||||
|
||||
series: [
|
||||
{
|
||||
name: 'Funnel',
|
||||
type: 'funnel',
|
||||
left: '20%',
|
||||
top: 60,
|
||||
bottom: 60,
|
||||
width: '60%',
|
||||
min: 0,
|
||||
max: 100,
|
||||
minSize: '0%',
|
||||
maxSize: '100%',
|
||||
sort: 'descending',
|
||||
gap: 2,
|
||||
label: {
|
||||
show: true,
|
||||
position: 'inside'
|
||||
},
|
||||
labelLine: {
|
||||
length: 10,
|
||||
lineStyle: {
|
||||
width: 1,
|
||||
type: 'solid'
|
||||
}
|
||||
},
|
||||
itemStyle: {
|
||||
borderColor: '#fff',
|
||||
borderWidth: 1
|
||||
},
|
||||
emphasis: {
|
||||
label: {
|
||||
fontSize: 20
|
||||
}
|
||||
},
|
||||
data: [
|
||||
{ value: 60, name: 'Visit' },
|
||||
{ value: 40, name: 'Inquiry' },
|
||||
{ value: 20, name: 'Order' },
|
||||
{ value: 80, name: 'Click' },
|
||||
{ value: 100, name: 'Show' }
|
||||
]
|
||||
}
|
||||
]
|
||||
} as EChartsOption);
|
||||
chart.value = funnelChart;
|
||||
series: [
|
||||
{
|
||||
name: 'Funnel',
|
||||
type: 'funnel',
|
||||
left: '20%',
|
||||
top: 60,
|
||||
bottom: 60,
|
||||
width: '60%',
|
||||
min: 0,
|
||||
max: 100,
|
||||
minSize: '0%',
|
||||
maxSize: '100%',
|
||||
sort: 'descending',
|
||||
gap: 2,
|
||||
label: {
|
||||
show: true,
|
||||
position: 'inside'
|
||||
},
|
||||
labelLine: {
|
||||
length: 10,
|
||||
lineStyle: {
|
||||
width: 1,
|
||||
type: 'solid'
|
||||
}
|
||||
},
|
||||
itemStyle: {
|
||||
borderColor: '#fff',
|
||||
borderWidth: 1
|
||||
},
|
||||
emphasis: {
|
||||
label: {
|
||||
fontSize: 20
|
||||
}
|
||||
},
|
||||
data: [
|
||||
{ value: 60, name: 'Visit' },
|
||||
{ value: 40, name: 'Inquiry' },
|
||||
{ value: 20, name: 'Order' },
|
||||
{ value: 80, name: 'Click' },
|
||||
{ value: 100, name: 'Show' }
|
||||
]
|
||||
}
|
||||
]
|
||||
} as EChartsOption);
|
||||
chart.value = funnelChart;
|
||||
}
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
beforeDestroy();
|
||||
beforeDestroy();
|
||||
});
|
||||
|
||||
onActivated(() => {
|
||||
activated();
|
||||
activated();
|
||||
});
|
||||
|
||||
onDeactivated(() => {
|
||||
deactivated();
|
||||
deactivated();
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
mounted();
|
||||
nextTick(() => {
|
||||
initChart();
|
||||
});
|
||||
mounted();
|
||||
nextTick(() => {
|
||||
initChart();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,114 +1,114 @@
|
|||
<!-- 饼图 -->
|
||||
<template>
|
||||
<div :id="id" :class="className" :style="{ height, width }" />
|
||||
<div :id="id" :class="className" :style="{ height, width }" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
nextTick,
|
||||
onActivated,
|
||||
onBeforeUnmount,
|
||||
onDeactivated,
|
||||
onMounted
|
||||
nextTick,
|
||||
onActivated,
|
||||
onBeforeUnmount,
|
||||
onDeactivated,
|
||||
onMounted
|
||||
} from 'vue';
|
||||
import { init, EChartsOption } from 'echarts';
|
||||
import resize from '@/utils/resize';
|
||||
|
||||
const props = defineProps({
|
||||
id: {
|
||||
type: String,
|
||||
default: 'pieChart'
|
||||
},
|
||||
className: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
width: {
|
||||
type: String,
|
||||
default: '200px',
|
||||
required: true
|
||||
},
|
||||
height: {
|
||||
type: String,
|
||||
default: '200px',
|
||||
required: true
|
||||
}
|
||||
id: {
|
||||
type: String,
|
||||
default: 'pieChart'
|
||||
},
|
||||
className: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
width: {
|
||||
type: String,
|
||||
default: '200px',
|
||||
required: true
|
||||
},
|
||||
height: {
|
||||
type: String,
|
||||
default: '200px',
|
||||
required: true
|
||||
}
|
||||
});
|
||||
|
||||
const { mounted, chart, beforeDestroy, activated, deactivated } = resize();
|
||||
|
||||
function initChart() {
|
||||
const pieChart = init(document.getElementById(props.id) as HTMLDivElement);
|
||||
const pieChart = init(document.getElementById(props.id) as HTMLDivElement);
|
||||
|
||||
pieChart.setOption({
|
||||
title: {
|
||||
show: true,
|
||||
text: '产品分类总览',
|
||||
x: 'center',
|
||||
padding: 15,
|
||||
textStyle: {
|
||||
fontSize: 18,
|
||||
fontStyle: 'normal',
|
||||
fontWeight: 'bold',
|
||||
color: '#337ecc'
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
left: '2%',
|
||||
right: '2%',
|
||||
bottom: '10%',
|
||||
containLabel: true
|
||||
},
|
||||
legend: {
|
||||
top: 'bottom'
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: 'Nightingale Chart',
|
||||
type: 'pie',
|
||||
radius: [50, 130],
|
||||
center: ['50%', '50%'],
|
||||
roseType: 'area',
|
||||
itemStyle: {
|
||||
borderRadius: 6,
|
||||
normal: {
|
||||
color: function (params: any) {
|
||||
//自定义颜色
|
||||
const colorList = ['#409EFF', '#67C23A', '#E6A23C', '#F56C6C'];
|
||||
return colorList[params.dataIndex];
|
||||
}
|
||||
}
|
||||
},
|
||||
data: [
|
||||
{ value: 26, name: '家用电器' },
|
||||
{ value: 27, name: '户外运动' },
|
||||
{ value: 24, name: '汽车用品' },
|
||||
{ value: 23, name: '手机数码' }
|
||||
]
|
||||
}
|
||||
]
|
||||
} as EChartsOption);
|
||||
pieChart.setOption({
|
||||
title: {
|
||||
show: true,
|
||||
text: '产品分类总览',
|
||||
x: 'center',
|
||||
padding: 15,
|
||||
textStyle: {
|
||||
fontSize: 18,
|
||||
fontStyle: 'normal',
|
||||
fontWeight: 'bold',
|
||||
color: '#337ecc'
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
left: '2%',
|
||||
right: '2%',
|
||||
bottom: '10%',
|
||||
containLabel: true
|
||||
},
|
||||
legend: {
|
||||
top: 'bottom'
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: 'Nightingale Chart',
|
||||
type: 'pie',
|
||||
radius: [50, 130],
|
||||
center: ['50%', '50%'],
|
||||
roseType: 'area',
|
||||
itemStyle: {
|
||||
borderRadius: 6,
|
||||
normal: {
|
||||
color: function (params: any) {
|
||||
//自定义颜色
|
||||
const colorList = ['#409EFF', '#67C23A', '#E6A23C', '#F56C6C'];
|
||||
return colorList[params.dataIndex];
|
||||
}
|
||||
}
|
||||
},
|
||||
data: [
|
||||
{ value: 26, name: '家用电器' },
|
||||
{ value: 27, name: '户外运动' },
|
||||
{ value: 24, name: '汽车用品' },
|
||||
{ value: 23, name: '手机数码' }
|
||||
]
|
||||
}
|
||||
]
|
||||
} as EChartsOption);
|
||||
|
||||
chart.value = pieChart;
|
||||
chart.value = pieChart;
|
||||
}
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
beforeDestroy();
|
||||
beforeDestroy();
|
||||
});
|
||||
|
||||
onActivated(() => {
|
||||
activated();
|
||||
activated();
|
||||
});
|
||||
|
||||
onDeactivated(() => {
|
||||
deactivated();
|
||||
deactivated();
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
mounted();
|
||||
nextTick(() => {
|
||||
initChart();
|
||||
});
|
||||
mounted();
|
||||
nextTick(() => {
|
||||
initChart();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,133 +1,133 @@
|
|||
<!-- 雷达图 -->
|
||||
<template>
|
||||
<div :id="id" :class="className" :style="{ height, width }" />
|
||||
<div :id="id" :class="className" :style="{ height, width }" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
nextTick,
|
||||
onActivated,
|
||||
onBeforeUnmount,
|
||||
onDeactivated,
|
||||
onMounted
|
||||
nextTick,
|
||||
onActivated,
|
||||
onBeforeUnmount,
|
||||
onDeactivated,
|
||||
onMounted
|
||||
} from 'vue';
|
||||
import { init, EChartsOption } from 'echarts';
|
||||
import resize from '@/utils/resize';
|
||||
|
||||
const props = defineProps({
|
||||
id: {
|
||||
type: String,
|
||||
default: 'radarChart'
|
||||
},
|
||||
className: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
width: {
|
||||
type: String,
|
||||
default: '200px',
|
||||
required: true
|
||||
},
|
||||
height: {
|
||||
type: String,
|
||||
default: '200px',
|
||||
required: true
|
||||
}
|
||||
id: {
|
||||
type: String,
|
||||
default: 'radarChart'
|
||||
},
|
||||
className: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
width: {
|
||||
type: String,
|
||||
default: '200px',
|
||||
required: true
|
||||
},
|
||||
height: {
|
||||
type: String,
|
||||
default: '200px',
|
||||
required: true
|
||||
}
|
||||
});
|
||||
|
||||
const { mounted, chart, beforeDestroy, activated, deactivated } = resize();
|
||||
|
||||
function initChart() {
|
||||
const radarChart = init(document.getElementById(props.id) as HTMLDivElement);
|
||||
const radarChart = init(document.getElementById(props.id) as HTMLDivElement);
|
||||
|
||||
radarChart.setOption({
|
||||
title: {
|
||||
show: true,
|
||||
text: '订单状态统计',
|
||||
x: 'center',
|
||||
padding: 15,
|
||||
textStyle: {
|
||||
fontSize: 18,
|
||||
fontStyle: 'normal',
|
||||
fontWeight: 'bold',
|
||||
color: '#337ecc'
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
left: '2%',
|
||||
right: '2%',
|
||||
bottom: '10%',
|
||||
containLabel: true
|
||||
},
|
||||
legend: {
|
||||
x: 'center',
|
||||
y: 'bottom',
|
||||
data: ['预定数量', '下单数量', '发货数量']
|
||||
},
|
||||
radar: {
|
||||
// shape: 'circle',
|
||||
radius: '60%',
|
||||
indicator: [
|
||||
{ name: '家用电器', max: 6500 },
|
||||
{ name: '服装箱包', max: 16000 },
|
||||
{ name: '运动户外', max: 30000 },
|
||||
{ name: '手机数码', max: 38000 },
|
||||
{ name: '汽车用品', max: 52000 },
|
||||
{ name: '家具厨具', max: 25000 }
|
||||
]
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: 'Budget vs spending',
|
||||
type: 'radar',
|
||||
itemStyle: {
|
||||
borderRadius: 6,
|
||||
normal: {
|
||||
color: function (params: any) {
|
||||
//自定义颜色
|
||||
const colorList = ['#409EFF', '#67C23A', '#E6A23C', '#F56C6C'];
|
||||
return colorList[params.dataIndex];
|
||||
}
|
||||
}
|
||||
},
|
||||
data: [
|
||||
{
|
||||
value: [4200, 10000, 20000, 35000, 50000, 18000],
|
||||
name: '预定数量'
|
||||
},
|
||||
{
|
||||
value: [5000, 14000, 28000, 26000, 42000, 21000],
|
||||
name: '下单数量'
|
||||
},
|
||||
{
|
||||
value: [5000, 12000, 23000, 18000, 31000, 11000],
|
||||
name: '发货数量'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
} as EChartsOption);
|
||||
radarChart.setOption({
|
||||
title: {
|
||||
show: true,
|
||||
text: '订单状态统计',
|
||||
x: 'center',
|
||||
padding: 15,
|
||||
textStyle: {
|
||||
fontSize: 18,
|
||||
fontStyle: 'normal',
|
||||
fontWeight: 'bold',
|
||||
color: '#337ecc'
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
left: '2%',
|
||||
right: '2%',
|
||||
bottom: '10%',
|
||||
containLabel: true
|
||||
},
|
||||
legend: {
|
||||
x: 'center',
|
||||
y: 'bottom',
|
||||
data: ['预定数量', '下单数量', '发货数量']
|
||||
},
|
||||
radar: {
|
||||
// shape: 'circle',
|
||||
radius: '60%',
|
||||
indicator: [
|
||||
{ name: '家用电器', max: 6500 },
|
||||
{ name: '服装箱包', max: 16000 },
|
||||
{ name: '运动户外', max: 30000 },
|
||||
{ name: '手机数码', max: 38000 },
|
||||
{ name: '汽车用品', max: 52000 },
|
||||
{ name: '家具厨具', max: 25000 }
|
||||
]
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: 'Budget vs spending',
|
||||
type: 'radar',
|
||||
itemStyle: {
|
||||
borderRadius: 6,
|
||||
normal: {
|
||||
color: function (params: any) {
|
||||
//自定义颜色
|
||||
const colorList = ['#409EFF', '#67C23A', '#E6A23C', '#F56C6C'];
|
||||
return colorList[params.dataIndex];
|
||||
}
|
||||
}
|
||||
},
|
||||
data: [
|
||||
{
|
||||
value: [4200, 10000, 20000, 35000, 50000, 18000],
|
||||
name: '预定数量'
|
||||
},
|
||||
{
|
||||
value: [5000, 14000, 28000, 26000, 42000, 21000],
|
||||
name: '下单数量'
|
||||
},
|
||||
{
|
||||
value: [5000, 12000, 23000, 18000, 31000, 11000],
|
||||
name: '发货数量'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
} as EChartsOption);
|
||||
|
||||
chart.value = radarChart;
|
||||
chart.value = radarChart;
|
||||
}
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
beforeDestroy();
|
||||
beforeDestroy();
|
||||
});
|
||||
|
||||
onActivated(() => {
|
||||
activated();
|
||||
activated();
|
||||
});
|
||||
|
||||
onDeactivated(() => {
|
||||
deactivated();
|
||||
deactivated();
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
mounted();
|
||||
nextTick(() => {
|
||||
initChart();
|
||||
});
|
||||
mounted();
|
||||
nextTick(() => {
|
||||
initChart();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,119 +1,119 @@
|
|||
<template>
|
||||
<div class="component-container">
|
||||
<el-card class="project-card">
|
||||
<template #header>
|
||||
<span class="fw-b">有来项目简介</span>
|
||||
</template>
|
||||
<div class="project-card__main">
|
||||
<!-- 项目简介 -->
|
||||
<el-link target="_blank" type="primary" href="https://gitee.com/haoxr">
|
||||
youlai-mall
|
||||
</el-link>
|
||||
是基于Spring Boot 2.6、Spring Cloud 2021 & Alibaba
|
||||
2021、Vue3、Element-Plus、uni-app等主流技术栈构建的一整套全栈开源商城项目,
|
||||
涉及
|
||||
<el-link
|
||||
target="_blank"
|
||||
type="primary"
|
||||
href="https://gitee.com/youlaitech/youlai-mall"
|
||||
>后端微服务</el-link
|
||||
>
|
||||
、
|
||||
<el-link
|
||||
target="_blank"
|
||||
type="success"
|
||||
href="https://gitee.com/youlaitech/youlai-mall-admin"
|
||||
>前端管理</el-link
|
||||
>
|
||||
、
|
||||
<el-link
|
||||
target="_blank"
|
||||
type="warning"
|
||||
href="https://gitee.com/youlaitech/youlai-mall-weapp"
|
||||
>微信小程序
|
||||
</el-link>
|
||||
和
|
||||
<el-link
|
||||
target="_blank"
|
||||
type="danger"
|
||||
href="https://gitee.com/youlaitech/youlai-mall-weapp"
|
||||
>APP应用</el-link
|
||||
>
|
||||
等多端的开发。
|
||||
<el-divider />
|
||||
<div class="component-container">
|
||||
<el-card class="project-card">
|
||||
<template #header>
|
||||
<span class="fw-b">有来项目简介</span>
|
||||
</template>
|
||||
<div class="project-card__main">
|
||||
<!-- 项目简介 -->
|
||||
<el-link target="_blank" type="primary" href="https://gitee.com/haoxr">
|
||||
youlai-mall
|
||||
</el-link>
|
||||
是基于Spring Boot 2.6、Spring Cloud 2021 & Alibaba
|
||||
2021、Vue3、Element-Plus、uni-app等主流技术栈构建的一整套全栈开源商城项目,
|
||||
涉及
|
||||
<el-link
|
||||
target="_blank"
|
||||
type="primary"
|
||||
href="https://gitee.com/youlaitech/youlai-mall"
|
||||
>后端微服务</el-link
|
||||
>
|
||||
、
|
||||
<el-link
|
||||
target="_blank"
|
||||
type="success"
|
||||
href="https://gitee.com/youlaitech/youlai-mall-admin"
|
||||
>前端管理</el-link
|
||||
>
|
||||
、
|
||||
<el-link
|
||||
target="_blank"
|
||||
type="warning"
|
||||
href="https://gitee.com/youlaitech/youlai-mall-weapp"
|
||||
>微信小程序
|
||||
</el-link>
|
||||
和
|
||||
<el-link
|
||||
target="_blank"
|
||||
type="danger"
|
||||
href="https://gitee.com/youlaitech/youlai-mall-weapp"
|
||||
>APP应用</el-link
|
||||
>
|
||||
等多端的开发。
|
||||
<el-divider />
|
||||
|
||||
<!-- 源码地址 -->
|
||||
<el-row :gutter="10">
|
||||
<el-col :span="6">
|
||||
<el-badge value="免费开源" class="fw-b"> 项目地址 </el-badge>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-link
|
||||
target="_blank"
|
||||
type="warning"
|
||||
href="http://youlaitech.gitee.io/youlai-mall"
|
||||
>官方文档(完善中..)</el-link
|
||||
>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-link
|
||||
target="_blank"
|
||||
type="primary"
|
||||
href="https://github.com/youlaitech"
|
||||
>Github</el-link
|
||||
>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-link
|
||||
target="_blank"
|
||||
type="success"
|
||||
href="https://gitee.com/youlaiorg"
|
||||
>码云</el-link
|
||||
>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-divider />
|
||||
<!-- 技术栈 -->
|
||||
<el-row :gutter="10">
|
||||
<el-col :span="6" class="fw-b"> 后端技术栈 </el-col>
|
||||
<el-col :span="18">
|
||||
Spring Boot、Spring Cloud & Alibaba、Spring Security
|
||||
OAuth2、JWT、Elastic Stack 、K8s...
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-divider />
|
||||
<el-row :gutter="10">
|
||||
<el-col :span="6" class="fw-b"> 前端技术栈 </el-col>
|
||||
<el-col :span="18">
|
||||
Vue3、TypeScript、Element-Plus、uni-app、vue3-element-admin ...
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
<!-- 源码地址 -->
|
||||
<el-row :gutter="10">
|
||||
<el-col :span="6">
|
||||
<el-badge value="免费开源" class="fw-b"> 项目地址 </el-badge>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-link
|
||||
target="_blank"
|
||||
type="warning"
|
||||
href="http://youlaitech.gitee.io/youlai-mall"
|
||||
>官方文档(完善中..)</el-link
|
||||
>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-link
|
||||
target="_blank"
|
||||
type="primary"
|
||||
href="https://github.com/youlaitech"
|
||||
>Github</el-link
|
||||
>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-link
|
||||
target="_blank"
|
||||
type="success"
|
||||
href="https://gitee.com/youlaiorg"
|
||||
>码云</el-link
|
||||
>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-divider />
|
||||
<!-- 技术栈 -->
|
||||
<el-row :gutter="10">
|
||||
<el-col :span="6" class="fw-b"> 后端技术栈 </el-col>
|
||||
<el-col :span="18">
|
||||
Spring Boot、Spring Cloud & Alibaba、Spring Security
|
||||
OAuth2、JWT、Elastic Stack 、K8s...
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-divider />
|
||||
<el-row :gutter="10">
|
||||
<el-col :span="6" class="fw-b"> 前端技术栈 </el-col>
|
||||
<el-col :span="18">
|
||||
Vue3、TypeScript、Element-Plus、uni-app、vue3-element-admin ...
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'index'
|
||||
name: 'index'
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.component-container {
|
||||
.project-card {
|
||||
font-size: 14px;
|
||||
.project-card {
|
||||
font-size: 14px;
|
||||
|
||||
&__main {
|
||||
line-height: 28px;
|
||||
height: 320px;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
}
|
||||
&__main {
|
||||
line-height: 28px;
|
||||
height: 320px;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
.fw-b {
|
||||
font-weight: bold;
|
||||
}
|
||||
.fw-b {
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -1,75 +1,75 @@
|
|||
<!-- 团队介绍 -->
|
||||
<template>
|
||||
<div class="component-container">
|
||||
<el-card class="team-card">
|
||||
<template #header>
|
||||
<span class="fw-b">有来开源组织 & 技术团队</span>
|
||||
</template>
|
||||
<el-tabs v-model="teamActiveName">
|
||||
<el-tab-pane label="开发者" name="developer">
|
||||
<div class="developer" ref="dev_wrapper">
|
||||
<ul class="developer__container">
|
||||
<li
|
||||
class="developer__item"
|
||||
v-for="(item, index) in developers"
|
||||
:key="index"
|
||||
>
|
||||
<div class="developer__inner">
|
||||
<el-image
|
||||
class="developer__img"
|
||||
:src="item.imgUrl"
|
||||
:preview-src-list="[item.imgUrl]"
|
||||
></el-image>
|
||||
<div class="developer__info">
|
||||
<span class="developer__nickname">{{ item.nickname }}</span>
|
||||
<div class="developer__position">
|
||||
<el-tag
|
||||
v-for="(position, i) in item.positions"
|
||||
:type="(colors[i % colors.length] as any)"
|
||||
:class="i !== 0 ? 'f-ml' : ''"
|
||||
size="small"
|
||||
:key="i"
|
||||
>{{ position }}</el-tag
|
||||
>
|
||||
</div>
|
||||
<div class="developer__homepage">
|
||||
<a :href="item.homepage" target="_blank">个人主页</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<el-image class="developer__indicator" :src="indicatorImgUrl" />
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
<div class="component-container">
|
||||
<el-card class="team-card">
|
||||
<template #header>
|
||||
<span class="fw-b">有来开源组织 & 技术团队</span>
|
||||
</template>
|
||||
<el-tabs v-model="teamActiveName">
|
||||
<el-tab-pane label="开发者" name="developer">
|
||||
<div class="developer" ref="dev_wrapper">
|
||||
<ul class="developer__container">
|
||||
<li
|
||||
class="developer__item"
|
||||
v-for="(item, index) in developers"
|
||||
:key="index"
|
||||
>
|
||||
<div class="developer__inner">
|
||||
<el-image
|
||||
class="developer__img"
|
||||
:src="item.imgUrl"
|
||||
:preview-src-list="[item.imgUrl]"
|
||||
></el-image>
|
||||
<div class="developer__info">
|
||||
<span class="developer__nickname">{{ item.nickname }}</span>
|
||||
<div class="developer__position">
|
||||
<el-tag
|
||||
v-for="(position, i) in item.positions"
|
||||
:type="(colors[i % colors.length] as any)"
|
||||
:class="i !== 0 ? 'f-ml' : ''"
|
||||
size="small"
|
||||
:key="i"
|
||||
>{{ position }}</el-tag
|
||||
>
|
||||
</div>
|
||||
<div class="developer__homepage">
|
||||
<a :href="item.homepage" target="_blank">个人主页</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<el-image class="developer__indicator" :src="indicatorImgUrl" />
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="交流群" name="2">
|
||||
<div class="group">
|
||||
<el-image
|
||||
class="group-img"
|
||||
src="https://www.youlai.tech/files/blog/youlaiqun.png"
|
||||
:preview-src-list="[
|
||||
'https://www.youlai.tech/files/blog/youlaiqun.png'
|
||||
]"
|
||||
/>
|
||||
<div class="group-tip">
|
||||
群二维码过期可添加开发者微信由其拉入群,备注「有来」即可。
|
||||
</div>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="交流群" name="2">
|
||||
<div class="group">
|
||||
<el-image
|
||||
class="group-img"
|
||||
src="https://www.youlai.tech/files/blog/youlaiqun.png"
|
||||
:preview-src-list="[
|
||||
'https://www.youlai.tech/files/blog/youlaiqun.png'
|
||||
]"
|
||||
/>
|
||||
<div class="group-tip">
|
||||
群二维码过期可添加开发者微信由其拉入群,备注「有来」即可。
|
||||
</div>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="加入我们" name="3">
|
||||
<div class="join">
|
||||
<p>1. 人品良好、善于思考、执行力强;</p>
|
||||
<p>2. 熟悉项目,且至少给项目提交(过)一个PR;</p>
|
||||
<p>3. Git代码库活跃,个人主页或博客完善者优先;</p>
|
||||
<p>4. 过分优秀者我们会主动联系您...</p>
|
||||
<div class="join__desc">申请加入方式: 添加开发者微信申请即可。</div>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</el-card>
|
||||
</div>
|
||||
<el-tab-pane label="加入我们" name="3">
|
||||
<div class="join">
|
||||
<p>1. 人品良好、善于思考、执行力强;</p>
|
||||
<p>2. 熟悉项目,且至少给项目提交(过)一个PR;</p>
|
||||
<p>3. Git代码库活跃,个人主页或博客完善者优先;</p>
|
||||
<p>4. 过分优秀者我们会主动联系您...</p>
|
||||
<div class="join__desc">申请加入方式: 添加开发者微信申请即可。</div>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
|
@ -77,32 +77,32 @@ import { nextTick, onMounted, reactive, ref, toRefs, watchEffect } from 'vue';
|
|||
import BScroll from 'better-scroll';
|
||||
|
||||
const state = reactive({
|
||||
teamActiveName: 'developer',
|
||||
developers: [
|
||||
{
|
||||
imgUrl: 'https://s2.loli.net/2022/04/06/yRx8uzj4emA5QVr.jpg',
|
||||
nickname: '郝先瑞',
|
||||
positions: ['后端', '前端', '文档'],
|
||||
homepage: 'https://www.cnblogs.com/haoxianrui/'
|
||||
},
|
||||
{
|
||||
imgUrl: 'https://s2.loli.net/2022/04/06/cQihGv9uPsTjXk1.jpg',
|
||||
nickname: '张川',
|
||||
positions: ['后端', '前端'],
|
||||
homepage: 'https://blog.csdn.net/qq_41595149'
|
||||
},
|
||||
{
|
||||
imgUrl: 'https://s2.loli.net/2022/04/07/2IiOYBHnRGKgCSd.jpg',
|
||||
nickname: '张加林',
|
||||
positions: ['DevOps'],
|
||||
homepage: 'https://gitee.com/ximy'
|
||||
}
|
||||
],
|
||||
colors: ['', 'success', 'warning', 'danger'],
|
||||
indicatorImgUrl: new URL(
|
||||
`../../../../assets/index/indicator.png`,
|
||||
import.meta.url
|
||||
).href
|
||||
teamActiveName: 'developer',
|
||||
developers: [
|
||||
{
|
||||
imgUrl: 'https://s2.loli.net/2022/04/06/yRx8uzj4emA5QVr.jpg',
|
||||
nickname: '郝先瑞',
|
||||
positions: ['后端', '前端', '文档'],
|
||||
homepage: 'https://www.cnblogs.com/haoxianrui/'
|
||||
},
|
||||
{
|
||||
imgUrl: 'https://s2.loli.net/2022/04/06/cQihGv9uPsTjXk1.jpg',
|
||||
nickname: '张川',
|
||||
positions: ['后端', '前端'],
|
||||
homepage: 'https://blog.csdn.net/qq_41595149'
|
||||
},
|
||||
{
|
||||
imgUrl: 'https://s2.loli.net/2022/04/07/2IiOYBHnRGKgCSd.jpg',
|
||||
nickname: '张加林',
|
||||
positions: ['DevOps'],
|
||||
homepage: 'https://gitee.com/ximy'
|
||||
}
|
||||
],
|
||||
colors: ['', 'success', 'warning', 'danger'],
|
||||
indicatorImgUrl: new URL(
|
||||
`../../../../assets/index/indicator.png`,
|
||||
import.meta.url
|
||||
).href
|
||||
});
|
||||
|
||||
const { teamActiveName, developers, colors, indicatorImgUrl } = toRefs(state);
|
||||
|
|
@ -112,17 +112,17 @@ let bScroll = reactive({});
|
|||
const dev_wrapper = ref<HTMLElement | any>(null);
|
||||
|
||||
onMounted(() => {
|
||||
bScroll = new BScroll(dev_wrapper.value, {
|
||||
mouseWheel: true, //开启鼠标滚轮
|
||||
disableMouse: false, //启用鼠标拖动
|
||||
scrollX: true //X轴滚动启用
|
||||
});
|
||||
bScroll = new BScroll(dev_wrapper.value, {
|
||||
mouseWheel: true, //开启鼠标滚轮
|
||||
disableMouse: false, //启用鼠标拖动
|
||||
scrollX: true //X轴滚动启用
|
||||
});
|
||||
});
|
||||
|
||||
watchEffect(() => {
|
||||
nextTick(() => {
|
||||
bScroll && (bScroll as any).refresh();
|
||||
});
|
||||
nextTick(() => {
|
||||
bScroll && (bScroll as any).refresh();
|
||||
});
|
||||
});
|
||||
|
||||
// let bScroll = reactive({})
|
||||
|
|
@ -146,116 +146,116 @@ watchEffect(() => {
|
|||
|
||||
<style lang="scss" scoped>
|
||||
.component-container {
|
||||
.team-card {
|
||||
font-size: 14px;
|
||||
.team-card {
|
||||
font-size: 14px;
|
||||
|
||||
.el-tabs__content {
|
||||
.el-tab-pane {
|
||||
height: 265px;
|
||||
}
|
||||
}
|
||||
.el-tabs__content {
|
||||
.el-tab-pane {
|
||||
height: 265px;
|
||||
}
|
||||
}
|
||||
|
||||
.developer {
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
.developer {
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
|
||||
&__container {
|
||||
display: inline-flex;
|
||||
overflow: hidden;
|
||||
justify-content: flex-start;
|
||||
padding: 10px;
|
||||
&__container {
|
||||
display: inline-flex;
|
||||
overflow: hidden;
|
||||
justify-content: flex-start;
|
||||
padding: 10px;
|
||||
|
||||
.developer__item {
|
||||
&:not(:first-child) {
|
||||
margin-left: 20px;
|
||||
}
|
||||
.developer__item {
|
||||
&:not(:first-child) {
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
align-items: center;
|
||||
list-style: none;
|
||||
width: 180px;
|
||||
min-width: 180px;
|
||||
align-items: center;
|
||||
list-style: none;
|
||||
width: 180px;
|
||||
min-width: 180px;
|
||||
|
||||
.developer__inner {
|
||||
border: 1px solid #cccccc;
|
||||
border-radius: 5px;
|
||||
box-shadow: 6px 6px 6px #aaa;
|
||||
padding: 8px;
|
||||
text-align: center;
|
||||
.developer__inner {
|
||||
border: 1px solid #cccccc;
|
||||
border-radius: 5px;
|
||||
box-shadow: 6px 6px 6px #aaa;
|
||||
padding: 8px;
|
||||
text-align: center;
|
||||
|
||||
.developer__img {
|
||||
height: 100px;
|
||||
width: 100px;
|
||||
}
|
||||
.developer__img {
|
||||
height: 100px;
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
.developer__info {
|
||||
padding: 6px;
|
||||
font-size: 14px;
|
||||
.developer__info {
|
||||
padding: 6px;
|
||||
font-size: 14px;
|
||||
|
||||
.developer__position {
|
||||
margin-top: 10px;
|
||||
}
|
||||
.developer__position {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.developer__homepage {
|
||||
margin-top: 16px;
|
||||
.developer__homepage {
|
||||
margin-top: 16px;
|
||||
|
||||
a {
|
||||
display: inline-block;
|
||||
padding: 4px 10px;
|
||||
color: #409eff;
|
||||
border: 1px solid #409eff;
|
||||
border-radius: 5px;
|
||||
background: #ecf5ff;
|
||||
a {
|
||||
display: inline-block;
|
||||
padding: 4px 10px;
|
||||
color: #409eff;
|
||||
border: 1px solid #409eff;
|
||||
border-radius: 5px;
|
||||
background: #ecf5ff;
|
||||
|
||||
&:hover {
|
||||
background: #409eff;
|
||||
color: #ffffff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
&:hover {
|
||||
background: #409eff;
|
||||
color: #ffffff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__indicator {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
width: 120px;
|
||||
height: 100px;
|
||||
}
|
||||
}
|
||||
&__indicator {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
width: 120px;
|
||||
height: 100px;
|
||||
}
|
||||
}
|
||||
|
||||
.join {
|
||||
height: 240px;
|
||||
.join {
|
||||
height: 240px;
|
||||
|
||||
p {
|
||||
font-weight: bold;
|
||||
}
|
||||
p {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
&__desc {
|
||||
margin-top: 20px;
|
||||
color: #409eff;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
&__desc {
|
||||
margin-top: 20px;
|
||||
color: #409eff;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
.group {
|
||||
height: 254px;
|
||||
.group {
|
||||
height: 254px;
|
||||
|
||||
&-img {
|
||||
height: 200px;
|
||||
width: 200px;
|
||||
}
|
||||
}
|
||||
}
|
||||
&-img {
|
||||
height: 200px;
|
||||
width: 200px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.fw-b {
|
||||
font-weight: bold;
|
||||
}
|
||||
.fw-b {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.f-ml {
|
||||
margin-left: 5px;
|
||||
}
|
||||
.f-ml {
|
||||
margin-left: 5px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
<template>
|
||||
<div class="dashboard-container">
|
||||
<github-corner class="github-corner" />
|
||||
<div class="dashboard-container">
|
||||
<github-corner class="github-corner" />
|
||||
|
||||
<!-- 数据 -->
|
||||
<el-row :gutter="40" class="card-panel__col">
|
||||
<!-- <el-col :xs="24" :lg="6" class="card-panel__col">
|
||||
<!-- 数据 -->
|
||||
<el-row :gutter="40" class="card-panel__col">
|
||||
<!-- <el-col :xs="24" :lg="6" class="card-panel__col">
|
||||
<div class="card-panel">
|
||||
<div class="card-panel-icon-wrapper" style="margin-top: -10px">
|
||||
<el-image style="width:200px; height: 100px"
|
||||
|
|
@ -22,99 +22,99 @@
|
|||
</div>
|
||||
</el-col>-->
|
||||
|
||||
<el-col :xs="24" :sm="12" :lg="6" class="card-panel__col">
|
||||
<div class="card-panel">
|
||||
<div class="card-panel-icon-wrapper icon-people">
|
||||
<svg-icon icon-class="uv" class-name="card-panel-icon" />
|
||||
</div>
|
||||
<div class="card-panel-description">
|
||||
<div class="card-panel-text">访问数</div>
|
||||
<div class="card-panel-num">1000</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :lg="6" class="card-panel__col">
|
||||
<div class="card-panel">
|
||||
<div class="card-panel-icon-wrapper icon-people">
|
||||
<svg-icon icon-class="uv" class-name="card-panel-icon" />
|
||||
</div>
|
||||
<div class="card-panel-description">
|
||||
<div class="card-panel-text">访问数</div>
|
||||
<div class="card-panel-num">1000</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
|
||||
<el-col :xs="24" :sm="12" :lg="6" class="card-panel__col">
|
||||
<div class="card-panel">
|
||||
<div class="card-panel-icon-wrapper icon-message">
|
||||
<svg-icon icon-class="message" class-name="card-panel-icon" />
|
||||
</div>
|
||||
<div class="card-panel-description">
|
||||
<div class="card-panel-text">消息数</div>
|
||||
<div class="card-panel-num">1000</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :lg="6" class="card-panel__col">
|
||||
<div class="card-panel">
|
||||
<div class="card-panel-icon-wrapper icon-message">
|
||||
<svg-icon icon-class="message" class-name="card-panel-icon" />
|
||||
</div>
|
||||
<div class="card-panel-description">
|
||||
<div class="card-panel-text">消息数</div>
|
||||
<div class="card-panel-num">1000</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
|
||||
<el-col :xs="24" :sm="12" :lg="6" class="card-panel__col">
|
||||
<div class="card-panel">
|
||||
<div class="card-panel-icon-wrapper icon-money">
|
||||
<svg-icon icon-class="money" class-name="card-panel-icon" />
|
||||
</div>
|
||||
<div class="card-panel-description">
|
||||
<div class="card-panel-text">收入金额</div>
|
||||
<div class="card-panel-num">1000</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :lg="6" class="card-panel__col">
|
||||
<div class="card-panel">
|
||||
<div class="card-panel-icon-wrapper icon-shopping">
|
||||
<svg-icon icon-class="shopping" class-name="card-panel-icon" />
|
||||
</div>
|
||||
<div class="card-panel-description">
|
||||
<div class="card-panel-text">订单数</div>
|
||||
<div class="card-panel-num">1000</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-col :xs="24" :sm="12" :lg="6" class="card-panel__col">
|
||||
<div class="card-panel">
|
||||
<div class="card-panel-icon-wrapper icon-money">
|
||||
<svg-icon icon-class="money" class-name="card-panel-icon" />
|
||||
</div>
|
||||
<div class="card-panel-description">
|
||||
<div class="card-panel-text">收入金额</div>
|
||||
<div class="card-panel-num">1000</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :lg="6" class="card-panel__col">
|
||||
<div class="card-panel">
|
||||
<div class="card-panel-icon-wrapper icon-shopping">
|
||||
<svg-icon icon-class="shopping" class-name="card-panel-icon" />
|
||||
</div>
|
||||
<div class="card-panel-description">
|
||||
<div class="card-panel-text">订单数</div>
|
||||
<div class="card-panel-num">1000</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 项目 + 团队成员介绍 -->
|
||||
<el-row :gutter="40">
|
||||
<!-- 项目介绍 -->
|
||||
<el-col :md="12" :lg="12" class="card-panel__col">
|
||||
<Project />
|
||||
</el-col>
|
||||
<!-- 项目 + 团队成员介绍 -->
|
||||
<el-row :gutter="40">
|
||||
<!-- 项目介绍 -->
|
||||
<el-col :md="12" :lg="12" class="card-panel__col">
|
||||
<Project />
|
||||
</el-col>
|
||||
|
||||
<!-- 团队介绍 -->
|
||||
<el-col :md="12" :lg="12" class="card-panel__col">
|
||||
<Team />
|
||||
</el-col>
|
||||
</el-row>
|
||||
<!-- 团队介绍 -->
|
||||
<el-col :md="12" :lg="12" class="card-panel__col">
|
||||
<Team />
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- Echarts 图表 -->
|
||||
<el-row :gutter="40" style="margin-top: 20px">
|
||||
<el-col :sm="24" :lg="8" class="card-panel__col">
|
||||
<BarChart
|
||||
id="barChart"
|
||||
height="400px"
|
||||
width="100%"
|
||||
class="chart-container"
|
||||
/>
|
||||
</el-col>
|
||||
<!-- Echarts 图表 -->
|
||||
<el-row :gutter="40" style="margin-top: 20px">
|
||||
<el-col :sm="24" :lg="8" class="card-panel__col">
|
||||
<BarChart
|
||||
id="barChart"
|
||||
height="400px"
|
||||
width="100%"
|
||||
class="chart-container"
|
||||
/>
|
||||
</el-col>
|
||||
|
||||
<el-col :xs="24" :sm="12" :lg="8" class="card-panel__col">
|
||||
<PieChart
|
||||
id="pieChart"
|
||||
height="400px"
|
||||
width="100%"
|
||||
class="chart-container"
|
||||
/>
|
||||
<!--订单漏斗图-->
|
||||
<!--<FunnelChart id="funnelChart" height="400px" width="100%" class="chart-container"/>-->
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :lg="8" class="card-panel__col">
|
||||
<PieChart
|
||||
id="pieChart"
|
||||
height="400px"
|
||||
width="100%"
|
||||
class="chart-container"
|
||||
/>
|
||||
<!--订单漏斗图-->
|
||||
<!--<FunnelChart id="funnelChart" height="400px" width="100%" class="chart-container"/>-->
|
||||
</el-col>
|
||||
|
||||
<el-col :xs="24" :sm="12" :lg="8" class="card-panel__col">
|
||||
<RadarChart
|
||||
id="radarChart"
|
||||
height="400px"
|
||||
width="100%"
|
||||
class="chart-container"
|
||||
/>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
<el-col :xs="24" :sm="12" :lg="8" class="card-panel__col">
|
||||
<RadarChart
|
||||
id="radarChart"
|
||||
height="400px"
|
||||
width="100%"
|
||||
class="chart-container"
|
||||
/>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
|
@ -131,155 +131,155 @@ import Team from './components/Team/index.vue';
|
|||
|
||||
<style lang="scss" scoped>
|
||||
.dashboard-container {
|
||||
padding: 24px;
|
||||
background-color: rgb(240, 242, 245);
|
||||
position: relative;
|
||||
padding: 24px;
|
||||
background-color: rgb(240, 242, 245);
|
||||
position: relative;
|
||||
|
||||
.github-corner {
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
border: 0;
|
||||
right: 0;
|
||||
z-index: 99;
|
||||
}
|
||||
.github-corner {
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
border: 0;
|
||||
right: 0;
|
||||
z-index: 99;
|
||||
}
|
||||
|
||||
.box-center {
|
||||
margin: 0 auto;
|
||||
display: table;
|
||||
}
|
||||
.box-center {
|
||||
margin: 0 auto;
|
||||
display: table;
|
||||
}
|
||||
|
||||
.user-profile {
|
||||
.user-name {
|
||||
}
|
||||
.user-profile {
|
||||
.user-name {
|
||||
}
|
||||
|
||||
.box-center {
|
||||
padding-top: 10px;
|
||||
}
|
||||
.box-center {
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
.user-role {
|
||||
padding-top: 10px;
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
}
|
||||
.user-role {
|
||||
padding-top: 10px;
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.box-social {
|
||||
padding-top: 30px;
|
||||
.box-social {
|
||||
padding-top: 30px;
|
||||
|
||||
.el-table {
|
||||
border-top: 1px solid #dfe6ec;
|
||||
}
|
||||
}
|
||||
.el-table {
|
||||
border-top: 1px solid #dfe6ec;
|
||||
}
|
||||
}
|
||||
|
||||
.user-follow {
|
||||
padding-top: 20px;
|
||||
}
|
||||
}
|
||||
.user-follow {
|
||||
padding-top: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.card-panel__col {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.card-panel__col {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.card-panel {
|
||||
height: 108px;
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
color: #666;
|
||||
background: #fff;
|
||||
box-shadow: 4px 4px 40px rgba(0, 0, 0, 0.05);
|
||||
border-color: rgba(0, 0, 0, 0.05);
|
||||
.card-panel {
|
||||
height: 108px;
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
color: #666;
|
||||
background: #fff;
|
||||
box-shadow: 4px 4px 40px rgba(0, 0, 0, 0.05);
|
||||
border-color: rgba(0, 0, 0, 0.05);
|
||||
|
||||
&:hover {
|
||||
.card-panel-icon-wrapper {
|
||||
color: #fff;
|
||||
}
|
||||
&:hover {
|
||||
.card-panel-icon-wrapper {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.icon-user {
|
||||
background: #e77541;
|
||||
}
|
||||
.icon-user {
|
||||
background: #e77541;
|
||||
}
|
||||
|
||||
.icon-people {
|
||||
background: #40c9c6;
|
||||
}
|
||||
.icon-people {
|
||||
background: #40c9c6;
|
||||
}
|
||||
|
||||
.icon-message {
|
||||
background: #36a3f7;
|
||||
}
|
||||
.icon-message {
|
||||
background: #36a3f7;
|
||||
}
|
||||
|
||||
.icon-money {
|
||||
background: #f4516c;
|
||||
}
|
||||
.icon-money {
|
||||
background: #f4516c;
|
||||
}
|
||||
|
||||
.icon-shopping {
|
||||
background: #34bfa3;
|
||||
}
|
||||
}
|
||||
.icon-shopping {
|
||||
background: #34bfa3;
|
||||
}
|
||||
}
|
||||
|
||||
.icon-people {
|
||||
color: #40c9c6;
|
||||
.icon-people {
|
||||
color: #40c9c6;
|
||||
|
||||
.svg-icon {
|
||||
width: 4em !important;
|
||||
height: 4em !important;
|
||||
}
|
||||
}
|
||||
.svg-icon {
|
||||
width: 4em !important;
|
||||
height: 4em !important;
|
||||
}
|
||||
}
|
||||
|
||||
.icon-message {
|
||||
color: #36a3f7;
|
||||
.icon-message {
|
||||
color: #36a3f7;
|
||||
|
||||
.svg-icon {
|
||||
width: 4em !important;
|
||||
height: 4em !important;
|
||||
}
|
||||
}
|
||||
.svg-icon {
|
||||
width: 4em !important;
|
||||
height: 4em !important;
|
||||
}
|
||||
}
|
||||
|
||||
.icon-money {
|
||||
color: #f4516c;
|
||||
.icon-money {
|
||||
color: #f4516c;
|
||||
|
||||
.svg-icon {
|
||||
width: 4em !important;
|
||||
height: 4em !important;
|
||||
}
|
||||
}
|
||||
.svg-icon {
|
||||
width: 4em !important;
|
||||
height: 4em !important;
|
||||
}
|
||||
}
|
||||
|
||||
.icon-shopping {
|
||||
color: #34bfa3;
|
||||
.icon-shopping {
|
||||
color: #34bfa3;
|
||||
|
||||
.svg-icon {
|
||||
width: 4em !important;
|
||||
height: 4em !important;
|
||||
}
|
||||
}
|
||||
.svg-icon {
|
||||
width: 4em !important;
|
||||
height: 4em !important;
|
||||
}
|
||||
}
|
||||
|
||||
.card-panel-icon-wrapper {
|
||||
float: left;
|
||||
margin: 14px 0 0 14px;
|
||||
padding: 16px;
|
||||
transition: all 0.38s ease-out;
|
||||
border-radius: 6px;
|
||||
}
|
||||
.card-panel-icon-wrapper {
|
||||
float: left;
|
||||
margin: 14px 0 0 14px;
|
||||
padding: 16px;
|
||||
transition: all 0.38s ease-out;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.card-panel-description {
|
||||
float: right;
|
||||
font-weight: bold;
|
||||
margin: 26px 20px 0;
|
||||
.card-panel-description {
|
||||
float: right;
|
||||
font-weight: bold;
|
||||
margin: 26px 20px 0;
|
||||
|
||||
.card-panel-text {
|
||||
line-height: 18px;
|
||||
color: rgba(0, 0, 0, 0.45);
|
||||
font-size: 16px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.card-panel-text {
|
||||
line-height: 18px;
|
||||
color: rgba(0, 0, 0, 0.45);
|
||||
font-size: 16px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.card-panel-num {
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.card-panel-num {
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.chart-container {
|
||||
background: #ffffff;
|
||||
}
|
||||
.chart-container {
|
||||
background: #ffffff;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue