mirror of
https://github.com/continew-org/continew-admin-ui.git
synced 2025-10-24 04:57:08 +08:00
refactor: 更换 ESLint 配置为 @antfu/eslint-config
This commit is contained in:
@@ -1,2 +0,0 @@
|
|||||||
dist
|
|
||||||
node_modules
|
|
@@ -1,68 +0,0 @@
|
|||||||
/* eslint-env node */
|
|
||||||
require('@rushstack/eslint-patch/modern-module-resolution')
|
|
||||||
|
|
||||||
// @see https://eslint.bootcss.com/docs/rules/
|
|
||||||
module.exports = {
|
|
||||||
root: true,
|
|
||||||
env: {
|
|
||||||
browser: true,
|
|
||||||
es2021: true,
|
|
||||||
node: true,
|
|
||||||
jest: true
|
|
||||||
},
|
|
||||||
/* 指定如何解析语法 */
|
|
||||||
parser: 'vue-eslint-parser',
|
|
||||||
/** 优先级低于 parse 的语法解析配置 */
|
|
||||||
parserOptions: {
|
|
||||||
ecmaVersion: 'latest',
|
|
||||||
sourceType: 'module',
|
|
||||||
parser: '@typescript-eslint/parser',
|
|
||||||
jsxPragma: 'React',
|
|
||||||
ecmaFeatures: {
|
|
||||||
jsx: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
/* 继承已有的规则(全部规则默认是关闭的, 这个配置项开启推荐规则, 推荐规则参照文档) */
|
|
||||||
extends: [
|
|
||||||
'eslint:recommended', // 比如: 函数不能重名、对象不能出现重复key
|
|
||||||
'plugin:vue/vue3-essential', // vue3语法规则
|
|
||||||
'plugin:vue/vue3-recommended',
|
|
||||||
'@vue/eslint-config-typescript/recommended', // ts语法规则
|
|
||||||
'@vue/eslint-config-prettier'
|
|
||||||
],
|
|
||||||
plugins: ['vue', '@typescript-eslint'],
|
|
||||||
/*
|
|
||||||
* "off" 或 0 ==> 关闭规则
|
|
||||||
* "warn" 或 1 ==> 打开的规则作为警告(不影响代码执行)
|
|
||||||
* "error" 或 2 ==> 规则作为一个错误(代码不能执行,界面报错)
|
|
||||||
*/
|
|
||||||
rules: {
|
|
||||||
// eslint(https://eslint.bootcss.com/docs/rules/)
|
|
||||||
'no-var': 'error', // 要求使用 let 或 const 而不是 var
|
|
||||||
'no-multiple-empty-lines': ['warn', { max: 1 }], // 不允许多个空行
|
|
||||||
'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
|
|
||||||
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
|
|
||||||
'no-unexpected-multiline': 'error', // 禁止空余的多行
|
|
||||||
'no-useless-escape': 'off', // 禁止不必要的转义字符
|
|
||||||
|
|
||||||
// typeScript (https://typescript-eslint.io/rules)
|
|
||||||
'@typescript-eslint/no-unused-vars': 'error', // 禁止定义未使用的变量
|
|
||||||
'@typescript-eslint/prefer-ts-expect-error': 'error', // 禁止使用 @ts-ignore
|
|
||||||
'@typescript-eslint/no-explicit-any': 'off', // 禁止使用 any 类型
|
|
||||||
'@typescript-eslint/no-non-null-assertion': 'off',
|
|
||||||
'@typescript-eslint/no-namespace': 'off', // 禁止使用自定义 TypeScript 模块和命名空间
|
|
||||||
'@typescript-eslint/semi': 'off',
|
|
||||||
|
|
||||||
// eslint-plugin-vue (https://eslint.vuejs.org/rules/)
|
|
||||||
'vue/multi-word-component-names': 'off', // 要求组件名称始终为 “-” 链接的单词
|
|
||||||
'vue/script-setup-uses-vars': 'error', // 防止<script setup>使用的变量<template>被标记为未使用
|
|
||||||
'vue/no-mutating-props': 'off', // 不允许组件 prop 的改变
|
|
||||||
'vue/no-reserved-component-names': 'off', // 不允许在组件定义中使用保留名称
|
|
||||||
'vue/attribute-hyphenation': 'off', // 对模板中的自定义组件强制执行属性命名样式
|
|
||||||
|
|
||||||
'vue/padding-line-between-blocks': [2, 'always'], // 模板 template script style 之间空一行
|
|
||||||
'vue/component-name-in-template-casing': [2, 'PascalCase'], // 模板中的自定义组件名采用PascalCase方式
|
|
||||||
'vue/custom-event-name-casing': [2, 'kebab-case'], // emit事件名采用kebab-case方式
|
|
||||||
'vue/v-on-event-hyphenation': [1, 'always', { autofix: true }] // 组件事件名采用@kebab-case方式
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,7 +0,0 @@
|
|||||||
/dist/*
|
|
||||||
.local
|
|
||||||
/node_modules/**
|
|
||||||
|
|
||||||
**/*.sh
|
|
||||||
|
|
||||||
/public/*
|
|
@@ -1,14 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
printWidth: 120, // 单行字符数, 默认80
|
|
||||||
tabWidth: 2, // 缩进字节数, 默认2
|
|
||||||
semi: false, // 句尾使用分号, 默认true
|
|
||||||
singleQuote: true, // 使用单引号代替双引号, 默认false
|
|
||||||
trailingComma: 'none', // 多行时尽可能打印尾随逗号, 默认'none', 可选: ['none', 'es5', 'all']
|
|
||||||
endOfLine: 'auto' // 结束行形式, 默认'auto', 结尾是 \n \r \n\r auto
|
|
||||||
|
|
||||||
// arrowParens: 'avoid', // 在单个箭头函数参数周围加上括号, 默认'avoid', 可选: ['avoid', 'always'] avoid: 尽可能 always: 始终
|
|
||||||
// bracketSpacing: true, // 在对象文字中打印括号之间的空格, 默认true, 示例: true -- { name: 'abc' } false -- {name:'abc'}
|
|
||||||
// useTabs: false, // 使用制表符 tab 缩进行而不是空格, 默认false
|
|
||||||
// bracketSpacing: true, //在对象前后添加空格-obj: { foo: bar }
|
|
||||||
// jsxSingleQuote: false, // 在JSX中使用单引号代替双引号, 默认false
|
|
||||||
}
|
|
52
.vscode/settings.json
vendored
52
.vscode/settings.json
vendored
@@ -1,13 +1,47 @@
|
|||||||
{
|
{
|
||||||
// 配置该项, 新建文件时默认就是space: 2
|
// Enable the ESlint flat config support
|
||||||
"editor.tabSize": 2,
|
// (remove this if your ESLint extension above v3.0.5)
|
||||||
// 保存的时候自动格式化
|
"eslint.experimental.useFlatConfig": true,
|
||||||
|
|
||||||
|
// Disable the default formatter, use eslint instead
|
||||||
|
"prettier.enable": false,
|
||||||
"editor.formatOnSave": true,
|
"editor.formatOnSave": true,
|
||||||
// 默认格式化工具选择prettier
|
|
||||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
// Auto fix
|
||||||
// 开启自动修复
|
|
||||||
"editor.codeActionsOnSave": {
|
"editor.codeActionsOnSave": {
|
||||||
"source.fixAll": "never",
|
"source.fixAll.eslint": "explicit",
|
||||||
"source.fixAll.eslint": "explicit"
|
"source.organizeImports": "never"
|
||||||
}
|
},
|
||||||
|
|
||||||
|
// Silent the stylistic rules in you IDE, but still auto fix them
|
||||||
|
"eslint.rules.customizations": [
|
||||||
|
{ "rule": "style/*", "severity": "off" },
|
||||||
|
{ "rule": "format/*", "severity": "off" },
|
||||||
|
{ "rule": "*-indent", "severity": "off" },
|
||||||
|
{ "rule": "*-spacing", "severity": "off" },
|
||||||
|
{ "rule": "*-spaces", "severity": "off" },
|
||||||
|
{ "rule": "*-order", "severity": "off" },
|
||||||
|
{ "rule": "*-dangle", "severity": "off" },
|
||||||
|
{ "rule": "*-newline", "severity": "off" },
|
||||||
|
{ "rule": "*quotes", "severity": "off" },
|
||||||
|
{ "rule": "*semi", "severity": "off" }
|
||||||
|
],
|
||||||
|
|
||||||
|
// Enable eslint for all supported languages
|
||||||
|
"eslint.validate": [
|
||||||
|
"javascript",
|
||||||
|
"javascriptreact",
|
||||||
|
"typescript",
|
||||||
|
"typescriptreact",
|
||||||
|
"vue",
|
||||||
|
"html",
|
||||||
|
"markdown",
|
||||||
|
"json",
|
||||||
|
"jsonc",
|
||||||
|
"yaml",
|
||||||
|
"toml",
|
||||||
|
"gql",
|
||||||
|
"graphql",
|
||||||
|
"astro"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
17
README.md
17
README.md
@@ -224,9 +224,9 @@ pnpm dev
|
|||||||
## 项目结构
|
## 项目结构
|
||||||
|
|
||||||
```
|
```
|
||||||
continew-admin-ui # 前端项目
|
continew-admin-ui
|
||||||
├─ config # Vite 插件配置
|
├─ config # Vite 插件配置
|
||||||
├─ public # 公共静态资源(favicon.ico、logo.svg)
|
├─ public # 公共静态资源(favicon.ico、logo.svg)
|
||||||
├─ src
|
├─ src
|
||||||
│ ├─ apis # 请求接口
|
│ ├─ apis # 请求接口
|
||||||
│ │ ├─ auth # 认证模块
|
│ │ ├─ auth # 认证模块
|
||||||
@@ -275,13 +275,10 @@ continew-admin-ui # 前端项目
|
|||||||
│ │ └─ user # 用户管理
|
│ │ └─ user # 用户管理
|
||||||
│ ├─ App.vue
|
│ ├─ App.vue
|
||||||
│ └─ main.ts
|
│ └─ main.ts
|
||||||
├─ .env.development
|
├─ .env.development # 开发环境配置
|
||||||
├─ .env.production
|
├─ .env.production # 生产环境配置
|
||||||
├─ .env.test
|
├─ .env.test # 测试环境配置
|
||||||
├─ .eslintignore
|
├─ eslint.config.js # ESLint 配置
|
||||||
├─ .eslintrc.cjs
|
|
||||||
├─ .prettierignore
|
|
||||||
├─ .prettierrc.js
|
|
||||||
├─ index.html
|
├─ index.html
|
||||||
├─ package.json
|
├─ package.json
|
||||||
├─ package-lock.json
|
├─ package-lock.json
|
||||||
|
@@ -6,7 +6,7 @@ export default function createAutoImport() {
|
|||||||
imports: [
|
imports: [
|
||||||
'vue',
|
'vue',
|
||||||
'vue-router',
|
'vue-router',
|
||||||
'pinia',
|
'pinia'
|
||||||
],
|
],
|
||||||
dts: './src/types/auto-imports.d.ts'
|
dts: './src/types/auto-imports.d.ts'
|
||||||
})
|
})
|
||||||
|
@@ -6,6 +6,6 @@ export default function createComponents() {
|
|||||||
dirs: ['src/components'],
|
dirs: ['src/components'],
|
||||||
extensions: ['vue', 'tsx'],
|
extensions: ['vue', 'tsx'],
|
||||||
// 配置文件生成位置
|
// 配置文件生成位置
|
||||||
dts: './src/types/components.d.ts',
|
dts: './src/types/components.d.ts'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@@ -8,6 +8,6 @@ export default function createSvgIcon(isBuild) {
|
|||||||
iconDirs: [path.resolve(process.cwd(), 'src/assets/icons')],
|
iconDirs: [path.resolve(process.cwd(), 'src/assets/icons')],
|
||||||
// 指定 symbolId 格式
|
// 指定 symbolId 格式
|
||||||
symbolId: 'icon-[dir]-[name]',
|
symbolId: 'icon-[dir]-[name]',
|
||||||
svgoOptions: isBuild,
|
svgoOptions: isBuild
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
55
eslint.config.js
Normal file
55
eslint.config.js
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
import antfu from '@antfu/eslint-config'
|
||||||
|
|
||||||
|
// https://github.com/antfu/eslint-config
|
||||||
|
export default antfu(
|
||||||
|
{
|
||||||
|
vue: true,
|
||||||
|
typescript: true,
|
||||||
|
ignores: [
|
||||||
|
'README.md',
|
||||||
|
'src/types/shims-vue.d.ts'
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// Remember to specify the file glob here, otherwise it might cause the vue plugin to handle non-vue files
|
||||||
|
files: ['**/*.vue'],
|
||||||
|
rules: {
|
||||||
|
'vue/block-order': [2, {
|
||||||
|
order: [['script', 'template'], 'style']
|
||||||
|
}], // 强制组件顶级元素的顺序
|
||||||
|
'vue/html-self-closing': [0, {
|
||||||
|
html: {
|
||||||
|
void: 'never',
|
||||||
|
normal: 'always',
|
||||||
|
component: 'never'
|
||||||
|
}
|
||||||
|
}], // 强制自结束样式
|
||||||
|
'vue/custom-event-name-casing': [2, 'kebab-case'], // 对自定义事件名称强制使用特定大小写
|
||||||
|
'vue/singleline-html-element-content-newline': 0, // 要求在单行元素的内容前后换行
|
||||||
|
'vue/first-attribute-linebreak': 0, // 强制第一个属性的位置
|
||||||
|
'vue/define-macros-order': [2, {
|
||||||
|
order: ['defineOptions', 'defineModel', 'defineProps', 'defineEmits', 'defineSlots'],
|
||||||
|
defineExposeLast: false
|
||||||
|
}], // 强制执行定义限制和定义弹出编译器宏的顺序
|
||||||
|
'vue/html-indent': 0, // 在《模板》中强制一致的缩进
|
||||||
|
'vue/html-closing-bracket-newline': 0 // 要求或不允许在标记的右括号前换行
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// Without `files`, they are general rules for all files
|
||||||
|
rules: {
|
||||||
|
'curly': [0, 'all'], // 对所有控制语句强制使用一致的大括号样式
|
||||||
|
'dot-notation': 0, // 尽可能强制使用点表示法。 在 JavaScript 中,可以使用点表示法 (foo.bar) 或方括号表示法 (foo["bar"]) 访问属性
|
||||||
|
'no-new': 0, // 不允许在赋值或比较之外使用 new 运算符
|
||||||
|
// 'no-console': 2, // 禁止使用 console
|
||||||
|
'no-process-env': 0,
|
||||||
|
'style/arrow-parens': [2, 'always'], // 箭头函数参数需要括号
|
||||||
|
'style/brace-style': [2, '1tbs', { allowSingleLine: true }], // 对块执行一致的大括号样式
|
||||||
|
'style/comma-dangle': [2, 'never'], // 要求或不允许尾随逗号
|
||||||
|
'ts/consistent-type-definitions': 0,
|
||||||
|
'node/prefer-global/process': 0,
|
||||||
|
'antfu/top-level-function': 0,
|
||||||
|
'antfu/if-newline': 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
20
package.json
20
package.json
@@ -1,5 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "continew-admin-ui",
|
"name": "continew-admin-ui",
|
||||||
|
"type": "module",
|
||||||
"version": "3.1.0-SNAPSHOT",
|
"version": "3.1.0-SNAPSHOT",
|
||||||
"private": "true",
|
"private": "true",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@@ -9,7 +10,7 @@
|
|||||||
"preview": "vite preview --port 5050",
|
"preview": "vite preview --port 5050",
|
||||||
"typecheck": "vue-tsc --noEmit",
|
"typecheck": "vue-tsc --noEmit",
|
||||||
"lint": "eslint src",
|
"lint": "eslint src",
|
||||||
"fix": "eslint src --fix"
|
"lint:fix": "eslint src --fix"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@amap/amap-jsapi-loader": "^1.0.1",
|
"@amap/amap-jsapi-loader": "^1.0.1",
|
||||||
@@ -54,24 +55,21 @@
|
|||||||
"xgplayer": "^2.31.6"
|
"xgplayer": "^2.31.6"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@antfu/eslint-config": "^2.16.3",
|
||||||
"@arco-design/web-vue": "^2.55.0",
|
"@arco-design/web-vue": "^2.55.0",
|
||||||
"@rushstack/eslint-patch": "^1.3.1",
|
|
||||||
"@types/crypto-js": "^4.2.2",
|
"@types/crypto-js": "^4.2.2",
|
||||||
"@types/lodash": "^4.14.195",
|
|
||||||
"@types/node": "^20.2.5",
|
"@types/node": "^20.2.5",
|
||||||
"@types/query-string": "^6.3.0",
|
"@types/query-string": "^6.3.0",
|
||||||
"@vitejs/plugin-vue": "^5.0.4",
|
"@vitejs/plugin-vue": "^5.0.4",
|
||||||
"@vitejs/plugin-vue-jsx": "^3.1.0",
|
"@vitejs/plugin-vue-jsx": "^3.1.0",
|
||||||
"@vue/eslint-config-prettier": "^7.1.0",
|
|
||||||
"@vue/eslint-config-typescript": "^11.0.3",
|
|
||||||
"@vue/tsconfig": "^0.1.3",
|
"@vue/tsconfig": "^0.1.3",
|
||||||
"eslint": "^8.41.0",
|
"eslint": "^9.0.0",
|
||||||
"eslint-plugin-vue": "^9.13.0",
|
|
||||||
"less": "^4.1.3",
|
"less": "^4.1.3",
|
||||||
"less-loader": "^11.0.0",
|
"less-loader": "^11.0.0",
|
||||||
"prettier": "^2.8.8",
|
"lint-staged": "^15.2.2",
|
||||||
"sass": "^1.62.1",
|
"sass": "^1.62.1",
|
||||||
"sass-loader": "^13.2.2",
|
"sass-loader": "^13.2.2",
|
||||||
|
"simple-git-hooks": "^2.11.1",
|
||||||
"typescript": "~5.0.4",
|
"typescript": "~5.0.4",
|
||||||
"unplugin-auto-import": "^0.16.4",
|
"unplugin-auto-import": "^0.16.4",
|
||||||
"unplugin-vue-components": "^0.25.1",
|
"unplugin-vue-components": "^0.25.1",
|
||||||
@@ -80,5 +78,11 @@
|
|||||||
"vite-plugin-style-import": "^2.0.0",
|
"vite-plugin-style-import": "^2.0.0",
|
||||||
"vite-plugin-svg-icons": "^2.0.1",
|
"vite-plugin-svg-icons": "^2.0.1",
|
||||||
"vue-tsc": "^2.0.6"
|
"vue-tsc": "^2.0.6"
|
||||||
|
},
|
||||||
|
"simple-git-hooks": {
|
||||||
|
"pre-commit": "pnpm lint-staged"
|
||||||
|
},
|
||||||
|
"lint-staged": {
|
||||||
|
"*": "eslint --fix"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
10361
pnpm-lock.yaml
generated
10361
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
|||||||
import http from '@/utils/http'
|
|
||||||
import type * as Area from './type'
|
import type * as Area from './type'
|
||||||
|
import http from '@/utils/http'
|
||||||
|
|
||||||
/** @desc 获取地区列表 */
|
/** @desc 获取地区列表 */
|
||||||
export const getAreaList = (params: { type: 'province' | 'city' | 'area'; code?: string }) => {
|
export const getAreaList = (params: { type: 'province' | 'city' | 'area', code?: string }) => {
|
||||||
return http.get<Area.AreaItem>('/area/list', params)
|
return http.get<Area.AreaItem>('/area/list', params)
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import http from '@/utils/http'
|
|
||||||
import type * as Auth from './type'
|
import type * as Auth from './type'
|
||||||
|
import http from '@/utils/http'
|
||||||
|
|
||||||
const BASE_URL = '/auth'
|
const BASE_URL = '/auth'
|
||||||
|
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import http from '@/utils/http'
|
|
||||||
import type * as Common from './type'
|
import type * as Common from './type'
|
||||||
|
import http from '@/utils/http'
|
||||||
|
|
||||||
const BASE_URL = '/captcha'
|
const BASE_URL = '/captcha'
|
||||||
|
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
|
import type { TreeNodeData } from '@arco-design/web-vue'
|
||||||
import http from '@/utils/http'
|
import http from '@/utils/http'
|
||||||
import type { LabelValueState } from '@/types/global'
|
import type { LabelValueState } from '@/types/global'
|
||||||
import type { TreeNodeData } from '@arco-design/web-vue'
|
|
||||||
import type { OptionQuery } from '@/apis'
|
import type { OptionQuery } from '@/apis'
|
||||||
|
|
||||||
const BASE_URL = '/common'
|
const BASE_URL = '/common'
|
||||||
@@ -16,7 +16,7 @@ export function listMenuTree(query: { description: string }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** @desc 查询角色列表 */
|
/** @desc 查询角色列表 */
|
||||||
export function listRoleDict(query?: { name: string; status: number }) {
|
export function listRoleDict(query?: { name: string, status: number }) {
|
||||||
return http.get<LabelValueState[]>(`${BASE_URL}/dict/role`, query)
|
return http.get<LabelValueState[]>(`${BASE_URL}/dict/role`, query)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import http from '@/utils/http'
|
|
||||||
import type * as Common from './type'
|
import type * as Common from './type'
|
||||||
|
import http from '@/utils/http'
|
||||||
|
|
||||||
const BASE_URL = '/dashboard'
|
const BASE_URL = '/dashboard'
|
||||||
|
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import http from '@/utils/http'
|
|
||||||
import type * as Monitor from './type'
|
import type * as Monitor from './type'
|
||||||
|
import http from '@/utils/http'
|
||||||
|
|
||||||
const BASE_URL = '/system/log'
|
const BASE_URL = '/system/log'
|
||||||
|
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import http from '@/utils/http'
|
|
||||||
import type * as Monitor from './type'
|
import type * as Monitor from './type'
|
||||||
|
import http from '@/utils/http'
|
||||||
|
|
||||||
const BASE_URL = '/monitor/online'
|
const BASE_URL = '/monitor/online'
|
||||||
|
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import http from '@/utils/http'
|
|
||||||
import type * as System from './type'
|
import type * as System from './type'
|
||||||
|
import http from '@/utils/http'
|
||||||
|
|
||||||
const BASE_URL = '/system/dept'
|
const BASE_URL = '/system/dept'
|
||||||
|
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import http from '@/utils/http'
|
|
||||||
import type * as System from './type'
|
import type * as System from './type'
|
||||||
|
import http from '@/utils/http'
|
||||||
|
|
||||||
const BASE_URL = '/system/dict'
|
const BASE_URL = '/system/dict'
|
||||||
|
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import http from '@/utils/http'
|
|
||||||
import type * as System from './type'
|
import type * as System from './type'
|
||||||
|
import http from '@/utils/http'
|
||||||
|
|
||||||
const BASE_URL = '/system/file'
|
const BASE_URL = '/system/file'
|
||||||
|
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import http from '@/utils/http'
|
|
||||||
import type * as System from './type'
|
import type * as System from './type'
|
||||||
|
import http from '@/utils/http'
|
||||||
|
|
||||||
const BASE_URL = '/system/menu'
|
const BASE_URL = '/system/menu'
|
||||||
|
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import http from '@/utils/http'
|
|
||||||
import type * as System from './type'
|
import type * as System from './type'
|
||||||
|
import http from '@/utils/http'
|
||||||
|
|
||||||
const BASE_URL = '/system/notice'
|
const BASE_URL = '/system/notice'
|
||||||
|
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import http from '@/utils/http'
|
|
||||||
import type * as System from './type'
|
import type * as System from './type'
|
||||||
|
import http from '@/utils/http'
|
||||||
|
|
||||||
const BASE_URL = '/system/option'
|
const BASE_URL = '/system/option'
|
||||||
|
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import http from '@/utils/http'
|
|
||||||
import type * as System from './type'
|
import type * as System from './type'
|
||||||
|
import http from '@/utils/http'
|
||||||
|
|
||||||
const BASE_URL = '/system/role'
|
const BASE_URL = '/system/role'
|
||||||
|
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import http from '@/utils/http'
|
|
||||||
import type * as System from './type'
|
import type * as System from './type'
|
||||||
|
import http from '@/utils/http'
|
||||||
|
|
||||||
const BASE_URL = '/system/storage'
|
const BASE_URL = '/system/storage'
|
||||||
|
|
||||||
|
@@ -261,7 +261,7 @@ export interface SecurityConfigResp {
|
|||||||
password_update_interval: OptionResp
|
password_update_interval: OptionResp
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 绑定三方账号信息*/
|
/** 绑定三方账号信息 */
|
||||||
export interface BindSocialAccountRes {
|
export interface BindSocialAccountRes {
|
||||||
source: string
|
source: string
|
||||||
description: string
|
description: string
|
||||||
|
@@ -9,22 +9,22 @@ export function uploadAvatar(data: FormData) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** @desc 修改用户基本信息 */
|
/** @desc 修改用户基本信息 */
|
||||||
export function updateUserBaseInfo(data: { nickname: string; gender: number }) {
|
export function updateUserBaseInfo(data: { nickname: string, gender: number }) {
|
||||||
return http.patch(`${BASE_URL}/basic/info`, data)
|
return http.patch(`${BASE_URL}/basic/info`, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @desc 修改密码 */
|
/** @desc 修改密码 */
|
||||||
export function updateUserPassword(data: { oldPassword: string; newPassword: string }) {
|
export function updateUserPassword(data: { oldPassword: string, newPassword: string }) {
|
||||||
return http.patch(`${BASE_URL}/password`, data)
|
return http.patch(`${BASE_URL}/password`, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @desc 修改手机号 */
|
/** @desc 修改手机号 */
|
||||||
export function updateUserPhone(data: { phone: string; captcha: string; oldPassword: string }) {
|
export function updateUserPhone(data: { phone: string, captcha: string, oldPassword: string }) {
|
||||||
return http.patch(`${BASE_URL}/phone`, data)
|
return http.patch(`${BASE_URL}/phone`, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @desc 修改邮箱 */
|
/** @desc 修改邮箱 */
|
||||||
export function updateUserEmail(data: { email: string; captcha: string; oldPassword: string }) {
|
export function updateUserEmail(data: { email: string, captcha: string, oldPassword: string }) {
|
||||||
return http.patch(`${BASE_URL}/email`, data)
|
return http.patch(`${BASE_URL}/email`, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import http from '@/utils/http'
|
|
||||||
import type * as System from './type'
|
import type * as System from './type'
|
||||||
|
import http from '@/utils/http'
|
||||||
|
|
||||||
const BASE_URL = '/system/user'
|
const BASE_URL = '/system/user'
|
||||||
|
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import http from '@/utils/http'
|
|
||||||
import type * as Tool from './type'
|
import type * as Tool from './type'
|
||||||
|
import http from '@/utils/http'
|
||||||
|
|
||||||
const BASE_URL = '/generator'
|
const BASE_URL = '/generator'
|
||||||
|
|
||||||
|
@@ -13,8 +13,8 @@
|
|||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { RouteLocationMatched } from 'vue-router'
|
import type { RouteLocationMatched } from 'vue-router'
|
||||||
import { useRouteStore } from '@/stores'
|
|
||||||
import { findTree } from 'xe-utils'
|
import { findTree } from 'xe-utils'
|
||||||
|
import { useRouteStore } from '@/stores'
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
@@ -11,12 +11,6 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
defineOptions({ name: 'GiCellAvatar' })
|
defineOptions({ name: 'GiCellAvatar' })
|
||||||
|
|
||||||
interface Props {
|
|
||||||
avatar: string
|
|
||||||
name: string
|
|
||||||
isLink?: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
const props = withDefaults(defineProps<Props>(), {
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
avatar: '',
|
avatar: '',
|
||||||
name: '',
|
name: '',
|
||||||
@@ -26,6 +20,12 @@ const props = withDefaults(defineProps<Props>(), {
|
|||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(e: 'click'): void
|
(e: 'click'): void
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
avatar: string
|
||||||
|
name: string
|
||||||
|
isLink?: boolean
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped></style>
|
<style lang="scss" scoped></style>
|
||||||
|
@@ -15,13 +15,13 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
defineOptions({ name: 'GiCellGender' })
|
defineOptions({ name: 'GiCellGender' })
|
||||||
|
|
||||||
interface Props {
|
|
||||||
gender: 1 | 2 | 0
|
|
||||||
}
|
|
||||||
|
|
||||||
const props = withDefaults(defineProps<Props>(), {
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
gender: 1
|
gender: 1
|
||||||
})
|
})
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
gender: 1 | 2 | 0
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped></style>
|
<style lang="scss" scoped></style>
|
||||||
|
@@ -12,13 +12,13 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
defineOptions({ name: 'GiCellStatus' })
|
defineOptions({ name: 'GiCellStatus' })
|
||||||
|
|
||||||
interface Props {
|
|
||||||
status: 0 | 1
|
|
||||||
}
|
|
||||||
|
|
||||||
const props = withDefaults(defineProps<Props>(), {
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
status: 1
|
status: 1
|
||||||
})
|
})
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
status: 0 | 1
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped></style>
|
<style lang="scss" scoped></style>
|
||||||
|
@@ -21,13 +21,13 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
defineOptions({ name: 'GiCellTags' })
|
defineOptions({ name: 'GiCellTags' })
|
||||||
|
|
||||||
interface Props {
|
|
||||||
data: string[]
|
|
||||||
}
|
|
||||||
|
|
||||||
withDefaults(defineProps<Props>(), {
|
withDefaults(defineProps<Props>(), {
|
||||||
data: () => []
|
data: () => []
|
||||||
})
|
})
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
data: string[]
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped></style>
|
<style lang="scss" scoped></style>
|
||||||
|
@@ -17,6 +17,10 @@ import { githubLight } from '@ddietr/codemirror-themes/github-light'
|
|||||||
import { oneDark } from '@codemirror/theme-one-dark'
|
import { oneDark } from '@codemirror/theme-one-dark'
|
||||||
import { useAppStore } from '@/stores'
|
import { useAppStore } from '@/stores'
|
||||||
|
|
||||||
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
|
type: 'javascript',
|
||||||
|
codeJson: ''
|
||||||
|
})
|
||||||
const appStore = useAppStore()
|
const appStore = useAppStore()
|
||||||
const isDark = computed(() => appStore.theme === 'dark')
|
const isDark = computed(() => appStore.theme === 'dark')
|
||||||
|
|
||||||
@@ -24,11 +28,6 @@ interface Props {
|
|||||||
type?: 'javascript' | 'vue'
|
type?: 'javascript' | 'vue'
|
||||||
codeJson: string
|
codeJson: string
|
||||||
}
|
}
|
||||||
const props = withDefaults(defineProps<Props>(), {
|
|
||||||
type: 'javascript',
|
|
||||||
codeJson: ''
|
|
||||||
})
|
|
||||||
|
|
||||||
const defaultConfig = {
|
const defaultConfig = {
|
||||||
tabSize: 2,
|
tabSize: 2,
|
||||||
basic: true,
|
basic: true,
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { defineComponent, type PropType } from 'vue'
|
import { type PropType, defineComponent } from 'vue'
|
||||||
import './dot.scss'
|
import './dot.scss'
|
||||||
|
|
||||||
type TPropsType = 'primary' | 'success' | 'warning' | 'danger' | 'info'
|
type TPropsType = 'primary' | 'success' | 'warning' | 'danger' | 'info'
|
||||||
|
@@ -9,22 +9,22 @@ import type { CSSProperties } from 'vue'
|
|||||||
|
|
||||||
defineOptions({ name: 'GiFlexibleBox' })
|
defineOptions({ name: 'GiFlexibleBox' })
|
||||||
|
|
||||||
interface Props {
|
|
||||||
modelValue: boolean
|
|
||||||
direction: 'left' | 'right'
|
|
||||||
}
|
|
||||||
|
|
||||||
const props = withDefaults(defineProps<Props>(), {
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
modelValue: false,
|
modelValue: false,
|
||||||
direction: 'right'
|
direction: 'right'
|
||||||
})
|
})
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
modelValue: boolean
|
||||||
|
direction: 'left' | 'right'
|
||||||
|
}
|
||||||
|
|
||||||
const BoxRef = ref<HTMLElement | null>()
|
const BoxRef = ref<HTMLElement | null>()
|
||||||
|
|
||||||
const style = computed(() => {
|
const style = computed(() => {
|
||||||
const obj: CSSProperties = {}
|
const obj: CSSProperties = {}
|
||||||
obj[`margin-${props.direction}`] =
|
obj[`margin-${props.direction}`]
|
||||||
!props.modelValue && BoxRef.value && BoxRef.value.clientWidth ? `-${BoxRef.value.clientWidth}px` : 0
|
= !props.modelValue && BoxRef.value && BoxRef.value.clientWidth ? `-${BoxRef.value.clientWidth}px` : 0
|
||||||
return obj
|
return obj
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
@@ -5,9 +5,9 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { useAppStore } from '@/stores'
|
import { useAppStore } from '@/stores'
|
||||||
|
|
||||||
const appStore = useAppStore()
|
|
||||||
|
|
||||||
defineOptions({ name: 'GiFooter' })
|
defineOptions({ name: 'GiFooter' })
|
||||||
|
|
||||||
|
const appStore = useAppStore()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
@@ -104,7 +104,7 @@
|
|||||||
|
|
||||||
<template v-if="item.type === 'date-picker'">
|
<template v-if="item.type === 'date-picker'">
|
||||||
<a-date-picker
|
<a-date-picker
|
||||||
:placeholder="`请选择日期`"
|
placeholder="请选择日期"
|
||||||
v-bind="(item.props as A.DatePickerInstance['$props'])"
|
v-bind="(item.props as A.DatePickerInstance['$props'])"
|
||||||
:model-value="modelValue[item.field as keyof typeof modelValue]"
|
:model-value="modelValue[item.field as keyof typeof modelValue]"
|
||||||
@update:model-value="valueChange($event, item.field)"
|
@update:model-value="valueChange($event, item.field)"
|
||||||
@@ -113,7 +113,7 @@
|
|||||||
|
|
||||||
<template v-if="item.type === 'time-picker'">
|
<template v-if="item.type === 'time-picker'">
|
||||||
<a-time-picker
|
<a-time-picker
|
||||||
:placeholder="`请选择时间`"
|
placeholder="请选择时间"
|
||||||
v-bind="(item.props as A.TimePickerInstance['$props'])"
|
v-bind="(item.props as A.TimePickerInstance['$props'])"
|
||||||
:model-value="modelValue[item.field as keyof typeof modelValue]"
|
:model-value="modelValue[item.field as keyof typeof modelValue]"
|
||||||
@update:model-value="valueChange($event, item.field)"
|
@update:model-value="valueChange($event, item.field)"
|
||||||
@@ -171,9 +171,9 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { Options, Columns, ColumnsItemHide, ColumnsItemDisabled, ColumnsItem } from './type'
|
|
||||||
import type * as A from '@arco-design/web-vue'
|
import type * as A from '@arco-design/web-vue'
|
||||||
import { cloneDeep } from 'lodash-es'
|
import { cloneDeep } from 'lodash-es'
|
||||||
|
import type { Columns, ColumnsItem, ColumnsItemDisabled, ColumnsItemHide, Options } from './type'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
modelValue: any
|
modelValue: any
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
import { reactive } from 'vue'
|
import { reactive } from 'vue'
|
||||||
import { cloneDeep } from 'lodash-es'
|
import { cloneDeep } from 'lodash-es'
|
||||||
import type { Columns, ColumnsItem, ColumnsItemPropsKey } from './type'
|
|
||||||
import { Message } from '@arco-design/web-vue'
|
import { Message } from '@arco-design/web-vue'
|
||||||
|
import type { Columns, ColumnsItem, ColumnsItemPropsKey } from './type'
|
||||||
|
|
||||||
export function useGiForm(initValue: Columns) {
|
export function useGiForm(initValue: Columns) {
|
||||||
const getInitValue = () => cloneDeep(initValue)
|
const getInitValue = () => cloneDeep(initValue)
|
||||||
|
@@ -36,11 +36,11 @@ export type ColumnsItemRequest<F = any> = (form: F) => Promise<any>
|
|||||||
export type ColumnsItemFormat<T = any> = (
|
export type ColumnsItemFormat<T = any> = (
|
||||||
res: T
|
res: T
|
||||||
) =>
|
) =>
|
||||||
| A.SelectInstance['$props']['options']
|
| A.SelectInstance['$props']['options']
|
||||||
| A.RadioGroupInstance['$props']['options']
|
| A.RadioGroupInstance['$props']['options']
|
||||||
| A.CheckboxGroupInstance['$props']['options']
|
| A.CheckboxGroupInstance['$props']['options']
|
||||||
| A.CascaderInstance['$props']['options']
|
| A.CascaderInstance['$props']['options']
|
||||||
| A.TreeSelectInstance['$props']['data']
|
| A.TreeSelectInstance['$props']['data']
|
||||||
|
|
||||||
export type ColumnsItemOptionsOrData =
|
export type ColumnsItemOptionsOrData =
|
||||||
| A.SelectInstance['$props']['options']
|
| A.SelectInstance['$props']['options']
|
||||||
@@ -91,8 +91,8 @@ export interface Options {
|
|||||||
form: Omit<A.FormInstance['$props'], 'model'>
|
form: Omit<A.FormInstance['$props'], 'model'>
|
||||||
row?: Partial<typeof import('@arco-design/web-vue')['Row']['__defaults']>
|
row?: Partial<typeof import('@arco-design/web-vue')['Row']['__defaults']>
|
||||||
col?: A.ColProps
|
col?: A.ColProps
|
||||||
btns?: { hide?: boolean; span?: number; col?: A.ColProps; searchBtnText?: string }
|
btns?: { hide?: boolean, span?: number, col?: A.ColProps, searchBtnText?: string }
|
||||||
fold?: { enable?: boolean; index?: number; defaultCollapsed?: boolean }
|
fold?: { enable?: boolean, index?: number, defaultCollapsed?: boolean }
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Columns<F = any> = ColumnsItem<F>[]
|
export type Columns<F = any> = ColumnsItem<F>[]
|
||||||
|
@@ -55,7 +55,7 @@
|
|||||||
<a-row justify="center" align="center">
|
<a-row justify="center" align="center">
|
||||||
<a-pagination
|
<a-pagination
|
||||||
size="mini"
|
size="mini"
|
||||||
:pageSize="pageSize"
|
:page-size="pageSize"
|
||||||
:total="total"
|
:total="total"
|
||||||
:show-size-changer="false"
|
:show-size-changer="false"
|
||||||
@change="onPageChange"
|
@change="onPageChange"
|
||||||
@@ -69,22 +69,24 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useClipboard } from '@vueuse/core'
|
import { useClipboard } from '@vueuse/core'
|
||||||
import { Message } from '@arco-design/web-vue'
|
import { Message } from '@arco-design/web-vue'
|
||||||
// 自定义图标模块
|
|
||||||
const SvgIconModules = import.meta.glob('@/assets/icons/*.svg')
|
|
||||||
|
|
||||||
defineOptions({ name: 'GiIconSelector' })
|
defineOptions({ name: 'GiIconSelector' })
|
||||||
const emit = defineEmits(['select', 'update:modelValue'])
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
modelValue?: string
|
|
||||||
enableCopy?: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
const props = withDefaults(defineProps<Props>(), {
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
modelValue: '',
|
modelValue: '',
|
||||||
enableCopy: false
|
enableCopy: false
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const emit = defineEmits(['select', 'update:modelValue'])
|
||||||
|
|
||||||
|
// 自定义图标模块
|
||||||
|
const SvgIconModules = import.meta.glob('@/assets/icons/*.svg')
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
modelValue?: string
|
||||||
|
enableCopy?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
const searchValue = ref('') // 搜索词
|
const searchValue = ref('') // 搜索词
|
||||||
|
|
||||||
// 图标列表
|
// 图标列表
|
||||||
|
@@ -21,13 +21,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
defineOptions({ name: 'GiOptionItem' })
|
defineOptions({ name: 'GiOptionItem' })
|
||||||
|
|
||||||
interface Props {
|
|
||||||
icon?: string
|
|
||||||
label?: string
|
|
||||||
more?: boolean
|
|
||||||
active?: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
const props = withDefaults(defineProps<Props>(), {
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
icon: '',
|
icon: '',
|
||||||
label: '',
|
label: '',
|
||||||
@@ -39,6 +32,13 @@ const emit = defineEmits<{
|
|||||||
(e: 'click'): void
|
(e: 'click'): void
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
icon?: string
|
||||||
|
label?: string
|
||||||
|
more?: boolean
|
||||||
|
active?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
const handleClick = () => {
|
const handleClick = () => {
|
||||||
emit('click')
|
emit('click')
|
||||||
}
|
}
|
||||||
|
@@ -21,12 +21,12 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
defineOptions({ name: 'GiOverFlowTags' })
|
defineOptions({ name: 'GiOverFlowTags' })
|
||||||
|
|
||||||
interface Props {
|
|
||||||
data: string[]
|
|
||||||
}
|
|
||||||
withDefaults(defineProps<Props>(), {
|
withDefaults(defineProps<Props>(), {
|
||||||
data: () => []
|
data: () => []
|
||||||
})
|
})
|
||||||
|
interface Props {
|
||||||
|
data: string[]
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped></style>
|
<style lang="scss" scoped></style>
|
||||||
|
@@ -3,7 +3,7 @@
|
|||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
:class="svgClass"
|
:class="svgClass"
|
||||||
v-bind="$attrs"
|
v-bind="$attrs"
|
||||||
:style="{ color: color, fill: color, width: iconSize, height: iconSize }"
|
:style="{ color, fill: color, width: iconSize, height: iconSize }"
|
||||||
>
|
>
|
||||||
<use :xlink:href="iconName"></use>
|
<use :xlink:href="iconName"></use>
|
||||||
</svg>
|
</svg>
|
||||||
@@ -12,21 +12,21 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
defineOptions({ name: 'GiSvgIcon' })
|
defineOptions({ name: 'GiSvgIcon' })
|
||||||
|
|
||||||
interface Props {
|
|
||||||
name: string
|
|
||||||
color?: string
|
|
||||||
size?: string | number
|
|
||||||
}
|
|
||||||
|
|
||||||
const props = withDefaults(defineProps<Props>(), {
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
name: '',
|
name: '',
|
||||||
color: '',
|
color: '',
|
||||||
size: 20
|
size: 20
|
||||||
})
|
})
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
name: string
|
||||||
|
color?: string
|
||||||
|
size?: string | number
|
||||||
|
}
|
||||||
|
|
||||||
// 判断传入的值,是否带有单位,如果没有,就默认用px单位
|
// 判断传入的值,是否带有单位,如果没有,就默认用px单位
|
||||||
const getUnitValue = (value: string | number): string | number => {
|
const getUnitValue = (value: string | number): string | number => {
|
||||||
return /(px|em|rem|%)$/.test(value.toString()) ? value : value + 'px'
|
return /(px|em|rem|%)$/.test(value.toString()) ? value : `${value}px`
|
||||||
}
|
}
|
||||||
|
|
||||||
const iconSize = computed<string | number>(() => {
|
const iconSize = computed<string | number>(() => {
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div ref="giTableRef" class="gi-table" :class="{ 'gi-table--fullscreen': isFullscreen }">
|
<div class="gi-table" :class="{ 'gi-table--fullscreen': isFullscreen }">
|
||||||
<a-row justify="space-between" align="center" class="gi-table__toolbar">
|
<a-row justify="space-between" align="center" class="gi-table__toolbar">
|
||||||
<a-space wrap class="gi-table__toolbar-left" :size="[8, 8]">
|
<a-space wrap class="gi-table__toolbar-left" :size="[8, 8]">
|
||||||
<slot name="custom-left"></slot>
|
<slot name="custom-left"></slot>
|
||||||
@@ -18,9 +18,11 @@
|
|||||||
</a-button>
|
</a-button>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
<template #content>
|
<template #content>
|
||||||
<a-doption v-for="item in sizeList" :key="item.value" :value="item.value" :active="item.value === size">{{
|
<a-doption v-for="item in sizeList" :key="item.value" :value="item.value" :active="item.value === size">
|
||||||
|
{{
|
||||||
item.label
|
item.label
|
||||||
}}</a-doption>
|
}}
|
||||||
|
</a-doption>
|
||||||
</template>
|
</template>
|
||||||
</a-dropdown>
|
</a-dropdown>
|
||||||
<a-popover
|
<a-popover
|
||||||
@@ -38,7 +40,7 @@
|
|||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
<template #content>
|
<template #content>
|
||||||
<div class="gi-table__draggable">
|
<div class="gi-table__draggable">
|
||||||
<VueDraggable ref="el" v-model="settingColumnList">
|
<VueDraggable v-model="settingColumnList">
|
||||||
<div v-for="item in settingColumnList" :key="item.title" class="drag-item">
|
<div v-for="item in settingColumnList" :key="item.title" class="drag-item">
|
||||||
<div class="drag-item__move"><icon-drag-dot-vertical /></div>
|
<div class="drag-item__move"><icon-drag-dot-vertical /></div>
|
||||||
<a-checkbox v-model:model-value="item.show" :disabled="item.disabled">{{ item.title }}</a-checkbox>
|
<a-checkbox v-model:model-value="item.show" :disabled="item.disabled">{{ item.title }}</a-checkbox>
|
||||||
@@ -73,17 +75,23 @@
|
|||||||
v-bind="{ ...attrs, columns: _columns }"
|
v-bind="{ ...attrs, columns: _columns }"
|
||||||
>
|
>
|
||||||
<template v-for="key in Object.keys(slots)" :key="key" #[key]="scoped">
|
<template v-for="key in Object.keys(slots)" :key="key" #[key]="scoped">
|
||||||
<slot :key="key" :name="key" v-bind="scoped"></slot> </template
|
<slot :key="key" :name="key" v-bind="scoped"></slot>
|
||||||
></a-table>
|
</template>
|
||||||
|
</a-table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { TableInstance, TableColumnData, DropdownInstance } from '@arco-design/web-vue'
|
import type { DropdownInstance, TableColumnData, TableInstance } from '@arco-design/web-vue'
|
||||||
import { VueDraggable } from 'vue-draggable-plus'
|
import { VueDraggable } from 'vue-draggable-plus'
|
||||||
|
|
||||||
defineOptions({ name: 'GiTable', inheritAttrs: false })
|
defineOptions({ name: 'GiTable', inheritAttrs: false })
|
||||||
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
|
disabledTools: () => [], // 禁止显示的工具
|
||||||
|
disabledColumnKeys: () => [] // 禁止控制显示隐藏的列
|
||||||
|
})
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(e: 'refresh'): void
|
(e: 'refresh'): void
|
||||||
}>()
|
}>()
|
||||||
@@ -96,18 +104,13 @@ interface Props {
|
|||||||
disabledColumnKeys?: string[]
|
disabledColumnKeys?: string[]
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = withDefaults(defineProps<Props>(), {
|
|
||||||
disabledTools: () => [], // 禁止显示的工具
|
|
||||||
disabledColumnKeys: () => [] // 禁止控制显示隐藏的列
|
|
||||||
})
|
|
||||||
|
|
||||||
const tableRef = ref<TableInstance | null>(null)
|
const tableRef = ref<TableInstance | null>(null)
|
||||||
const stripe = ref(false)
|
const stripe = ref(false)
|
||||||
const size = ref<TableInstance['size']>('medium')
|
const size = ref<TableInstance['size']>('medium')
|
||||||
const isBordered = ref(false)
|
const isBordered = ref(false)
|
||||||
const isFullscreen = ref(false)
|
const isFullscreen = ref(false)
|
||||||
|
|
||||||
type SizeItem = { label: string; value: TableInstance['size'] }
|
type SizeItem = { label: string, value: TableInstance['size'] }
|
||||||
const sizeList: SizeItem[] = [
|
const sizeList: SizeItem[] = [
|
||||||
{ label: '紧凑', value: 'small' },
|
{ label: '紧凑', value: 'small' },
|
||||||
{ label: '默认', value: 'medium' }
|
{ label: '默认', value: 'medium' }
|
||||||
@@ -127,7 +130,7 @@ const showFullscreenBtn = computed(() => !props.disabledTools.includes('fullscre
|
|||||||
const showSettingColumnBtn = computed(
|
const showSettingColumnBtn = computed(
|
||||||
() => !props.disabledTools.includes('setting') && attrs?.columns && (attrs?.columns as TableColumnData[])?.length
|
() => !props.disabledTools.includes('setting') && attrs?.columns && (attrs?.columns as TableColumnData[])?.length
|
||||||
)
|
)
|
||||||
type SettingColumnItem = { title: string; key: string; show: boolean; disabled: boolean }
|
type SettingColumnItem = { title: string, key: string, show: boolean, disabled: boolean }
|
||||||
const settingColumnList = ref<SettingColumnItem[]>([])
|
const settingColumnList = ref<SettingColumnItem[]>([])
|
||||||
|
|
||||||
// 重置配置列
|
// 重置配置列
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { defineComponent, computed, type PropType } from 'vue'
|
import { type PropType, computed, defineComponent } from 'vue'
|
||||||
import './tag.scss'
|
import './tag.scss'
|
||||||
|
|
||||||
type TPropsType = 'dark' | 'light' | 'outline' | 'light-outline'
|
type TPropsType = 'dark' | 'light' | 'outline' | 'light-outline'
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="json_pretty_container">
|
<div class="json_pretty_container">
|
||||||
<VueJsonPretty :path="'res'" :data="JSONObject" :show-length="true" />
|
<VueJsonPretty path="res" :data="JSONObject" :show-length="true" />
|
||||||
<icon-copy class="copy_icon" @click="onCopy(JSONObject)" />
|
<icon-copy class="copy_icon" @click="onCopy(JSONObject)" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Message } from '@arco-design/web-vue'
|
import { Message } from '@arco-design/web-vue'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
value: any
|
value: any
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
type LabelValueItem = { label: string; value: number; color: string }
|
type LabelValueItem = { label: string, value: number, color: string }
|
||||||
export const DisEnableStatusList: LabelValueItem[] = [
|
export const DisEnableStatusList: LabelValueItem[] = [
|
||||||
{ label: '启用', value: 1, color: 'green' },
|
{ label: '启用', value: 1, color: 'green' },
|
||||||
{ label: '禁用', value: 2, color: 'red' }
|
{ label: '禁用', value: 2, color: 'red' }
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import type { DirectiveBinding, Directive } from 'vue'
|
import type { Directive, DirectiveBinding } from 'vue'
|
||||||
import { useUserStore } from '@/stores'
|
import { useUserStore } from '@/stores'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import type { DirectiveBinding, Directive } from 'vue'
|
import type { Directive, DirectiveBinding } from 'vue'
|
||||||
import { useUserStore } from '@/stores'
|
import { useUserStore } from '@/stores'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { listDeptTree } from '@/apis'
|
|
||||||
import type { TreeNodeData } from '@arco-design/web-vue'
|
import type { TreeNodeData } from '@arco-design/web-vue'
|
||||||
|
import { listDeptTree } from '@/apis'
|
||||||
|
|
||||||
/** 部门模块 */
|
/** 部门模块 */
|
||||||
export function useDept(options?: { onSuccess?: () => void }) {
|
export function useDept(options?: { onSuccess?: () => void }) {
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
import { reactive, computed, ref, type Ref } from 'vue'
|
import { type Ref, computed, reactive, ref } from 'vue'
|
||||||
import { useRoute, useRouter } from 'vue-router'
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
import { Modal, Message, type FormInstance } from '@arco-design/web-vue'
|
import { type FormInstance, Message, Modal } from '@arco-design/web-vue'
|
||||||
import { isEqual } from 'lodash-es'
|
import { isEqual } from 'lodash-es'
|
||||||
|
|
||||||
type Option<T> = {
|
type Option<T> = {
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { listMenuTree } from '@/apis'
|
|
||||||
import type { TreeNodeData } from '@arco-design/web-vue'
|
import type { TreeNodeData } from '@arco-design/web-vue'
|
||||||
|
import { listMenuTree } from '@/apis'
|
||||||
|
|
||||||
/** 菜单模块 */
|
/** 菜单模块 */
|
||||||
export function useMenu(options?: { onSuccess?: () => void }) {
|
export function useMenu(options?: { onSuccess?: () => void }) {
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { computed, type ComputedRef } from 'vue'
|
import { type ComputedRef, computed } from 'vue'
|
||||||
import { useBreakpoints } from '@vueuse/core'
|
import { useBreakpoints } from '@vueuse/core'
|
||||||
import type { ColProps } from '@arco-design/web-vue'
|
import type { ColProps } from '@arco-design/web-vue'
|
||||||
|
|
||||||
|
@@ -16,7 +16,7 @@ export function useBreakpointIndex(callback: (v: number) => void, breakpointObj?
|
|||||||
() => breakpoint.value,
|
() => breakpoint.value,
|
||||||
(v) => {
|
(v) => {
|
||||||
const def = { xs: 0, sm: 0, md: 0, lg: 1, xl: 1, xxl: 2 }
|
const def = { xs: 0, sm: 0, md: 0, lg: 1, xl: 1, xxl: 2 }
|
||||||
const obj = breakpointObj ? breakpointObj : def
|
const obj = breakpointObj || def
|
||||||
callback(obj[v as keyof typeof obj])
|
callback(obj[v as keyof typeof obj])
|
||||||
},
|
},
|
||||||
{ immediate: true }
|
{ immediate: true }
|
||||||
|
@@ -2,13 +2,13 @@ import { Message, Notification } from '@arco-design/web-vue'
|
|||||||
/**
|
/**
|
||||||
* @description 接收数据流生成 blob,创建链接,下载文件
|
* @description 接收数据流生成 blob,创建链接,下载文件
|
||||||
* @param {Function} api 导出表格的api方法 (必传)
|
* @param {Function} api 导出表格的api方法 (必传)
|
||||||
* @param {String} tempName 导出的文件名 (必传)
|
* @param {string} tempName 导出的文件名 (必传)
|
||||||
* @param {Object} params 导出的参数 (默认{})
|
* @param {object} params 导出的参数 (默认{})
|
||||||
* @param {Boolean} isNotify 是否有导出消息提示 (默认为 true)
|
* @param {boolean} isNotify 是否有导出消息提示 (默认为 true)
|
||||||
* @param {String} fileType 导出的文件格式 (默认为.xlsx)
|
* @param {string} fileType 导出的文件格式 (默认为.xlsx)
|
||||||
* */
|
*/
|
||||||
interface NavigatorWithMsSaveOrOpenBlob extends Navigator {
|
interface NavigatorWithMsSaveOrOpenBlob extends Navigator {
|
||||||
msSaveOrOpenBlob(blob: Blob, fileName: string): void
|
msSaveOrOpenBlob: (blob: Blob, fileName: string) => void
|
||||||
}
|
}
|
||||||
export const useDownload = async (api: () => Promise<any>, isNotify = true, tempName = '', fileType = '.xlsx') => {
|
export const useDownload = async (api: () => Promise<any>, isNotify = true, tempName = '', fileType = '.xlsx') => {
|
||||||
try {
|
try {
|
||||||
@@ -16,7 +16,7 @@ export const useDownload = async (api: () => Promise<any>, isNotify = true, temp
|
|||||||
if (res.headers['content-disposition']) {
|
if (res.headers['content-disposition']) {
|
||||||
tempName = decodeURI(res.headers['content-disposition'].split(';')[1].split('=')[1])
|
tempName = decodeURI(res.headers['content-disposition'].split(';')[1].split('=')[1])
|
||||||
} else {
|
} else {
|
||||||
tempName = tempName ? tempName : new Date().getTime() + fileType
|
tempName = tempName || new Date().getTime() + fileType
|
||||||
}
|
}
|
||||||
if (isNotify && !res?.code) {
|
if (isNotify && !res?.code) {
|
||||||
Notification.warning({
|
Notification.warning({
|
||||||
@@ -24,7 +24,7 @@ export const useDownload = async (api: () => Promise<any>, isNotify = true, temp
|
|||||||
content: '如果数据庞大会导致下载缓慢哦,请您耐心等待!'
|
content: '如果数据庞大会导致下载缓慢哦,请您耐心等待!'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if (res.status !== 200 || res.data === null || !(res.data instanceof Blob)) {
|
if (res.status !== 200 || res.data == null || !(res.data instanceof Blob)) {
|
||||||
Message.error('导出失败,请稍后再试!')
|
Message.error('导出失败,请稍后再试!')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -44,6 +44,6 @@ export const useDownload = async (api: () => Promise<any>, isNotify = true, temp
|
|||||||
document.body.removeChild(exportFile)
|
document.body.removeChild(exportFile)
|
||||||
window.URL.revokeObjectURL(blobUrl)
|
window.URL.revokeObjectURL(blobUrl)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error)
|
// console.log(error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { ref, type UnwrapRef } from 'vue'
|
import { type UnwrapRef, ref } from 'vue'
|
||||||
import type { AxiosResponse } from 'axios'
|
import type { AxiosResponse } from 'axios'
|
||||||
import { useLoading } from '@/hooks'
|
import { useLoading } from '@/hooks'
|
||||||
|
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import type { TableInstance, TableData } from '@arco-design/web-vue'
|
import type { TableData, TableInstance } from '@arco-design/web-vue'
|
||||||
import { Modal, Message } from '@arco-design/web-vue'
|
import { Message, Modal } from '@arco-design/web-vue'
|
||||||
import { usePagination } from '@/hooks'
|
import { usePagination } from '@/hooks'
|
||||||
|
|
||||||
interface Options<T> {
|
interface Options<T> {
|
||||||
@@ -9,11 +9,12 @@ interface Options<T> {
|
|||||||
rowKey?: keyof T
|
rowKey?: keyof T
|
||||||
}
|
}
|
||||||
|
|
||||||
type PaginationParams = { page: number; size: number }
|
type PaginationParams = { page: number, size: number }
|
||||||
type Api<T> = (params: PaginationParams) => Promise<ApiRes<PageRes<T[]>>>
|
type Api<T> = (params: PaginationParams) => Promise<ApiRes<PageRes<T[]>>>
|
||||||
|
|
||||||
export function useTable<T>(api: Api<T>, options?: Options<T>) {
|
export function useTable<T>(api: Api<T>, options?: Options<T>) {
|
||||||
const { formatResult, onSuccess, immediate, rowKey } = options || {}
|
const { formatResult, onSuccess, immediate, rowKey } = options || {}
|
||||||
|
// eslint-disable-next-line ts/no-use-before-define
|
||||||
const { pagination, setTotal } = usePagination(() => getTableData())
|
const { pagination, setTotal } = usePagination(() => getTableData())
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const tableData = ref<T[]>([])
|
const tableData = ref<T[]>([])
|
||||||
@@ -34,12 +35,6 @@ export function useTable<T>(api: Api<T>, options?: Options<T>) {
|
|||||||
const isImmediate = immediate ?? true
|
const isImmediate = immediate ?? true
|
||||||
isImmediate && getTableData()
|
isImmediate && getTableData()
|
||||||
|
|
||||||
// 查询
|
|
||||||
const search = () => {
|
|
||||||
selectedKeys.value = []
|
|
||||||
pagination.onChange(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 多选
|
// 多选
|
||||||
const selectedKeys = ref<(string | number)[]>([])
|
const selectedKeys = ref<(string | number)[]>([])
|
||||||
const select: TableInstance['onSelect'] = (rowKeys) => {
|
const select: TableInstance['onSelect'] = (rowKeys) => {
|
||||||
@@ -53,10 +48,16 @@ export function useTable<T>(api: Api<T>, options?: Options<T>) {
|
|||||||
selectedKeys.value = checked ? arr.map((i) => i[key as string]) : []
|
selectedKeys.value = checked ? arr.map((i) => i[key as string]) : []
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 查询
|
||||||
|
const search = () => {
|
||||||
|
selectedKeys.value = []
|
||||||
|
pagination.onChange(1)
|
||||||
|
}
|
||||||
|
|
||||||
// 删除
|
// 删除
|
||||||
const handleDelete = async <T>(
|
const handleDelete = async <T>(
|
||||||
deleteApi: () => Promise<ApiRes<T>>,
|
deleteApi: () => Promise<ApiRes<T>>,
|
||||||
options?: { title?: string; content?: string; successTip?: string; showModal?: boolean }
|
options?: { title?: string, content?: string, successTip?: string, showModal?: boolean }
|
||||||
): Promise<boolean | undefined> => {
|
): Promise<boolean | undefined> => {
|
||||||
const onDelete = async () => {
|
const onDelete = async () => {
|
||||||
try {
|
try {
|
||||||
|
@@ -38,6 +38,8 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import type { RouteRecordRaw } from 'vue-router'
|
||||||
|
import { searchTree } from 'xe-utils'
|
||||||
import Main from './components/Main.vue'
|
import Main from './components/Main.vue'
|
||||||
import Tabs from './components/Tabs/index.vue'
|
import Tabs from './components/Tabs/index.vue'
|
||||||
import Menu from './components/Menu/index.vue'
|
import Menu from './components/Menu/index.vue'
|
||||||
@@ -45,9 +47,7 @@ import HeaderRightBar from './components/HeaderRightBar/index.vue'
|
|||||||
import Logo from './components/Logo.vue'
|
import Logo from './components/Logo.vue'
|
||||||
import MenuFoldBtn from './components/MenuFoldBtn.vue'
|
import MenuFoldBtn from './components/MenuFoldBtn.vue'
|
||||||
import { useAppStore, useRouteStore } from '@/stores'
|
import { useAppStore, useRouteStore } from '@/stores'
|
||||||
import type { RouteRecordRaw } from 'vue-router'
|
|
||||||
import { isExternal } from '@/utils/validate'
|
import { isExternal } from '@/utils/validate'
|
||||||
import { searchTree } from 'xe-utils'
|
|
||||||
import { filterTree } from '@/utils'
|
import { filterTree } from '@/utils'
|
||||||
import { useDevice } from '@/hooks'
|
import { useDevice } from '@/hooks'
|
||||||
|
|
||||||
@@ -63,23 +63,11 @@ const menuRoutes = filterTree(routeStore.routes, (i) => i.meta?.hidden === false
|
|||||||
// 顶部一级菜单
|
// 顶部一级菜单
|
||||||
const topMenus = ref<RouteRecordRaw[]>([])
|
const topMenus = ref<RouteRecordRaw[]>([])
|
||||||
topMenus.value = JSON.parse(JSON.stringify(menuRoutes))
|
topMenus.value = JSON.parse(JSON.stringify(menuRoutes))
|
||||||
console.log('topMenus', toRaw(topMenus.value))
|
|
||||||
|
|
||||||
const getMenuIcon = (item: RouteRecordRaw) => {
|
const getMenuIcon = (item: RouteRecordRaw) => {
|
||||||
return item.meta?.icon || item.children?.[0].meta?.icon
|
return item.meta?.icon || item.children?.[0].meta?.icon
|
||||||
}
|
}
|
||||||
|
|
||||||
const onMenuItemClick = (key: string) => {
|
|
||||||
if (isExternal(key)) {
|
|
||||||
window.open(key)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
setTimeout(() => getLeftMenus(key))
|
|
||||||
const obj = topMenus.value.find((i) => i.path === key)
|
|
||||||
if (obj && obj.redirect === 'noRedirect') return
|
|
||||||
router.push({ path: key })
|
|
||||||
}
|
|
||||||
|
|
||||||
// 克隆是菜单的路由
|
// 克隆是菜单的路由
|
||||||
const cloneMenuRoutes: RouteRecordRaw[] = JSON.parse(JSON.stringify(menuRoutes))
|
const cloneMenuRoutes: RouteRecordRaw[] = JSON.parse(JSON.stringify(menuRoutes))
|
||||||
// 顶部一级菜单选中的
|
// 顶部一级菜单选中的
|
||||||
@@ -95,6 +83,17 @@ const getLeftMenus = (key: string) => {
|
|||||||
leftMenus.value = obj ? (obj.children as RouteRecordRaw[]) : []
|
leftMenus.value = obj ? (obj.children as RouteRecordRaw[]) : []
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const onMenuItemClick = (key: string) => {
|
||||||
|
if (isExternal(key)) {
|
||||||
|
window.open(key)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
setTimeout(() => getLeftMenus(key))
|
||||||
|
const obj = topMenus.value.find((i) => i.path === key)
|
||||||
|
if (obj && obj.redirect === 'noRedirect') return
|
||||||
|
router.push({ path: key })
|
||||||
|
}
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => route.path,
|
() => route.path,
|
||||||
(newPath) => {
|
(newPath) => {
|
||||||
|
@@ -23,9 +23,9 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useAppStore } from '@/stores'
|
|
||||||
import Menu from '../Menu/index.vue'
|
import Menu from '../Menu/index.vue'
|
||||||
import Logo from '../Logo.vue'
|
import Logo from '../Logo.vue'
|
||||||
|
import { useAppStore } from '@/stores'
|
||||||
import { useDevice } from '@/hooks'
|
import { useDevice } from '@/hooks'
|
||||||
|
|
||||||
defineOptions({ name: 'Asider' })
|
defineOptions({ name: 'Asider' })
|
||||||
|
@@ -82,10 +82,10 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useAppStore } from '@/stores'
|
|
||||||
import { ColorPicker } from 'vue-color-kit'
|
import { ColorPicker } from 'vue-color-kit'
|
||||||
import 'vue-color-kit/dist/vue-color-kit.css'
|
import 'vue-color-kit/dist/vue-color-kit.css'
|
||||||
import LayoutItem from './components/LayoutItem.vue'
|
import LayoutItem from './components/LayoutItem.vue'
|
||||||
|
import { useAppStore } from '@/stores'
|
||||||
|
|
||||||
defineOptions({ name: 'SettingDrawer' })
|
defineOptions({ name: 'SettingDrawer' })
|
||||||
const appStore = useAppStore()
|
const appStore = useAppStore()
|
||||||
@@ -133,8 +133,8 @@ const defaultColorList = [
|
|||||||
|
|
||||||
type ColorObj = {
|
type ColorObj = {
|
||||||
hex: string
|
hex: string
|
||||||
hsv: { h: number; s: number; v: number }
|
hsv: { h: number, s: number, v: number }
|
||||||
rgba: { r: number; g: number; b: number; a: number }
|
rgba: { r: number, g: number, b: number, a: number }
|
||||||
}
|
}
|
||||||
|
|
||||||
// 改变主题色
|
// 改变主题色
|
||||||
|
@@ -67,15 +67,16 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Modal } from '@arco-design/web-vue'
|
import { Modal } from '@arco-design/web-vue'
|
||||||
import { useUserStore } from '@/stores'
|
import { useFullscreen } from '@vueuse/core'
|
||||||
import SettingDrawer from './SettingDrawer.vue'
|
import SettingDrawer from './SettingDrawer.vue'
|
||||||
import Message from './Message.vue'
|
import Message from './Message.vue'
|
||||||
|
import { useUserStore } from '@/stores'
|
||||||
import { isMobile } from '@/utils'
|
import { isMobile } from '@/utils'
|
||||||
import { useFullscreen } from '@vueuse/core'
|
|
||||||
|
defineOptions({ name: 'HeaderRight' })
|
||||||
|
|
||||||
const { isFullscreen, toggle } = useFullscreen()
|
const { isFullscreen, toggle } = useFullscreen()
|
||||||
|
|
||||||
defineOptions({ name: 'HeaderRight' })
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const userStore = useUserStore()
|
const userStore = useUserStore()
|
||||||
const SettingDrawerRef = ref<InstanceType<typeof SettingDrawer>>()
|
const SettingDrawerRef = ref<InstanceType<typeof SettingDrawer>>()
|
||||||
|
@@ -9,6 +9,9 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useAppStore } from '@/stores'
|
import { useAppStore } from '@/stores'
|
||||||
|
|
||||||
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
|
collapsed: false
|
||||||
|
})
|
||||||
const appStore = useAppStore()
|
const appStore = useAppStore()
|
||||||
const title = computed(() => appStore.getTitle())
|
const title = computed(() => appStore.getTitle())
|
||||||
const logo = computed(() => appStore.getLogo())
|
const logo = computed(() => appStore.getLogo())
|
||||||
@@ -16,10 +19,6 @@ const logo = computed(() => appStore.getLogo())
|
|||||||
interface Props {
|
interface Props {
|
||||||
collapsed?: boolean
|
collapsed?: boolean
|
||||||
}
|
}
|
||||||
const props = withDefaults(defineProps<Props>(), {
|
|
||||||
collapsed: false
|
|
||||||
})
|
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
// 跳转首页
|
// 跳转首页
|
||||||
const toHome = () => {
|
const toHome = () => {
|
||||||
|
@@ -2,9 +2,9 @@
|
|||||||
<template v-if="!item.meta?.hidden">
|
<template v-if="!item.meta?.hidden">
|
||||||
<a-menu-item
|
<a-menu-item
|
||||||
v-if="
|
v-if="
|
||||||
isOneShowingChild &&
|
isOneShowingChild
|
||||||
(!onlyOneChild?.children || onlyOneChild?.meta?.noShowingChildren) &&
|
&& (!onlyOneChild?.children || onlyOneChild?.meta?.noShowingChildren)
|
||||||
!item?.meta?.alwaysShow
|
&& !item?.meta?.alwaysShow
|
||||||
"
|
"
|
||||||
v-bind="attrs"
|
v-bind="attrs"
|
||||||
:key="onlyOneChild?.path"
|
:key="onlyOneChild?.path"
|
||||||
@@ -32,14 +32,14 @@ import type { RouteRecordRaw } from 'vue-router'
|
|||||||
import MenuIcon from './MenuIcon.vue'
|
import MenuIcon from './MenuIcon.vue'
|
||||||
|
|
||||||
defineOptions({ name: 'MenuItem' })
|
defineOptions({ name: 'MenuItem' })
|
||||||
|
const props = withDefaults(defineProps<Props>(), {})
|
||||||
|
|
||||||
const attrs = useAttrs()
|
const attrs = useAttrs()
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
item: RouteRecordRaw
|
item: RouteRecordRaw
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = withDefaults(defineProps<Props>(), {})
|
|
||||||
|
|
||||||
// 如果hidden: false那么代表这个路由项显示在左侧菜单栏中
|
// 如果hidden: false那么代表这个路由项显示在左侧菜单栏中
|
||||||
// 如果props.item的子项chidren只有一个hidden: false的子元素, 那么onlyOneChild就表示这个子元素
|
// 如果props.item的子项chidren只有一个hidden: false的子元素, 那么onlyOneChild就表示这个子元素
|
||||||
const onlyOneChild = ref<RouteRecordRaw | null>(null)
|
const onlyOneChild = ref<RouteRecordRaw | null>(null)
|
||||||
|
@@ -11,21 +11,23 @@
|
|||||||
@menu-item-click="onMenuItemClick"
|
@menu-item-click="onMenuItemClick"
|
||||||
@collapse="onCollapse"
|
@collapse="onCollapse"
|
||||||
>
|
>
|
||||||
<MenuItem v-for="(route, index) in sidebarRoutes" :key="route.path + index" :item="route"></MenuItem>
|
<MenuItem v-for="(item, index) in sidebarRoutes" :key="item.path + index" :item="item"></MenuItem>
|
||||||
</a-menu>
|
</a-menu>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useAppStore, useRouteStore } from '@/stores'
|
|
||||||
import MenuItem from './MenuItem.vue'
|
|
||||||
import { isExternal } from '@/utils/validate'
|
|
||||||
import type { RouteRecordRaw } from 'vue-router'
|
import type { RouteRecordRaw } from 'vue-router'
|
||||||
import type { CSSProperties } from 'vue'
|
import type { CSSProperties } from 'vue'
|
||||||
|
import MenuItem from './MenuItem.vue'
|
||||||
|
import { useAppStore, useRouteStore } from '@/stores'
|
||||||
|
import { isExternal } from '@/utils/validate'
|
||||||
import { useDevice } from '@/hooks'
|
import { useDevice } from '@/hooks'
|
||||||
|
|
||||||
defineOptions({ name: 'Menu' })
|
defineOptions({ name: 'AppMenu' })
|
||||||
|
const props = withDefaults(defineProps<Props>(), {})
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(e: 'menuItemClickAfter'): void
|
(e: 'menu-item-click-after'): void
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@@ -33,15 +35,12 @@ interface Props {
|
|||||||
menuStyle?: CSSProperties
|
menuStyle?: CSSProperties
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = withDefaults(defineProps<Props>(), {})
|
|
||||||
|
|
||||||
const { isDesktop } = useDevice()
|
const { isDesktop } = useDevice()
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const appStore = useAppStore()
|
const appStore = useAppStore()
|
||||||
const routeStore = useRouteStore()
|
const routeStore = useRouteStore()
|
||||||
const sidebarRoutes = computed(() => (props.menus ? props.menus : routeStore.routes))
|
const sidebarRoutes = computed(() => (props.menus ? props.menus : routeStore.routes))
|
||||||
// console.log('sidebarRoutes', sidebarRoutes.value)
|
|
||||||
|
|
||||||
// 菜单垂直模式/水平模式
|
// 菜单垂直模式/水平模式
|
||||||
const mode = computed(() => {
|
const mode = computed(() => {
|
||||||
@@ -73,7 +72,7 @@ const onMenuItemClick = (key: string) => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
router.push({ path: key })
|
router.push({ path: key })
|
||||||
emit('menuItemClickAfter')
|
emit('menu-item-click-after')
|
||||||
}
|
}
|
||||||
|
|
||||||
// 折叠状态改变时触发
|
// 折叠状态改变时触发
|
||||||
|
@@ -20,7 +20,7 @@
|
|||||||
:drawer-style="{
|
:drawer-style="{
|
||||||
'border-right': '1px solid var(--color-border-2)',
|
'border-right': '1px solid var(--color-border-2)',
|
||||||
'box-sizing': 'border-box',
|
'box-sizing': 'border-box',
|
||||||
'background-color': 'var(--color-bg-1)'
|
'background-color': 'var(--color-bg-1)',
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<Logo :collapsed="false"></Logo>
|
<Logo :collapsed="false"></Logo>
|
||||||
|
@@ -41,8 +41,8 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { RouteRecordRaw } from 'vue-router'
|
import type { RouteRecordRaw } from 'vue-router'
|
||||||
import { useTabsStore, useAppStore } from '@/stores'
|
|
||||||
import MagicIcon from './MagicIcon.vue'
|
import MagicIcon from './MagicIcon.vue'
|
||||||
|
import { useAppStore, useTabsStore } from '@/stores'
|
||||||
|
|
||||||
defineOptions({ name: 'Tabs' })
|
defineOptions({ name: 'Tabs' })
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
@@ -53,14 +53,6 @@ const tabsStore = useTabsStore()
|
|||||||
// 重置, 同时把 affix: true 的路由筛选出来
|
// 重置, 同时把 affix: true 的路由筛选出来
|
||||||
tabsStore.reset()
|
tabsStore.reset()
|
||||||
|
|
||||||
// 监听路由变化
|
|
||||||
watch(
|
|
||||||
() => route.path,
|
|
||||||
() => {
|
|
||||||
handleRouteChange()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
// 路由发生改变触发
|
// 路由发生改变触发
|
||||||
const handleRouteChange = () => {
|
const handleRouteChange = () => {
|
||||||
const item = { ...route } as unknown as RouteRecordRaw
|
const item = { ...route } as unknown as RouteRecordRaw
|
||||||
@@ -72,6 +64,14 @@ const handleRouteChange = () => {
|
|||||||
}
|
}
|
||||||
handleRouteChange()
|
handleRouteChange()
|
||||||
|
|
||||||
|
// 监听路由变化
|
||||||
|
watch(
|
||||||
|
() => route.path,
|
||||||
|
() => {
|
||||||
|
handleRouteChange()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
// 点击页签
|
// 点击页签
|
||||||
const handleTabClick = (key: string) => {
|
const handleTabClick = (key: string) => {
|
||||||
router.push({ path: key })
|
router.push({ path: key })
|
||||||
|
18
src/main.ts
18
src/main.ts
@@ -1,22 +1,14 @@
|
|||||||
import { createApp } from 'vue'
|
import { createApp } from 'vue'
|
||||||
import pinia from '@/stores'
|
import ArcoVue, { Card, Modal } from '@arco-design/web-vue'
|
||||||
|
|
||||||
import App from './App.vue'
|
|
||||||
import router from './router'
|
|
||||||
|
|
||||||
// 引入 Arco Design 组件库以及自定义主题
|
|
||||||
import ArcoVue from '@arco-design/web-vue'
|
|
||||||
import '@/styles/arco-ui/index.less'
|
import '@/styles/arco-ui/index.less'
|
||||||
import 'md-editor-v3/lib/style.css'
|
import 'md-editor-v3/lib/style.css'
|
||||||
// import '@arco-themes/vue-gi-demo/index.less'
|
// import '@arco-themes/vue-gi-demo/index.less'
|
||||||
// import '@arco-design/web-vue/dist/arco.css'
|
// import '@arco-design/web-vue/dist/arco.css'
|
||||||
|
|
||||||
// 对特定组件进行默认配置
|
|
||||||
import { Card, Modal } from '@arco-design/web-vue'
|
|
||||||
Card.props.bordered = false
|
|
||||||
|
|
||||||
// 额外引入 Arco Design Icon图标库
|
// 额外引入 Arco Design Icon图标库
|
||||||
import ArcoVueIcon from '@arco-design/web-vue/es/icon'
|
import ArcoVueIcon from '@arco-design/web-vue/es/icon'
|
||||||
|
import App from './App.vue'
|
||||||
|
import router from './router'
|
||||||
|
|
||||||
import '@/router/permission'
|
import '@/router/permission'
|
||||||
|
|
||||||
@@ -34,6 +26,10 @@ import 'virtual:svg-icons-register'
|
|||||||
|
|
||||||
// 自定义指令
|
// 自定义指令
|
||||||
import directives from './directives'
|
import directives from './directives'
|
||||||
|
import pinia from '@/stores'
|
||||||
|
|
||||||
|
// 对特定组件进行默认配置
|
||||||
|
Card.props.bordered = false
|
||||||
|
|
||||||
const app = createApp(App)
|
const app = createApp(App)
|
||||||
Modal._context = app._context
|
Modal._context = app._context
|
||||||
|
@@ -1,2 +1,2 @@
|
|||||||
/** 省市区数据类型 */
|
/** 省市区数据类型 */
|
||||||
export type MockAreaItem = { label: string; code: string; children?: MockAreaItem[] }
|
export type MockAreaItem = { label: string, code: string, children?: MockAreaItem[] }
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
import { defineMock } from '../_base'
|
|
||||||
import { resultSuccess, getDelayTime } from '../_utils'
|
|
||||||
import { findTree } from 'xe-utils'
|
import { findTree } from 'xe-utils'
|
||||||
|
import { defineMock } from '../_base'
|
||||||
|
import { getDelayTime, resultSuccess } from '../_utils'
|
||||||
import areaData from '../_data/area'
|
import areaData from '../_data/area'
|
||||||
|
|
||||||
export default defineMock([
|
export default defineMock([
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { createRouter, createWebHashHistory, type RouteRecordRaw } from 'vue-router'
|
import { type RouteRecordRaw, createRouter, createWebHashHistory } from 'vue-router'
|
||||||
|
|
||||||
/** 默认布局 */
|
/** 默认布局 */
|
||||||
const Layout = () => import('@/layout/index.vue')
|
const Layout = () => import('@/layout/index.vue')
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import router from '@/router'
|
import router from '@/router'
|
||||||
import { useUserStore, useRouteStore } from '@/stores'
|
import { useRouteStore, useUserStore } from '@/stores'
|
||||||
import { getToken } from '@/utils/auth'
|
import { getToken } from '@/utils/auth'
|
||||||
import { isHttp } from '@/utils/validate'
|
import { isHttp } from '@/utils/validate'
|
||||||
|
|
||||||
@@ -46,7 +46,7 @@ router.beforeEach(async (to, from, next) => {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 如果没有 Token
|
// 如果没有 Token
|
||||||
if (whiteList.indexOf(to.path) !== -1) {
|
if (whiteList.includes(to.path)) {
|
||||||
// 如果在免登录的白名单中,则直接进入
|
// 如果在免登录的白名单中,则直接进入
|
||||||
next()
|
next()
|
||||||
} else {
|
} else {
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
import { listOptionDict, type BasicConfigResp } from '@/apis'
|
|
||||||
import { computed, reactive, toRefs } from 'vue'
|
import { computed, reactive, toRefs } from 'vue'
|
||||||
import { generate, getRgbStr } from '@arco-design/color'
|
import { generate, getRgbStr } from '@arco-design/color'
|
||||||
|
import { type BasicConfigResp, listOptionDict } from '@/apis'
|
||||||
import defaultSettings from '@/config/setting.json'
|
import defaultSettings from '@/config/setting.json'
|
||||||
|
|
||||||
const storeSetup = () => {
|
const storeSetup = () => {
|
||||||
@@ -20,6 +20,17 @@ const storeSetup = () => {
|
|||||||
return obj
|
return obj
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 设置主题色
|
||||||
|
const setThemeColor = (color: string) => {
|
||||||
|
if (!color) return
|
||||||
|
settingConfig.themeColor = color
|
||||||
|
const list = generate(settingConfig.themeColor, { list: true, dark: settingConfig.theme === 'dark' })
|
||||||
|
list.forEach((color: string, index: number) => {
|
||||||
|
const rgbStr = getRgbStr(color)
|
||||||
|
document.body.style.setProperty(`--primary-${index + 1}`, rgbStr)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// 切换主题 暗黑模式|简白模式
|
// 切换主题 暗黑模式|简白模式
|
||||||
const toggleTheme = (dark: boolean) => {
|
const toggleTheme = (dark: boolean) => {
|
||||||
if (dark) {
|
if (dark) {
|
||||||
@@ -32,17 +43,6 @@ const storeSetup = () => {
|
|||||||
setThemeColor(settingConfig.themeColor)
|
setThemeColor(settingConfig.themeColor)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 设置主题色
|
|
||||||
const setThemeColor = (color: string) => {
|
|
||||||
if (!color) return
|
|
||||||
settingConfig.themeColor = color
|
|
||||||
const list = generate(settingConfig.themeColor, { list: true, dark: settingConfig.theme === 'dark' })
|
|
||||||
list.forEach((color: string, index: number) => {
|
|
||||||
const rgbStr = getRgbStr(color)
|
|
||||||
document.body.style.setProperty(`--primary-${index + 1}`, rgbStr)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 初始化主题
|
// 初始化主题
|
||||||
const initTheme = () => {
|
const initTheme = () => {
|
||||||
if (!settingConfig.themeColor) return
|
if (!settingConfig.themeColor) return
|
||||||
|
@@ -1,11 +1,11 @@
|
|||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
import type { RouteRecordRaw } from 'vue-router'
|
import type { RouteRecordRaw } from 'vue-router'
|
||||||
import { constantRoutes } from '@/router'
|
|
||||||
import ParentView from '@/components/ParentView/index.vue'
|
|
||||||
import { getUserRoute, type RouteItem } from '@/apis'
|
|
||||||
import { mapTree, toTreeArray } from 'xe-utils'
|
import { mapTree, toTreeArray } from 'xe-utils'
|
||||||
import { cloneDeep, omit } from 'lodash-es'
|
import { cloneDeep, omit } from 'lodash-es'
|
||||||
|
import { constantRoutes } from '@/router'
|
||||||
|
import ParentView from '@/components/ParentView/index.vue'
|
||||||
|
import { type RouteItem, getUserRoute } from '@/apis'
|
||||||
import { transformPathToName } from '@/utils'
|
import { transformPathToName } from '@/utils'
|
||||||
|
|
||||||
const Layout = () => import('@/layout/index.vue')
|
const Layout = () => import('@/layout/index.vue')
|
||||||
@@ -58,7 +58,7 @@ const formatAsyncRoutes = (menus: RouteItem[]) => {
|
|||||||
title: item.title,
|
title: item.title,
|
||||||
hidden: item.isHidden,
|
hidden: item.isHidden,
|
||||||
keepAlive: item.isCache,
|
keepAlive: item.isCache,
|
||||||
alwaysShow: item.type == 1,
|
alwaysShow: item.type === 1,
|
||||||
icon: item.icon
|
icon: item.icon
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,9 +1,9 @@
|
|||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import router from '@/router'
|
import type { RouteRecordName, RouteRecordRaw } from 'vue-router'
|
||||||
import type { RouteRecordRaw, RouteRecordName } from 'vue-router'
|
|
||||||
import { useRouteStore } from '@/stores'
|
|
||||||
import _XEUtils_ from 'xe-utils'
|
import _XEUtils_ from 'xe-utils'
|
||||||
|
import router from '@/router'
|
||||||
|
import { useRouteStore } from '@/stores'
|
||||||
|
|
||||||
const storeSetup = () => {
|
const storeSetup = () => {
|
||||||
const tagList = ref<RouteRecordRaw[]>([]) // 保存页签tab的数组
|
const tagList = ref<RouteRecordRaw[]>([]) // 保存页签tab的数组
|
||||||
|
@@ -1,19 +1,19 @@
|
|||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
import { ref, reactive, computed } from 'vue'
|
import { computed, reactive, ref } from 'vue'
|
||||||
import { resetRouter } from '@/router'
|
import { resetRouter } from '@/router'
|
||||||
import {
|
import {
|
||||||
accountLogin as accountLoginApi,
|
|
||||||
phoneLogin as phoneLoginApi,
|
|
||||||
emailLogin as emailLoginApi,
|
|
||||||
socialLogin as socialLoginApi,
|
|
||||||
logout as logoutApi,
|
|
||||||
getUserInfo as getUserInfoApi,
|
|
||||||
type AccountLoginReq,
|
type AccountLoginReq,
|
||||||
type PhoneLoginReq,
|
|
||||||
type EmailLoginReq,
|
type EmailLoginReq,
|
||||||
type UserInfo
|
type PhoneLoginReq,
|
||||||
|
type UserInfo,
|
||||||
|
accountLogin as accountLoginApi,
|
||||||
|
emailLogin as emailLoginApi,
|
||||||
|
getUserInfo as getUserInfoApi,
|
||||||
|
logout as logoutApi,
|
||||||
|
phoneLogin as phoneLoginApi,
|
||||||
|
socialLogin as socialLoginApi
|
||||||
} from '@/apis'
|
} from '@/apis'
|
||||||
import { setToken, clearToken, getToken } from '@/utils/auth'
|
import { clearToken, getToken, setToken } from '@/utils/auth'
|
||||||
import { resetHasRouteFlag } from '@/router/permission'
|
import { resetHasRouteFlag } from '@/router/permission'
|
||||||
import getAvatar from '@/utils/avatar'
|
import getAvatar from '@/utils/avatar'
|
||||||
|
|
||||||
@@ -76,6 +76,15 @@ const storeSetup = () => {
|
|||||||
token.value = res.data.token
|
token.value = res.data.token
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 退出登录回调
|
||||||
|
const logoutCallBack = async () => {
|
||||||
|
roles.value = []
|
||||||
|
permissions.value = []
|
||||||
|
pwdExpiredShow.value = true
|
||||||
|
resetToken()
|
||||||
|
resetRouter()
|
||||||
|
}
|
||||||
|
|
||||||
// 退出登录
|
// 退出登录
|
||||||
const logout = async () => {
|
const logout = async () => {
|
||||||
try {
|
try {
|
||||||
@@ -87,15 +96,6 @@ const storeSetup = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 退出登录回调
|
|
||||||
const logoutCallBack = async () => {
|
|
||||||
roles.value = []
|
|
||||||
permissions.value = []
|
|
||||||
pwdExpiredShow.value = true
|
|
||||||
resetToken()
|
|
||||||
resetRouter()
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取用户信息
|
// 获取用户信息
|
||||||
const getInfo = async () => {
|
const getInfo = async () => {
|
||||||
const res = await getUserInfoApi()
|
const res = await getUserInfoApi()
|
||||||
|
2
src/types/router.d.ts
vendored
2
src/types/router.d.ts
vendored
@@ -6,7 +6,7 @@ declare module 'vue-router' {
|
|||||||
title?: string
|
title?: string
|
||||||
/** 设置该路由的图标, 记得将svg导入 @/icons/svg */
|
/** 设置该路由的图标, 记得将svg导入 @/icons/svg */
|
||||||
svgIcon?: string
|
svgIcon?: string
|
||||||
/** 设置该路由的图标, 直接使用Arco Design的Icon(与svgIcon同时设置时, svgIcon将优先生效)*/
|
/** 设置该路由的图标, 直接使用Arco Design的Icon(与svgIcon同时设置时, svgIcon将优先生效) */
|
||||||
icon?: string
|
icon?: string
|
||||||
/** 默认false, 设置true的时候该路由不会在侧边栏出现 */
|
/** 默认false, 设置true的时候该路由不会在侧边栏出现 */
|
||||||
hidden?: boolean
|
hidden?: boolean
|
||||||
|
@@ -25,7 +25,7 @@ export function downloadByUrl({
|
|||||||
isSameHost: boolean
|
isSameHost: boolean
|
||||||
}): Promise<boolean> {
|
}): Promise<boolean> {
|
||||||
// 是否同源
|
// 是否同源
|
||||||
const isSameHost = new URL(url).host == location.host
|
const isSameHost = new URL(url).host === location.host
|
||||||
return new Promise<boolean>((resolve) => {
|
return new Promise<boolean>((resolve) => {
|
||||||
if (isSameHost) {
|
if (isSameHost) {
|
||||||
const link = document.createElement('a')
|
const link = document.createElement('a')
|
||||||
@@ -43,7 +43,7 @@ export function downloadByUrl({
|
|||||||
return resolve(true)
|
return resolve(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (url.indexOf('?') === -1) {
|
if (!url.includes('?')) {
|
||||||
url += '?download'
|
url += '?download'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -15,9 +15,9 @@ export function encryptByMd5(txt: string) {
|
|||||||
return md5(txt).toString()
|
return md5(txt).toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
const publicKey =
|
const publicKey
|
||||||
'MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAM51dgYtMyF+tTQt80sfFOpSV27a7t9u' +
|
= 'MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAM51dgYtMyF+tTQt80sfFOpSV27a7t9u'
|
||||||
'aUVeFrdGiVxscuizE7H8SMntYqfn9lp8a5GH5P1/GGehVjUD2gF/4kcCAwEAAQ=='
|
+ 'aUVeFrdGiVxscuizE7H8SMntYqfn9lp8a5GH5P1/GGehVjUD2gF/4kcCAwEAAQ=='
|
||||||
|
|
||||||
export function encryptByRsa(txt: string) {
|
export function encryptByRsa(txt: string) {
|
||||||
const encryptor = new JSEncrypt()
|
const encryptor = new JSEncrypt()
|
||||||
|
@@ -1,12 +1,12 @@
|
|||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import qs from 'query-string'
|
import qs from 'query-string'
|
||||||
import type { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios'
|
import type { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios'
|
||||||
|
import NProgress from 'nprogress'
|
||||||
import { useUserStore } from '@/stores'
|
import { useUserStore } from '@/stores'
|
||||||
import { getToken } from '@/utils/auth'
|
import { getToken } from '@/utils/auth'
|
||||||
import modalErrorWrapper from '@/utils/modal-error-wrapper'
|
import modalErrorWrapper from '@/utils/modal-error-wrapper'
|
||||||
import messageErrorWrapper from '@/utils/message-error-wrapper'
|
import messageErrorWrapper from '@/utils/message-error-wrapper'
|
||||||
import notificationErrorWrapper from '@/utils/notification-error-wrapper'
|
import notificationErrorWrapper from '@/utils/notification-error-wrapper'
|
||||||
import NProgress from 'nprogress'
|
|
||||||
import 'nprogress/nprogress.css'
|
import 'nprogress/nprogress.css'
|
||||||
import router from '@/router'
|
import router from '@/router'
|
||||||
|
|
||||||
@@ -103,11 +103,11 @@ http.interceptors.response.use(
|
|||||||
(error) => {
|
(error) => {
|
||||||
NProgress.done()
|
NProgress.done()
|
||||||
const response = Object.assign({}, error.response)
|
const response = Object.assign({}, error.response)
|
||||||
response &&
|
response
|
||||||
messageErrorWrapper({
|
&& messageErrorWrapper({
|
||||||
content: StatusCodeMessage[response.status] || '服务器暂时未响应,请刷新页面并重试。若无法解决,请联系管理员',
|
content: StatusCodeMessage[response.status] || '服务器暂时未响应,请刷新页面并重试。若无法解决,请联系管理员',
|
||||||
duration: 5 * 1000
|
duration: 5 * 1000
|
||||||
})
|
})
|
||||||
return Promise.reject(error)
|
return Promise.reject(error)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
import { isExternal } from '@/utils/validate'
|
|
||||||
import { browse, mapTree } from 'xe-utils'
|
import { browse, mapTree } from 'xe-utils'
|
||||||
import { upperFirst, camelCase } from 'lodash-es'
|
import { camelCase, upperFirst } from 'lodash-es'
|
||||||
import { Message } from '@arco-design/web-vue'
|
import { Message } from '@arco-design/web-vue'
|
||||||
|
import { isExternal } from '@/utils/validate'
|
||||||
|
|
||||||
export function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
|
export function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
|
||||||
return obj[key]
|
return obj[key]
|
||||||
@@ -14,16 +14,17 @@ export function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
|
|||||||
* pos="both": 去除两边空格
|
* pos="both": 去除两边空格
|
||||||
* pos="left": 去除左边空格
|
* pos="left": 去除左边空格
|
||||||
* pos="right": 去除右边空格
|
* pos="right": 去除右边空格
|
||||||
* pos="all": 去除所有空格 */
|
* pos="all": 去除所有空格
|
||||||
|
*/
|
||||||
type Pos = 'both' | 'left' | 'right' | 'all'
|
type Pos = 'both' | 'left' | 'right' | 'all'
|
||||||
export function trim(str: string, pos: Pos = 'both'): string {
|
export function trim(str: string, pos: Pos = 'both'): string {
|
||||||
if (pos == 'both') {
|
if (pos === 'both') {
|
||||||
return str.replace(/^\s+|\s+$/g, '')
|
return str.replace(/^\s+|\s+$/g, '')
|
||||||
} else if (pos == 'left') {
|
} else if (pos === 'left') {
|
||||||
return str.replace(/^\s*/, '')
|
return str.replace(/^\s*/, '')
|
||||||
} else if (pos == 'right') {
|
} else if (pos === 'right') {
|
||||||
return str.replace(/(\s*$)/g, '')
|
return str.replace(/(\s*$)/g, '')
|
||||||
} else if (pos == 'all') {
|
} else if (pos === 'all') {
|
||||||
return str.replace(/\s+/g, '')
|
return str.replace(/\s+/g, '')
|
||||||
} else {
|
} else {
|
||||||
return str
|
return str
|
||||||
@@ -32,7 +33,8 @@ export function trim(str: string, pos: Pos = 'both'): string {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据数字获取对应的汉字
|
* 根据数字获取对应的汉字
|
||||||
* @param {number} num - 数字(0-10) */
|
* @param {number} num - 数字(0-10)
|
||||||
|
*/
|
||||||
export function getHanByNumber(num: number): string {
|
export function getHanByNumber(num: number): string {
|
||||||
const str = '零一二三四五六七八九十'
|
const str = '零一二三四五六七八九十'
|
||||||
return str.charAt(num)
|
return str.charAt(num)
|
||||||
@@ -41,7 +43,8 @@ export function getHanByNumber(num: number): string {
|
|||||||
/**
|
/**
|
||||||
* 获取指定整数范围内的随机整数
|
* 获取指定整数范围内的随机整数
|
||||||
* @param {number} start - 开始范围
|
* @param {number} start - 开始范围
|
||||||
* @param {number} end - 结束范围 */
|
* @param {number} end - 结束范围
|
||||||
|
*/
|
||||||
export function getRandomInterger(start = 0, end: number): number {
|
export function getRandomInterger(start = 0, end: number): number {
|
||||||
const range = end - start
|
const range = end - start
|
||||||
return Math.floor(Math.random() * range + start)
|
return Math.floor(Math.random() * range + start)
|
||||||
@@ -59,30 +62,31 @@ export function getTypeOf(value: any) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @desc 格式化电话号码
|
* @desc 格式化电话号码
|
||||||
* @demo 183-7983-6654 */
|
@demo 183-7983-6654 */
|
||||||
export function formatPhone(mobile: string, formatStr = '-') {
|
export function formatPhone(mobile: string, formatStr = '-') {
|
||||||
return mobile.replace(/(?=(\d{4})+$)/g, formatStr)
|
return mobile.replace(/(?=(\d{4})+$)/g, formatStr)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @desc 手机号脱敏
|
* @desc 手机号脱敏
|
||||||
* @demo 155****8810 */
|
@demo 155****8810 */
|
||||||
export function hidePhone(phone: string) {
|
export function hidePhone(phone: string) {
|
||||||
return phone.replace(/^(\d{3})\d{4}(\d{4})$/, '$1****$2')
|
return phone.replace(/^(\d{3})\d{4}(\d{4})$/, '$1****$2')
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @desc 检测数据是否为空数据 */
|
/** @desc 检测数据是否为空数据 */
|
||||||
export function isEmpty(data: unknown) {
|
export function isEmpty(data: unknown) {
|
||||||
if (data === '' || data === 'undefined' || data === undefined || data === null || data === 'null') {
|
if (data === '' || data === 'undefined' || data === undefined || data == null || data === 'null') {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return JSON.stringify(data) == '{}' || JSON.stringify(data) == '[]' || JSON.stringify(data) == '[{}]'
|
return JSON.stringify(data) === '{}' || JSON.stringify(data) === '[]' || JSON.stringify(data) === '[{}]'
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @desc 大小写转换
|
* @desc 大小写转换
|
||||||
* @param {string} str 待转换的字符串
|
* @param {string} str 待转换的字符串
|
||||||
* @param {number} type 1:全大写 2:全小写 3:首字母大写 */
|
* @param {number} type 1:全大写 2:全小写 3:首字母大写
|
||||||
|
*/
|
||||||
export function toCase(str: string, type: number) {
|
export function toCase(str: string, type: number) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 1:
|
case 1:
|
||||||
@@ -100,39 +104,39 @@ export function toCase(str: string, type: number) {
|
|||||||
* @desc 获取随机数
|
* @desc 获取随机数
|
||||||
* @param {number} min 最小值
|
* @param {number} min 最小值
|
||||||
* @param {number} max 最大值
|
* @param {number} max 最大值
|
||||||
* */
|
*/
|
||||||
export const randomNum = (min: number, max: number) => {
|
export const randomNum = (min: number, max: number) => {
|
||||||
return Math.floor(min + Math.random() * (max + 1 - min))
|
return Math.floor(min + Math.random() * (max + 1 - min))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @desc 获取最大值 */
|
@desc 获取最大值 */
|
||||||
export const max = (arr: number[]) => {
|
export const max = (arr: number[]) => {
|
||||||
return Math.max.apply(null, arr)
|
return Math.max.apply(null, arr)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @desc 获取最小值 */
|
@desc 获取最小值 */
|
||||||
export const min = (arr: number[]) => {
|
export const min = (arr: number[]) => {
|
||||||
return Math.min.apply(null, arr)
|
return Math.min.apply(null, arr)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @desc 求和 */
|
@desc 求和 */
|
||||||
export const sum = (arr: number[]) => {
|
export const sum = (arr: number[]) => {
|
||||||
return arr.reduce((pre, cur) => pre + cur)
|
return arr.reduce((pre, cur) => pre + cur)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @desc 获取平均值 */
|
@desc 获取平均值 */
|
||||||
export const average = (arr: number[]) => {
|
export const average = (arr: number[]) => {
|
||||||
return sum(arr) / arr.length
|
return sum(arr) / arr.length
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @desc 深拷贝 */
|
@desc 深拷贝 */
|
||||||
export const deepClone = (data: any) => {
|
export const deepClone = (data: any) => {
|
||||||
if (typeof data !== 'object' || data === null) return '不是对象'
|
if (typeof data !== 'object' || data == null) return '不是对象'
|
||||||
const newData: any = Array.isArray(data) ? [] : {}
|
const newData: any = Array.isArray(data) ? [] : {}
|
||||||
for (const key in data) {
|
for (const key in data) {
|
||||||
newData[key] = typeof data[key] === 'object' ? deepClone(data[key]) : data[key]
|
newData[key] = typeof data[key] === 'object' ? deepClone(data[key]) : data[key]
|
||||||
@@ -142,35 +146,38 @@ export const deepClone = (data: any) => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @desc 判断是否是闰年
|
* @desc 判断是否是闰年
|
||||||
* @param {number} year 年份 */
|
* @param {number} year 年份
|
||||||
|
*/
|
||||||
export const isLeapYear = (year: number) => {
|
export const isLeapYear = (year: number) => {
|
||||||
return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0
|
return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @desc 判断是否是奇数
|
* @desc 判断是否是奇数
|
||||||
* @param {number} num 数字 */
|
* @param {number} num 数字
|
||||||
|
*/
|
||||||
export const isOdd = (num: number) => {
|
export const isOdd = (num: number) => {
|
||||||
return num % 2 !== 0
|
return num % 2 !== 0
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @desc 判断是否是偶数
|
* @desc 判断是否是偶数
|
||||||
* @param {number} num 数字 */
|
* @param {number} num 数字
|
||||||
|
*/
|
||||||
export const isEven = (num: number) => {
|
export const isEven = (num: number) => {
|
||||||
return !isOdd(num)
|
return !isOdd(num)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @desc 将RGB转化为十六机制 */
|
@desc 将RGB转化为十六机制 */
|
||||||
export const rgbToHex = (r: number, g: number, b: number) => {
|
export const rgbToHex = (r: number, g: number, b: number) => {
|
||||||
return '#' + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1)
|
return `#${((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1)}`
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @desc 获取随机十六进制颜色 */
|
@desc 获取随机十六进制颜色 */
|
||||||
export const randomHex = () => {
|
export const randomHex = () => {
|
||||||
return `#${Math.floor(Math.random() * 0xffffff)
|
return `#${Math.floor(Math.random() * 0xFFFFFF)
|
||||||
.toString(16)
|
.toString(16)
|
||||||
.padEnd(6, '0')}`
|
.padEnd(6, '0')}`
|
||||||
}
|
}
|
||||||
@@ -206,7 +213,7 @@ export const filterTree: FilterTree = (values, fn) => {
|
|||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
type SortTree = <T extends { sort: number; children?: T[] }>(array: T[]) => T[]
|
type SortTree = <T extends { sort: number, children?: T[] }>(array: T[]) => T[]
|
||||||
/**
|
/**
|
||||||
* @desc 排序树
|
* @desc 排序树
|
||||||
* @param values /
|
* @param values /
|
||||||
@@ -238,7 +245,7 @@ export const formatFileSize = (fileSize: number) => {
|
|||||||
}
|
}
|
||||||
const unitArr = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
|
const unitArr = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
|
||||||
let index = 0
|
let index = 0
|
||||||
const srcSize = parseFloat(fileSize.toString())
|
const srcSize = Number.parseFloat(fileSize.toString())
|
||||||
index = Math.floor(Math.log(srcSize) / Math.log(1024))
|
index = Math.floor(Math.log(srcSize) / Math.log(1024))
|
||||||
const size = srcSize / 1024 ** index
|
const size = srcSize / 1024 ** index
|
||||||
return `${size.toFixed(2)} ${unitArr[index]}`
|
return `${size.toFixed(2)} ${unitArr[index]}`
|
||||||
|
@@ -18,14 +18,14 @@ export const Code_6 = /^\d{6}$/
|
|||||||
export const Code_4 = /^\d{4}$/
|
export const Code_4 = /^\d{4}$/
|
||||||
|
|
||||||
/** @desc 正则-url链接 */
|
/** @desc 正则-url链接 */
|
||||||
export const Url =
|
export const Url
|
||||||
/(((^https?:(?:\/\/)?)(?:[-;:&=+$,\w]+@)?[A-Za-z0-9.-]+(?::\d+)?|(?:www.|[-;:&=+$,\w]+@)[A-Za-z0-9.-]+)((?:\/[+~%/.\w-_]*)?\??(?:[-+=&;%@.\w_]*)#?(?:[\w]*))?)$/
|
= /(((^https?:(?:\/\/)?)(?:[-;:&=+$,\w]+@)?[A-Za-z0-9.-]+(?::\d+)?|(?:www.|[-;:&=+$,\w]+@)[A-Za-z0-9.-]+)((?:\/[+~%/.\w-_]*)?\??(?:[-+=&;%@.\w_]*)#?(?:[\w]*))?)$/
|
||||||
|
|
||||||
/** @desc 正则-16进颜色值 #333 #8c8c8c */
|
/** @desc 正则-16进颜色值 #333 #8c8c8c */
|
||||||
export const ColorRegex = /^#?([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$/
|
export const ColorRegex = /^#?([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$/
|
||||||
|
|
||||||
/** @desc 正则-只能是中文 */
|
/** @desc 正则-只能是中文 */
|
||||||
export const OnlyCh = /^[\u4e00-\u9fa5]+$/gi
|
export const OnlyCh = /^[\u4E00-\u9FA5]+$/gi
|
||||||
|
|
||||||
/** @desc 正则-只能是英文 */
|
/** @desc 正则-只能是英文 */
|
||||||
export const OnlyEn = /^[a-zA-Z]*$/
|
export const OnlyEn = /^[a-zA-Z]*$/
|
||||||
|
@@ -6,5 +6,5 @@ export const isExternal = (path: string) => {
|
|||||||
|
|
||||||
/** 判断 url 是否是 http 或 https */
|
/** 判断 url 是否是 http 或 https */
|
||||||
export function isHttp(url: string) {
|
export function isHttp(url: string) {
|
||||||
return url.indexOf('http://') !== -1 || url.indexOf('https://') !== -1
|
return url.includes('http://') || url.includes('https://')
|
||||||
}
|
}
|
||||||
|
@@ -23,13 +23,12 @@ import Icon500 from '@/components/icons/Icon500.vue'
|
|||||||
|
|
||||||
defineOptions({ name: 'ErrorPage' })
|
defineOptions({ name: 'ErrorPage' })
|
||||||
|
|
||||||
interface Props {
|
|
||||||
code: number
|
|
||||||
}
|
|
||||||
const props = withDefaults(defineProps<Props>(), {
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
code: 403
|
code: 403
|
||||||
})
|
})
|
||||||
|
interface Props {
|
||||||
|
code: number
|
||||||
|
}
|
||||||
const IconMap: Record<number, Component> = {
|
const IconMap: Record<number, Component> = {
|
||||||
403: Icon403,
|
403: Icon403,
|
||||||
404: Icon404,
|
404: Icon404,
|
||||||
|
@@ -9,5 +9,5 @@ const router = useRouter()
|
|||||||
const { params, query } = route
|
const { params, query } = route
|
||||||
const { path } = params
|
const { path } = params
|
||||||
|
|
||||||
router.replace({ path: '/' + path, query })
|
router.replace({ path: `/${path}`, query })
|
||||||
</script>
|
</script>
|
||||||
|
@@ -13,11 +13,28 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { listDashboardAccessTrend, type DashboardAccessTrendResp } from '@/apis'
|
|
||||||
import VCharts from 'vue-echarts'
|
import VCharts from 'vue-echarts'
|
||||||
import { graphic } from 'echarts'
|
import { graphic } from 'echarts'
|
||||||
|
import { type DashboardAccessTrendResp, listDashboardAccessTrend } from '@/apis'
|
||||||
import { useChart } from '@/hooks'
|
import { useChart } from '@/hooks'
|
||||||
|
|
||||||
|
// 提示框
|
||||||
|
const tooltipItemsHtmlString = (items) => {
|
||||||
|
return items
|
||||||
|
.map(
|
||||||
|
(el) => `<div class="content-panel">
|
||||||
|
<p>
|
||||||
|
<span style="background-color: ${el.color}" class="tooltip-item-icon"></span>
|
||||||
|
<span>${el.seriesName}</span>
|
||||||
|
</p>
|
||||||
|
<span class="tooltip-value">
|
||||||
|
${el.value}
|
||||||
|
</span>
|
||||||
|
</div>`
|
||||||
|
)
|
||||||
|
.join('')
|
||||||
|
}
|
||||||
|
|
||||||
const xData = ref<string[]>([])
|
const xData = ref<string[]>([])
|
||||||
const pvStatisticsData = ref<number[]>([])
|
const pvStatisticsData = ref<number[]>([])
|
||||||
const ipStatisticsData = ref<number[]>([])
|
const ipStatisticsData = ref<number[]>([])
|
||||||
@@ -196,23 +213,6 @@ const onChange = (days: number) => {
|
|||||||
getChartData(days)
|
getChartData(days)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 提示框
|
|
||||||
const tooltipItemsHtmlString = (items) => {
|
|
||||||
return items
|
|
||||||
.map(
|
|
||||||
(el) => `<div class="content-panel">
|
|
||||||
<p>
|
|
||||||
<span style="background-color: ${el.color}" class="tooltip-item-icon"></span>
|
|
||||||
<span>${el.seriesName}</span>
|
|
||||||
</p>
|
|
||||||
<span class="tooltip-value">
|
|
||||||
${el.value}
|
|
||||||
</span>
|
|
||||||
</div>`
|
|
||||||
)
|
|
||||||
.join('')
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getChartData(30)
|
getChartData(30)
|
||||||
})
|
})
|
||||||
|
@@ -2,7 +2,7 @@
|
|||||||
<a-card title="快捷操作" :bordered="false" size="medium" class="card gi_card_title">
|
<a-card title="快捷操作" :bordered="false" size="medium" class="card gi_card_title">
|
||||||
<a-card-grid v-for="(item, index) in list" :key="item.name" class="card-grid-item" :style="{ width: '33.33%' }">
|
<a-card-grid v-for="(item, index) in list" :key="item.name" class="card-grid-item" :style="{ width: '33.33%' }">
|
||||||
<a-card :bordered="false" hoverable>
|
<a-card :bordered="false" hoverable>
|
||||||
<a-row justify="center" align="center" :class="'animated-fade-up-' + (index + 1)">
|
<a-row justify="center" align="center" :class="`animated-fade-up-${index + 1}`">
|
||||||
<a-space direction="vertical" align="center" class="wrapper" @click="router.replace({ path: item.path })">
|
<a-space direction="vertical" align="center" class="wrapper" @click="router.replace({ path: item.path })">
|
||||||
<component :is="item.icon" :size="30" class="icon"></component>
|
<component :is="item.icon" :size="30" class="icon"></component>
|
||||||
<a-typography-text class="text">{{ item.name }}</a-typography-text>
|
<a-typography-text class="text">{{ item.name }}</a-typography-text>
|
||||||
|
@@ -7,7 +7,7 @@
|
|||||||
v-for="(item, index) in list"
|
v-for="(item, index) in list"
|
||||||
:key="index"
|
:key="index"
|
||||||
align="right"
|
align="right"
|
||||||
:class="'animated-fade-up-' + index"
|
:class="`animated-fade-up-${index}`"
|
||||||
style="overflow: hidden"
|
style="overflow: hidden"
|
||||||
>
|
>
|
||||||
<template #content>
|
<template #content>
|
||||||
|
@@ -8,7 +8,7 @@
|
|||||||
v-for="(item, index) in dataList"
|
v-for="(item, index) in dataList"
|
||||||
:key="index"
|
:key="index"
|
||||||
align="right"
|
align="right"
|
||||||
:class="'animated-fade-up-' + index"
|
:class="`animated-fade-up-${index}`"
|
||||||
style="overflow: hidden"
|
style="overflow: hidden"
|
||||||
>
|
>
|
||||||
<template #content>
|
<template #content>
|
||||||
@@ -26,7 +26,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { listDashboardNotice, type DashboardNoticeResp } from '@/apis'
|
import { type DashboardNoticeResp, listDashboardNotice } from '@/apis'
|
||||||
import { useDict } from '@/hooks/app'
|
import { useDict } from '@/hooks/app'
|
||||||
import NoticeDetailModal from '@/views/system/notice/NoticeDetailModal.vue'
|
import NoticeDetailModal from '@/views/system/notice/NoticeDetailModal.vue'
|
||||||
|
|
||||||
|
@@ -3,7 +3,7 @@
|
|||||||
<a-row align="stretch">
|
<a-row align="stretch">
|
||||||
<a-col v-for="(item, index) in list" :key="item.name" :xs="12" :sm="8" :md="8">
|
<a-col v-for="(item, index) in list" :key="item.name" :xs="12" :sm="8" :md="8">
|
||||||
<a-card-grid class="w-full h-full">
|
<a-card-grid class="w-full h-full">
|
||||||
<a-card :bordered="false" hoverable :class="'animated-fade-up-' + index">
|
<a-card :bordered="false" hoverable :class="`animated-fade-up-${index}`">
|
||||||
<a :href="item.url" target="_blank">
|
<a :href="item.url" target="_blank">
|
||||||
<section class="item">
|
<section class="item">
|
||||||
<div class="item__header">
|
<div class="item__header">
|
||||||
|
@@ -26,10 +26,10 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import NowTime from './NowTime/index.vue'
|
import NowTime from './NowTime/index.vue'
|
||||||
|
import SupportCard from './SupportCard.vue'
|
||||||
import { useDevice } from '@/hooks'
|
import { useDevice } from '@/hooks'
|
||||||
import { useUserStore } from '@/stores'
|
import { useUserStore } from '@/stores'
|
||||||
import { goodTimeText } from '@/utils'
|
import { goodTimeText } from '@/utils'
|
||||||
import SupportCard from './SupportCard.vue'
|
|
||||||
|
|
||||||
const { isDesktop } = useDevice()
|
const { isDesktop } = useDevice()
|
||||||
const userStore = useUserStore()
|
const userStore = useUserStore()
|
||||||
|
@@ -31,19 +31,17 @@
|
|||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item>
|
<a-form-item>
|
||||||
<a-space direction="vertical" fill class="w-full">
|
<a-space direction="vertical" fill class="w-full">
|
||||||
<a-button class="btn" type="primary" :loading="loading" html-type="submit" size="large" long
|
<a-button class="btn" type="primary" :loading="loading" html-type="submit" size="large" long>立即登录</a-button>
|
||||||
>立即登录
|
|
||||||
</a-button>
|
|
||||||
</a-space>
|
</a-space>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-form>
|
</a-form>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { getImageCaptcha } from '@/apis'
|
import { type FormInstance, Message } from '@arco-design/web-vue'
|
||||||
import { Message, type FormInstance, Modal } from '@arco-design/web-vue'
|
|
||||||
import { useUserStore } from '@/stores'
|
|
||||||
import { useStorage } from '@vueuse/core'
|
import { useStorage } from '@vueuse/core'
|
||||||
|
import { getImageCaptcha } from '@/apis'
|
||||||
|
import { useUserStore } from '@/stores'
|
||||||
import { encryptByRsa } from '@/utils/encrypt'
|
import { encryptByRsa } from '@/utils/encrypt'
|
||||||
|
|
||||||
const loginConfig = useStorage('login-config', {
|
const loginConfig = useStorage('login-config', {
|
||||||
@@ -62,13 +60,46 @@ const form = reactive({
|
|||||||
uuid: '',
|
uuid: '',
|
||||||
expired: false
|
expired: false
|
||||||
})
|
})
|
||||||
|
|
||||||
const rules: FormInstance['rules'] = {
|
const rules: FormInstance['rules'] = {
|
||||||
username: [{ required: true, message: '请输入用户名' }],
|
username: [{ required: true, message: '请输入用户名' }],
|
||||||
password: [{ required: true, message: '请输入密码' }],
|
password: [{ required: true, message: '请输入密码' }],
|
||||||
captcha: [{ required: true, message: '请输入验证码' }]
|
captcha: [{ required: true, message: '请输入验证码' }]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 验证码过期定时器
|
||||||
|
let timer
|
||||||
|
const startTimer = (expireTime: number) => {
|
||||||
|
if (timer) {
|
||||||
|
clearTimeout(timer)
|
||||||
|
}
|
||||||
|
const remainingTime = expireTime - Date.now()
|
||||||
|
if (remainingTime <= 0) {
|
||||||
|
form.expired = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
timer = setTimeout(() => {
|
||||||
|
form.expired = true
|
||||||
|
}, remainingTime)
|
||||||
|
}
|
||||||
|
// 组件销毁时清理定时器
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
if (timer) {
|
||||||
|
clearTimeout(timer)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const captchaImgBase64 = ref()
|
||||||
|
// 获取验证码
|
||||||
|
const getCaptcha = () => {
|
||||||
|
getImageCaptcha().then((res) => {
|
||||||
|
const { uuid, img, expireTime } = res.data
|
||||||
|
form.uuid = uuid
|
||||||
|
captchaImgBase64.value = img
|
||||||
|
form.expired = false
|
||||||
|
startTimer(expireTime)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const userStore = useUserStore()
|
const userStore = useUserStore()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
@@ -102,40 +133,6 @@ const handleLogin = async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const captchaImgBase64 = ref()
|
|
||||||
// 获取验证码
|
|
||||||
const getCaptcha = () => {
|
|
||||||
getImageCaptcha().then((res) => {
|
|
||||||
const { uuid, img, expireTime } = res.data
|
|
||||||
form.uuid = uuid
|
|
||||||
captchaImgBase64.value = img
|
|
||||||
form.expired = false
|
|
||||||
startTimer(expireTime)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 验证码过期定时器
|
|
||||||
let timer
|
|
||||||
const startTimer = (expireTime: number) => {
|
|
||||||
if (timer) {
|
|
||||||
clearTimeout(timer)
|
|
||||||
}
|
|
||||||
const remainingTime = expireTime - Date.now()
|
|
||||||
if (remainingTime <= 0) {
|
|
||||||
form.expired = true
|
|
||||||
return
|
|
||||||
}
|
|
||||||
timer = setTimeout(() => {
|
|
||||||
form.expired = true
|
|
||||||
}, remainingTime)
|
|
||||||
}
|
|
||||||
// 组件销毁时清理定时器
|
|
||||||
onBeforeUnmount(() => {
|
|
||||||
if (timer) {
|
|
||||||
clearTimeout(timer)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getCaptcha()
|
getCaptcha()
|
||||||
})
|
})
|
||||||
|
@@ -25,8 +25,7 @@
|
|||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item>
|
<a-form-item>
|
||||||
<a-space direction="vertical" fill class="w-full">
|
<a-space direction="vertical" fill class="w-full">
|
||||||
<a-button disabled class="btn" type="primary" :loading="loading" html-type="submit" size="large" long
|
<a-button disabled class="btn" type="primary" :loading="loading" html-type="submit" size="large" long>立即登录</a-button
|
||||||
>立即登录</a-button
|
|
||||||
>
|
>
|
||||||
</a-space>
|
</a-space>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
@@ -35,7 +34,7 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
// import { getEmailCaptcha } from '@/apis'
|
// import { getEmailCaptcha } from '@/apis'
|
||||||
import { Message, type FormInstance } from '@arco-design/web-vue'
|
import { type FormInstance, Message } from '@arco-design/web-vue'
|
||||||
import { useUserStore } from '@/stores'
|
import { useUserStore } from '@/stores'
|
||||||
import * as Regexp from '@/utils/regexp'
|
import * as Regexp from '@/utils/regexp'
|
||||||
|
|
||||||
|
@@ -25,8 +25,7 @@
|
|||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item>
|
<a-form-item>
|
||||||
<a-space direction="vertical" fill class="w-full">
|
<a-space direction="vertical" fill class="w-full">
|
||||||
<a-button disabled class="btn" type="primary" :loading="loading" html-type="submit" size="large" long
|
<a-button disabled class="btn" type="primary" :loading="loading" html-type="submit" size="large" long>立即登录</a-button
|
||||||
>立即登录</a-button
|
|
||||||
>
|
>
|
||||||
</a-space>
|
</a-space>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
@@ -35,7 +34,7 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
// import { getSmsCaptcha } from '@/apis'
|
// import { getSmsCaptcha } from '@/apis'
|
||||||
import { Message, type FormInstance } from '@arco-design/web-vue'
|
import { type FormInstance, Message } from '@arco-design/web-vue'
|
||||||
import { useUserStore } from '@/stores'
|
import { useUserStore } from '@/stores'
|
||||||
import * as Regexp from '@/utils/regexp'
|
import * as Regexp from '@/utils/regexp'
|
||||||
|
|
||||||
|
@@ -89,11 +89,11 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { socialAuth } from '@/apis'
|
|
||||||
import Background from './components/background/index.vue'
|
import Background from './components/background/index.vue'
|
||||||
import AccountLogin from './components/account/index.vue'
|
import AccountLogin from './components/account/index.vue'
|
||||||
import PhoneLogin from './components/phone/index.vue'
|
import PhoneLogin from './components/phone/index.vue'
|
||||||
import EmailLogin from './components/email/index.vue'
|
import EmailLogin from './components/email/index.vue'
|
||||||
|
import { socialAuth } from '@/apis'
|
||||||
import { useAppStore } from '@/stores'
|
import { useAppStore } from '@/stores'
|
||||||
import { useDevice } from '@/hooks'
|
import { useDevice } from '@/hooks'
|
||||||
|
|
||||||
|
@@ -5,9 +5,9 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { bindSocialAccount } from '@/apis'
|
|
||||||
import { Message } from '@arco-design/web-vue'
|
import { Message } from '@arco-design/web-vue'
|
||||||
import { useRoute, useRouter } from 'vue-router'
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
|
import { bindSocialAccount } from '@/apis'
|
||||||
import { isLogin } from '@/utils/auth'
|
import { isLogin } from '@/utils/auth'
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user