refactor: ⬆️升级依赖
This commit is contained in:
@@ -1,3 +0,0 @@
|
||||
/*.json
|
||||
/*.js
|
||||
dist
|
||||
70
.eslintrc.js
70
.eslintrc.js
@@ -1,70 +0,0 @@
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const path = require('path');
|
||||
|
||||
module.exports = {
|
||||
root: true,
|
||||
parser: 'vue-eslint-parser',
|
||||
parserOptions: {
|
||||
// Parser that checks the content of the <script> tag
|
||||
parser: '@typescript-eslint/parser',
|
||||
sourceType: 'module',
|
||||
ecmaVersion: 2020,
|
||||
ecmaFeatures: {
|
||||
jsx: true,
|
||||
},
|
||||
},
|
||||
env: {
|
||||
'browser': true,
|
||||
'node': true,
|
||||
'vue/setup-compiler-macros': true,
|
||||
},
|
||||
plugins: ['@typescript-eslint'],
|
||||
extends: [
|
||||
// Airbnb JavaScript Style Guide https://github.com/airbnb/javascript
|
||||
'airbnb-base',
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
'plugin:import/recommended',
|
||||
'plugin:import/typescript',
|
||||
'plugin:vue/vue3-recommended',
|
||||
'plugin:prettier/recommended',
|
||||
],
|
||||
settings: {
|
||||
'import/resolver': {
|
||||
typescript: {
|
||||
project: path.resolve(__dirname, './tsconfig.json'),
|
||||
},
|
||||
},
|
||||
},
|
||||
rules: {
|
||||
'prettier/prettier': 1,
|
||||
// Vue: Recommended rules to be closed or modify
|
||||
'vue/require-default-prop': 0,
|
||||
'vue/singleline-html-element-content-newline': 0,
|
||||
'vue/max-attributes-per-line': 0,
|
||||
// Vue: Add extra rules
|
||||
'vue/custom-event-name-casing': [2, 'camelCase'],
|
||||
'vue/no-v-text': 1,
|
||||
'vue/padding-line-between-blocks': 1,
|
||||
'vue/require-direct-export': 1,
|
||||
'vue/multi-word-component-names': 0,
|
||||
// Allow @ts-ignore comment
|
||||
'@typescript-eslint/ban-ts-comment': 0,
|
||||
'@typescript-eslint/no-unused-vars': 1,
|
||||
'@typescript-eslint/no-empty-function': 1,
|
||||
'@typescript-eslint/no-explicit-any': 0,
|
||||
'import/extensions': [
|
||||
2,
|
||||
'ignorePackages',
|
||||
{
|
||||
js: 'never',
|
||||
jsx: 'never',
|
||||
ts: 'never',
|
||||
tsx: 'never',
|
||||
},
|
||||
],
|
||||
'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0,
|
||||
'no-param-reassign': 0,
|
||||
'prefer-regex-literals': 0,
|
||||
'import/no-extraneous-dependencies': 0,
|
||||
},
|
||||
};
|
||||
@@ -1,9 +1,23 @@
|
||||
module.exports = {
|
||||
/** @type {import("prettier").Config} */
|
||||
export default {
|
||||
// 使用 2 个空格缩进
|
||||
tabWidth: 2,
|
||||
// 行尾需要有分号
|
||||
semi: true,
|
||||
// 一行最多 80 个字符
|
||||
printWidth: 80,
|
||||
// 使用单引号
|
||||
singleQuote: true,
|
||||
// 对象的 key 是否有引号格式保持一致
|
||||
quoteProps: 'consistent',
|
||||
htmlWhitespaceSensitivity: 'strict',
|
||||
vueIndentScriptAndStyle: true,
|
||||
// html 空格敏感规则
|
||||
htmlWhitespaceSensitivity: 'ignore',
|
||||
// 不缩进 vue 文件中的 script 和 style
|
||||
vueIndentScriptAndStyle: false,
|
||||
// 多行时不强制尾随逗号
|
||||
trailingComma: 'none',
|
||||
// 箭头函数参数括号
|
||||
arrowParens: 'avoid',
|
||||
// jsx 中使用单引号
|
||||
jsxSingleQuote: true
|
||||
};
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
module.exports = {
|
||||
extends: [
|
||||
'stylelint-config-standard',
|
||||
'stylelint-config-rational-order',
|
||||
'stylelint-config-prettier',
|
||||
'stylelint-config-recommended-vue',
|
||||
],
|
||||
defaultSeverity: 'warning',
|
||||
plugins: ['stylelint-order'],
|
||||
rules: {
|
||||
'at-rule-no-unknown': [
|
||||
true,
|
||||
{
|
||||
ignoreAtRules: ['plugin'],
|
||||
},
|
||||
],
|
||||
'rule-empty-line-before': [
|
||||
'always',
|
||||
{
|
||||
except: ['after-single-line-comment', 'first-nested'],
|
||||
},
|
||||
],
|
||||
'selector-pseudo-class-no-unknown': [
|
||||
true,
|
||||
{
|
||||
ignorePseudoClasses: ['deep'],
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
@@ -1,3 +0,0 @@
|
||||
module.exports = {
|
||||
plugins: ['@vue/babel-plugin-jsx'],
|
||||
};
|
||||
@@ -1,3 +1,32 @@
|
||||
module.exports = {
|
||||
/** @type {import("@commitlint/types").UserConfig} */
|
||||
export default {
|
||||
extends: ['@commitlint/config-conventional'],
|
||||
rules: {
|
||||
'body-leading-blank': [2, 'always'],
|
||||
'footer-leading-blank': [1, 'always'],
|
||||
'header-max-length': [2, 'always', 108],
|
||||
'subject-empty': [2, 'never'],
|
||||
'type-empty': [2, 'never'],
|
||||
'type-enum': [
|
||||
2,
|
||||
'always',
|
||||
[
|
||||
'feat',
|
||||
'fix',
|
||||
'perf',
|
||||
'style',
|
||||
'docs',
|
||||
'test',
|
||||
'refactor',
|
||||
'build',
|
||||
'ci',
|
||||
'chore',
|
||||
'revert',
|
||||
'enhance',
|
||||
'workflow',
|
||||
'types',
|
||||
'release'
|
||||
]
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
@@ -13,7 +13,7 @@ export default function configArcoResolverPlugin() {
|
||||
const arcoResolverPlugin = Components({
|
||||
dirs: [], // Avoid parsing src/components. 避免解析到src/components
|
||||
deep: false,
|
||||
resolvers: [ArcoResolver()],
|
||||
resolvers: [ArcoResolver()]
|
||||
});
|
||||
return arcoResolverPlugin;
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ export default function configCompressPlugin(
|
||||
plugins.push(
|
||||
compressPlugin({
|
||||
ext: '.gz',
|
||||
deleteOriginFile,
|
||||
deleteOriginFile
|
||||
})
|
||||
);
|
||||
}
|
||||
@@ -26,7 +26,7 @@ export default function configCompressPlugin(
|
||||
compressPlugin({
|
||||
ext: '.br',
|
||||
algorithm: 'brotliCompress',
|
||||
deleteOriginFile,
|
||||
deleteOriginFile
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@@ -9,29 +9,29 @@ export default function configImageminPlugin() {
|
||||
const imageminPlugin = viteImagemin({
|
||||
gifsicle: {
|
||||
optimizationLevel: 7,
|
||||
interlaced: false,
|
||||
interlaced: false
|
||||
},
|
||||
optipng: {
|
||||
optimizationLevel: 7,
|
||||
optimizationLevel: 7
|
||||
},
|
||||
mozjpeg: {
|
||||
quality: 20,
|
||||
quality: 20
|
||||
},
|
||||
pngquant: {
|
||||
quality: [0.8, 0.9],
|
||||
speed: 4,
|
||||
speed: 4
|
||||
},
|
||||
svgo: {
|
||||
plugins: [
|
||||
{
|
||||
name: 'removeViewBox',
|
||||
name: 'removeViewBox'
|
||||
},
|
||||
{
|
||||
name: 'removeEmptyAttrs',
|
||||
active: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
active: false
|
||||
}
|
||||
]
|
||||
}
|
||||
});
|
||||
return imageminPlugin;
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ export default function configVisualizerPlugin() {
|
||||
filename: './node_modules/.cache/visualizer/stats.html',
|
||||
open: true,
|
||||
gzipSize: true,
|
||||
brotliSize: true,
|
||||
brotliSize: true
|
||||
});
|
||||
}
|
||||
return [];
|
||||
|
||||
@@ -4,8 +4,10 @@ import vue from '@vitejs/plugin-vue';
|
||||
import vueJsx from '@vitejs/plugin-vue-jsx';
|
||||
import svgLoader from 'vite-svg-loader';
|
||||
import configArcoStyleImportPlugin from './plugin/arcoStyleImport';
|
||||
import { type UserConfigExport, type ConfigEnv } from 'vite';
|
||||
|
||||
export default defineConfig({
|
||||
export default ({ mode }: ConfigEnv): UserConfigExport => {
|
||||
return {
|
||||
plugins: [
|
||||
vue(),
|
||||
vueJsx(),
|
||||
@@ -32,6 +34,10 @@ export default defineConfig({
|
||||
define: {
|
||||
'process.env': {},
|
||||
},
|
||||
optimizeDeps: {
|
||||
include: ['mitt', 'dayjs', 'axios', 'pinia', '@vueuse/core'],
|
||||
exclude: ['@iconify-icons/lets-icons']
|
||||
},
|
||||
css: {
|
||||
preprocessorOptions: {
|
||||
less: {
|
||||
@@ -44,4 +50,5 @@ export default defineConfig({
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
19
config/vite.config.dev.mts
Normal file
19
config/vite.config.dev.mts
Normal file
@@ -0,0 +1,19 @@
|
||||
import baseConfig from './vite.config.base.mts';
|
||||
import { type UserConfigExport, type ConfigEnv } from 'vite';
|
||||
|
||||
export default ({ mode }: ConfigEnv): UserConfigExport => {
|
||||
return {
|
||||
mode: 'development',
|
||||
server: {
|
||||
open: true,
|
||||
fs: {
|
||||
strict: true,
|
||||
},
|
||||
warmup: {
|
||||
// 预热的客户端文件:首页、views、 components
|
||||
clientFiles: ['./index.html', './src/{views,components}/*']
|
||||
}
|
||||
},
|
||||
...baseConfig({mode} as ConfigEnv)
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
import { mergeConfig } from 'vite';
|
||||
import eslint from 'vite-plugin-eslint';
|
||||
import baseConfig from './vite.config.base';
|
||||
|
||||
export default mergeConfig(
|
||||
{
|
||||
mode: 'development',
|
||||
server: {
|
||||
open: true,
|
||||
fs: {
|
||||
strict: true,
|
||||
},
|
||||
},
|
||||
plugins: [
|
||||
eslint({
|
||||
cache: false,
|
||||
include: ['src/**/*.ts', 'src/**/*.tsx', 'src/**/*.vue'],
|
||||
exclude: ['node_modules'],
|
||||
}),
|
||||
],
|
||||
},
|
||||
baseConfig
|
||||
);
|
||||
@@ -1,12 +1,13 @@
|
||||
import { mergeConfig } from 'vite';
|
||||
import baseConfig from './vite.config.base';
|
||||
import baseConfig from './vite.config.base.mts';
|
||||
import configCompressPlugin from './plugin/compress';
|
||||
import configVisualizerPlugin from './plugin/visualizer';
|
||||
import configArcoResolverPlugin from './plugin/arcoResolver';
|
||||
import configImageminPlugin from './plugin/imagemin';
|
||||
import { type UserConfigExport, type ConfigEnv } from 'vite';
|
||||
|
||||
export default mergeConfig(
|
||||
{
|
||||
|
||||
export default ({ mode }: ConfigEnv): UserConfigExport => {
|
||||
return {
|
||||
mode: 'production',
|
||||
plugins: [
|
||||
configCompressPlugin('gzip'),
|
||||
@@ -15,8 +16,15 @@ export default mergeConfig(
|
||||
configImageminPlugin(),
|
||||
],
|
||||
build: {
|
||||
// 浏览器兼容目标
|
||||
target: 'es2015',
|
||||
// 是否生成 source map 文件
|
||||
sourcemap: false,
|
||||
rollupOptions: {
|
||||
output: {
|
||||
entryFileNames: 'static/js/[name]-[hash].js',
|
||||
chunkFileNames: 'static/js/[name]-[hash].js',
|
||||
assetFileNames: 'static/[ext]/[name]-[hash].[ext]',
|
||||
manualChunks: {
|
||||
arco: ['@arco-design/web-vue'],
|
||||
chart: ['echarts', 'vue-echarts'],
|
||||
@@ -26,6 +34,6 @@ export default mergeConfig(
|
||||
},
|
||||
chunkSizeWarningLimit: 2000,
|
||||
},
|
||||
},
|
||||
baseConfig
|
||||
);
|
||||
...baseConfig({mode} as ConfigEnv)
|
||||
}
|
||||
}
|
||||
154
eslint.config.js
Normal file
154
eslint.config.js
Normal file
@@ -0,0 +1,154 @@
|
||||
import js from '@eslint/js';
|
||||
import pluginVue from 'eslint-plugin-vue';
|
||||
import * as parserVue from 'vue-eslint-parser';
|
||||
import configPrettier from 'eslint-config-prettier';
|
||||
import pluginPrettier from 'eslint-plugin-prettier';
|
||||
import { defineFlatConfig } from 'eslint-define-config';
|
||||
import * as parserTypeScript from '@typescript-eslint/parser';
|
||||
import pluginTypeScript from '@typescript-eslint/eslint-plugin';
|
||||
|
||||
export default defineFlatConfig([
|
||||
{
|
||||
...js.configs.recommended,
|
||||
ignores: ['**/.*', '*.d.ts', 'public/*', 'dist/*', 'src/assets/**'],
|
||||
languageOptions: {
|
||||
globals: {}
|
||||
},
|
||||
plugins: {
|
||||
prettier: pluginPrettier
|
||||
},
|
||||
rules: {
|
||||
...configPrettier.rules,
|
||||
...pluginPrettier.configs.recommended.rules,
|
||||
// 禁止使用 var
|
||||
'no-var': 'error',
|
||||
// 禁止使用 console
|
||||
'no-console': 'off',
|
||||
// 禁止使用 debugger
|
||||
'no-debugger': 'off',
|
||||
// 禁止不必要的转义字符
|
||||
'no-useless-escape': 'off',
|
||||
// 禁止出现未使用过的变量,忽略 _ 开头的参数和变量
|
||||
'no-unused-vars': [
|
||||
'warn',
|
||||
{
|
||||
argsIgnorePattern: '^_',
|
||||
varsIgnorePattern: '^_'
|
||||
}
|
||||
],
|
||||
'prettier/prettier': [
|
||||
'error',
|
||||
{
|
||||
endOfLine: 'auto'
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
files: ['**/*.?([cm])ts', '**/*.?([cm])tsx'],
|
||||
languageOptions: {
|
||||
parser: parserTypeScript,
|
||||
parserOptions: {
|
||||
sourceType: 'module'
|
||||
}
|
||||
},
|
||||
plugins: {
|
||||
'@typescript-eslint': pluginTypeScript
|
||||
},
|
||||
rules: {
|
||||
...pluginTypeScript.configs.strict.rules,
|
||||
'@typescript-eslint/ban-types': 'off',
|
||||
// 检查并报错重复声明的变量
|
||||
'@typescript-eslint/no-redeclare': 'error',
|
||||
// 关闭对 TypeScript 注释的限制
|
||||
'@typescript-eslint/ban-ts-comment': 'off',
|
||||
// 允许使用显式的 `any` 类型
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
// 建议使用 `as const` 而不是 `const` 断言类型
|
||||
'@typescript-eslint/prefer-as-const': 'warn',
|
||||
// 允许使用非空断言 `!`
|
||||
'@typescript-eslint/no-non-null-assertion': 'off',
|
||||
// 强制导入类型时不产生副作用
|
||||
'@typescript-eslint/no-import-type-side-effects': 'error',
|
||||
// 关闭对显式指定导出函数和类的类型的要求
|
||||
'@typescript-eslint/explicit-module-boundary-types': 'off',
|
||||
// 强制一致的类型导入方式
|
||||
'@typescript-eslint/consistent-type-imports': [
|
||||
'error',
|
||||
{ disallowTypeAnnotations: false, fixStyle: 'inline-type-imports' }
|
||||
],
|
||||
// 建议优先使用字面量枚举成员
|
||||
'@typescript-eslint/prefer-literal-enum-member': [
|
||||
'error',
|
||||
{ allowBitwiseExpressions: true }
|
||||
],
|
||||
'@typescript-eslint/no-unused-vars': [
|
||||
'warn',
|
||||
{
|
||||
argsIgnorePattern: '^_',
|
||||
varsIgnorePattern: '^_'
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
files: ['**/*.?([cm])js'],
|
||||
rules: {
|
||||
'@typescript-eslint/no-require-imports': 'off',
|
||||
'@typescript-eslint/no-var-requires': 'off'
|
||||
}
|
||||
},
|
||||
{
|
||||
files: ['**/*.d.ts'],
|
||||
rules: {
|
||||
'eslint-comments/no-unlimited-disable': 'off',
|
||||
'import/no-duplicates': 'off',
|
||||
'unused-imports/no-unused-vars': 'off'
|
||||
}
|
||||
},
|
||||
{
|
||||
files: ['**/*.vue'],
|
||||
languageOptions: {
|
||||
globals: {},
|
||||
parser: parserVue,
|
||||
parserOptions: {
|
||||
ecmaFeatures: {
|
||||
jsx: true
|
||||
},
|
||||
extraFileExtensions: ['.vue'],
|
||||
parser: '@typescript-eslint/parser',
|
||||
sourceType: 'module'
|
||||
}
|
||||
},
|
||||
plugins: {
|
||||
vue: pluginVue
|
||||
},
|
||||
processor: pluginVue.processors['.vue'],
|
||||
rules: {
|
||||
...pluginVue.configs.base.rules,
|
||||
...pluginVue.configs['vue3-essential'].rules,
|
||||
...pluginVue.configs['vue3-recommended'].rules,
|
||||
// 关闭对于组件 props 需要默认值的检查
|
||||
'vue/require-default-prop': 'off',
|
||||
// 关闭对于单行 HTML 元素内容换行的检查
|
||||
'vue/singleline-html-element-content-newline': 'off',
|
||||
// 关闭对于每行 HTML 属性数量的限制检查
|
||||
'vue/max-attributes-per-line': 'off',
|
||||
// 强制自定义事件的命名为驼峰式命名
|
||||
'vue/custom-event-name-casing': ['error', 'camelCase'],
|
||||
// 显示警告,不建议使用 v-text 指令
|
||||
'vue/no-v-text': 'warn',
|
||||
// 显示警告,建议在 Vue 组件中添加间距行
|
||||
'vue/padding-line-between-blocks': 'warn',
|
||||
// 显示警告,建议直接导出 Vue 组件
|
||||
'vue/require-direct-export': 'warn',
|
||||
// 允许使用单个单词作为组件名
|
||||
'vue/multi-word-component-names': 'off',
|
||||
// 关闭对于 TypeScript 注释的检查
|
||||
'@typescript-eslint/ban-ts-comment': 'off',
|
||||
// 关闭对于显式声明 any 类型的检查
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
'no-unused-vars': 'off'
|
||||
}
|
||||
}
|
||||
]);
|
||||
215
package.json
215
package.json
@@ -1,103 +1,112 @@
|
||||
{
|
||||
"name": "arco-design-pro-vue",
|
||||
"description": "Arco Design Pro for Vue",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"author": "ArcoDesign Team",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"dev": "vite --config ./config/vite.config.dev.ts",
|
||||
"build": "vue-tsc --noEmit && vite build --config ./config/vite.config.prod.ts",
|
||||
"report": "cross-env REPORT=true npm run build",
|
||||
"preview": "npm run build && vite preview --host",
|
||||
"type:check": "vue-tsc --noEmit --skipLibCheck",
|
||||
"lint-staged": "npx lint-staged",
|
||||
"prepare": "husky install"
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.{js,ts,jsx,tsx}": [
|
||||
"prettier --write",
|
||||
"eslint --fix"
|
||||
],
|
||||
"*.vue": [
|
||||
"stylelint --fix",
|
||||
"prettier --write",
|
||||
"eslint --fix"
|
||||
],
|
||||
"*.{less,css}": [
|
||||
"stylelint --fix",
|
||||
"prettier --write"
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"@arco-design/web-vue": "^2.44.7",
|
||||
"@vueuse/core": "^9.3.0",
|
||||
"axios": "^0.24.0",
|
||||
"crypto-js": "^4.2.0",
|
||||
"dayjs": "^1.11.5",
|
||||
"echarts": "^5.4.0",
|
||||
"lodash": "^4.17.21",
|
||||
"mitt": "^3.0.0",
|
||||
"nprogress": "^0.2.0",
|
||||
"pinia": "^2.0.23",
|
||||
"query-string": "^8.0.3",
|
||||
"sortablejs": "^1.15.0",
|
||||
"vue": "^3.2.40",
|
||||
"vue-echarts": "^6.2.3",
|
||||
"vue-router": "^4.0.14"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@arco-plugins/vite-vue": "^1.4.5",
|
||||
"@commitlint/cli": "^17.1.2",
|
||||
"@commitlint/config-conventional": "^17.1.0",
|
||||
"@types/crypto-js": "^4.2.1",
|
||||
"@types/lodash": "^4.14.186",
|
||||
"@types/mockjs": "^1.0.7",
|
||||
"@types/nprogress": "^0.2.0",
|
||||
"@types/sortablejs": "^1.15.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.40.0",
|
||||
"@typescript-eslint/parser": "^5.40.0",
|
||||
"@vitejs/plugin-vue": "^3.1.2",
|
||||
"@vitejs/plugin-vue-jsx": "^2.0.1",
|
||||
"@vue/babel-plugin-jsx": "^1.1.1",
|
||||
"consola": "^2.15.3",
|
||||
"cross-env": "^7.0.3",
|
||||
"eslint": "^8.25.0",
|
||||
"eslint-config-airbnb-base": "^15.0.0",
|
||||
"eslint-config-prettier": "^8.5.0",
|
||||
"eslint-import-resolver-typescript": "^3.5.1",
|
||||
"eslint-plugin-import": "^2.26.0",
|
||||
"eslint-plugin-prettier": "^4.2.1",
|
||||
"eslint-plugin-vue": "^9.6.0",
|
||||
"husky": "^8.0.1",
|
||||
"less": "^4.1.3",
|
||||
"lint-staged": "^13.0.3",
|
||||
"mockjs": "^1.1.0",
|
||||
"postcss-html": "^1.5.0",
|
||||
"prettier": "^2.7.1",
|
||||
"rollup": "^3.9.1",
|
||||
"rollup-plugin-visualizer": "^5.8.2",
|
||||
"stylelint": "^15.3.0",
|
||||
"stylelint-config-prettier": "^9.0.3",
|
||||
"stylelint-config-rational-order": "^0.1.2",
|
||||
"stylelint-config-recommended-vue": "^1.4.0",
|
||||
"stylelint-config-standard": "^29.0.0",
|
||||
"stylelint-order": "^5.0.0",
|
||||
"typescript": "^4.8.4",
|
||||
"unplugin-vue-components": "^0.24.1",
|
||||
"vite": "^3.2.5",
|
||||
"vite-plugin-compression": "^0.5.1",
|
||||
"vite-plugin-eslint": "^1.8.1",
|
||||
"vite-plugin-imagemin": "^0.6.1",
|
||||
"vite-svg-loader": "^3.6.0",
|
||||
"vue-tsc": "^1.0.14"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
},
|
||||
"resolutions": {
|
||||
"bin-wrapper": "npm:bin-wrapper-china",
|
||||
"rollup": "^2.56.3",
|
||||
"gifsicle": "5.2.0"
|
||||
}
|
||||
}
|
||||
{
|
||||
"name": "arco-design-pro-vue",
|
||||
"description": "Arco Design Pro for Vue",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"author": "ArcoDesign Team",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"dev": "vite --config ./config/vite.config.dev.mts",
|
||||
"build": "vue-tsc --noEmit && vite build --config ./config/vite.config.prod.mts",
|
||||
"type:check": "tsc --noEmit && vue-tsc --noEmit --skipLibCheck",
|
||||
"eslint": "eslint --max-warnings 0 \"{src,mock,config}/**/*.{vue,js,ts,tsx}\"",
|
||||
"eslint:fix": "eslint --max-warnings 0 \"{src,mock,config}/**/*.{vue,js,ts,tsx}\" --fix",
|
||||
"stylelint": "stylelint \"**/*.{html,vue,css,less}\"",
|
||||
"stylelint:fix": "stylelint \"**/*.{html,vue,css,less}\" --fix",
|
||||
"prettier": "prettier --check \"src/**/*.{js,ts,json,tsx,css,scss,vue,html,md}\"",
|
||||
"prettier:fix": "prettier --write \"src/**/*.{js,ts,json,tsx,css,scss,vue,html,md}\"",
|
||||
"lint": "pnpm eslint && pnpm stylelint && pnpm prettier",
|
||||
"lint:fix": "pnpm eslint:fix && pnpm stylelint:fix && pnpm prettier:fix",
|
||||
"lint-staged": "npx lint-staged",
|
||||
"clean:cache": "rimraf .eslintcache && rimraf node_modules && pnpm install",
|
||||
"prepare": "husky install",
|
||||
"release": "bumpp"
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.{js,ts,jsx,tsx}": [
|
||||
"prettier --write",
|
||||
"eslint --fix"
|
||||
],
|
||||
"*.vue": [
|
||||
"stylelint --fix",
|
||||
"prettier --write",
|
||||
"eslint --fix"
|
||||
],
|
||||
"*.{less,css}": [
|
||||
"stylelint --fix",
|
||||
"prettier --write"
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"@arco-design/web-vue": "^2.55.3",
|
||||
"@vueuse/core": "^10.11.0",
|
||||
"axios": "^1.7.2",
|
||||
"crypto-js": "^4.2.0",
|
||||
"dayjs": "^1.11.11",
|
||||
"echarts": "^5.5.0",
|
||||
"lodash": "^4.17.21",
|
||||
"mitt": "^3.0.1",
|
||||
"nprogress": "^0.2.0",
|
||||
"pinia": "^2.1.7",
|
||||
"query-string": "^9.0.0",
|
||||
"rollup": "^4.18.0",
|
||||
"sortablejs": "^1.15.2",
|
||||
"vue": "^3.4.29",
|
||||
"vue-echarts": "^6.7.3",
|
||||
"vue-router": "^4.3.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@arco-plugins/vite-vue": "^1.4.5",
|
||||
"@commitlint/cli": "^19.3.0",
|
||||
"@commitlint/config-conventional": "^19.2.2",
|
||||
"@commitlint/types": "^19.0.3",
|
||||
"@eslint/js": "^9.5.0",
|
||||
"@types/crypto-js": "^4.2.2",
|
||||
"@types/lodash": "^4.17.5",
|
||||
"@types/mockjs": "^1.0.10",
|
||||
"@types/nprogress": "^0.2.3",
|
||||
"@types/sortablejs": "^1.15.8",
|
||||
"@typescript-eslint/eslint-plugin": "^7.13.1",
|
||||
"@typescript-eslint/parser": "^7.13.1",
|
||||
"@vitejs/plugin-vue": "^5.0.5",
|
||||
"@vitejs/plugin-vue-jsx": "^4.0.0",
|
||||
"autoprefixer": "^10.4.19",
|
||||
"consola": "^3.2.3",
|
||||
"cross-env": "^7.0.3",
|
||||
"eslint": "^9.5.0",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"eslint-define-config": "^2.1.0",
|
||||
"eslint-plugin-prettier": "^5.1.3",
|
||||
"eslint-plugin-vue": "^9.26.0",
|
||||
"husky": "^9.0.11",
|
||||
"less": "^4.2.0",
|
||||
"lint-staged": "^15.2.7",
|
||||
"mockjs": "^1.1.0",
|
||||
"postcss-html": "^1.7.0",
|
||||
"prettier": "^3.3.2",
|
||||
"rollup-plugin-visualizer": "^5.12.0",
|
||||
"stylelint": "^16.6.1",
|
||||
"stylelint-config-recess-order": "^5.0.1",
|
||||
"stylelint-config-recommended-vue": "^1.5.0",
|
||||
"stylelint-config-standard-less": "^3.0.1",
|
||||
"stylelint-prettier": "^5.0.0",
|
||||
"typescript": "^5.4.5",
|
||||
"unplugin-vue-components": "^0.27.0",
|
||||
"vite": "^5.3.1",
|
||||
"vite-plugin-compression": "^0.5.1",
|
||||
"vite-plugin-imagemin": "^0.6.1",
|
||||
"vite-svg-loader": "^5.1.0",
|
||||
"vue-eslint-parser": "^9.4.3",
|
||||
"vue-tsc": "^2.0.21"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.0.0 || >=20.0.0",
|
||||
"pnpm": ">=8.11.0"
|
||||
},
|
||||
"resolutions": {
|
||||
"bin-wrapper": "npm:bin-wrapper-china",
|
||||
"gifsicle": "5.2.0"
|
||||
},
|
||||
"packageManager": "pnpm@9.1.2+sha512.127dc83b9ea10c32be65d22a8efb4a65fb952e8fefbdfded39bdc3c97efc32d31b48b00420df2c1187ace28c921c902f0cb5a134a4d032b8b5295cbfa2c681e2"
|
||||
}
|
||||
|
||||
6518
pnpm-lock.yaml
generated
6518
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -6,5 +6,5 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import GlobalSetting from '@/components/global-setting/index.vue';
|
||||
import GlobalSetting from '@/components/global-setting/index.vue';
|
||||
</script>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import axios from 'axios';
|
||||
import qs from 'query-string';
|
||||
import {
|
||||
import type {
|
||||
CommonPageParams,
|
||||
CommonPageResult,
|
||||
CommonStrIdParams,
|
||||
CommonStrIdParams
|
||||
} from './common';
|
||||
import { handleDownLoadFile } from './utils';
|
||||
|
||||
@@ -17,7 +17,7 @@ export interface AppleCardRecord extends AppleCardAddRecord {
|
||||
todayRechargeAmount: number;
|
||||
todayRechargeCount: number;
|
||||
todayRechargeDatetime: string;
|
||||
balance: number;
|
||||
balance: number;
|
||||
}
|
||||
|
||||
export interface AppleCardParams
|
||||
@@ -39,9 +39,9 @@ export function queryAppleCardList(params: AppleCardParams) {
|
||||
'/cardInfo/AppleCard/account/getList',
|
||||
{
|
||||
params,
|
||||
paramsSerializer: (obj) => {
|
||||
paramsSerializer: obj => {
|
||||
return qs.stringify(obj);
|
||||
},
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -62,8 +62,8 @@ export function batchUploadAppleCardAPI(data: Blob) {
|
||||
// 上传xlsx
|
||||
return axios.post('/cardInfo/AppleCard/account/batchAdd', data, {
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data',
|
||||
},
|
||||
'Content-Type': 'multipart/form-data'
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ export async function downloadAppleAccountTemplteAPI() {
|
||||
const response = await axios.get<Blob>(
|
||||
'/cardInfo/AppleCard/account/downloadTemplate',
|
||||
{
|
||||
responseType: 'blob',
|
||||
responseType: 'blob'
|
||||
}
|
||||
);
|
||||
handleDownLoadFile(response.data, '苹果账号批量导入模板.xlsx');
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import axios from 'axios';
|
||||
import qs from 'query-string';
|
||||
import { CommonPageParams, CommonPageResult, CommonResult } from './common';
|
||||
import type {
|
||||
CommonPageParams,
|
||||
CommonPageResult,
|
||||
CommonResult
|
||||
} from './common';
|
||||
|
||||
export interface AppleCardRechargeOrderRecord {
|
||||
id: number;
|
||||
@@ -34,9 +38,9 @@ export function queryAppleCardRechargeOrderList(
|
||||
'/cardInfo/appleCard/rechargeOrder/list',
|
||||
{
|
||||
params,
|
||||
paramsSerializer: (obj) => {
|
||||
paramsSerializer: obj => {
|
||||
return qs.stringify(obj);
|
||||
},
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -49,9 +53,9 @@ export function queryAppleCardOperationHistoryList(params: {
|
||||
'/cardInfo/AppleCard/rechargeOrder/getHistoryList',
|
||||
{
|
||||
params,
|
||||
paramsSerializer: (obj) => {
|
||||
paramsSerializer: obj => {
|
||||
return qs.stringify(obj);
|
||||
},
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import axios from 'axios';
|
||||
import type { AxiosRequestConfig, AxiosResponse } from 'axios';
|
||||
import type { AxiosResponse, InternalAxiosRequestConfig } from 'axios';
|
||||
import { Message, Modal } from '@arco-design/web-vue';
|
||||
import { useUserStore } from '@/store';
|
||||
import { getToken } from '@/utils/auth';
|
||||
@@ -16,28 +16,25 @@ if (import.meta.env.VITE_API_BASE_URL) {
|
||||
}
|
||||
|
||||
axios.interceptors.request.use(
|
||||
(config: AxiosRequestConfig) => {
|
||||
(config: InternalAxiosRequestConfig) => {
|
||||
// let each request carry token
|
||||
// this example using the JWT token
|
||||
// Authorization is a custom headers key
|
||||
// please modify it according to the actual situation
|
||||
const token = getToken();
|
||||
if (token) {
|
||||
if (!config.headers) {
|
||||
config.headers = {};
|
||||
}
|
||||
config.headers.Authorization = `Bearer ${token}`;
|
||||
}
|
||||
return config;
|
||||
},
|
||||
(error) => {
|
||||
error => {
|
||||
// do something
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
// add response interceptors
|
||||
axios.interceptors.response.use(
|
||||
(response: AxiosResponse<HttpResponse>) => {
|
||||
(response: AxiosResponse) => {
|
||||
// 二进制文件
|
||||
if (
|
||||
response.headers['content-type'] === 'application/force-download' ||
|
||||
@@ -52,7 +49,7 @@ axios.interceptors.response.use(
|
||||
if (res.code !== 0) {
|
||||
Message.error({
|
||||
content: res.message || 'Error',
|
||||
duration: 5 * 1000,
|
||||
duration: 5 * 1000
|
||||
});
|
||||
// 50008: Illegal token; 50012: Other clients logged in; 50014: Token expired;
|
||||
if (
|
||||
@@ -69,17 +66,17 @@ axios.interceptors.response.use(
|
||||
|
||||
await userStore.logout();
|
||||
window.location.reload();
|
||||
},
|
||||
}
|
||||
});
|
||||
}
|
||||
return Promise.reject(new Error(res.message || 'Error'));
|
||||
}
|
||||
return res;
|
||||
},
|
||||
(error) => {
|
||||
error => {
|
||||
Message.error({
|
||||
content: error.msg || 'Request Error',
|
||||
duration: 5 * 1000,
|
||||
duration: 5 * 1000
|
||||
});
|
||||
return Promise.reject(error);
|
||||
}
|
||||
|
||||
@@ -26,9 +26,9 @@ export interface PolicyListRes {
|
||||
export function queryPolicyList(params: PolicyParams) {
|
||||
return axios.get<PolicyListRes>('/api/list/policy', {
|
||||
params,
|
||||
paramsSerializer: (obj) => {
|
||||
paramsSerializer: obj => {
|
||||
return qs.stringify(obj);
|
||||
},
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import axios from 'axios';
|
||||
import qs from 'query-string';
|
||||
import { CommonNumIdParams, CommonResult } from './common';
|
||||
import type { CommonNumIdParams, CommonResult } from './common';
|
||||
|
||||
export type merchantDeployRate = {
|
||||
factLabel: number; // 淘宝售价
|
||||
@@ -53,9 +53,9 @@ export function getmerchantDeployList(params: { merchantUid: string }) {
|
||||
'/merchant/deploy/getList',
|
||||
{
|
||||
params,
|
||||
paramsSerializer: (obj) => {
|
||||
paramsSerializer: obj => {
|
||||
return qs.stringify(obj);
|
||||
},
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -79,9 +79,9 @@ export function updateMerchantDeploy(data: merchantDeployAddRecordWithID) {
|
||||
export function getMerchantDeployDetail(params: { id: number }) {
|
||||
return axios.get<merchantDeployRecord>('/merchant/deploy/getDetail', {
|
||||
params,
|
||||
paramsSerializer: (obj) => {
|
||||
paramsSerializer: obj => {
|
||||
return qs.stringify(obj);
|
||||
},
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import axios from 'axios';
|
||||
import qs from 'query-string';
|
||||
import {
|
||||
import type {
|
||||
CommonPageParams,
|
||||
CommonPageResult,
|
||||
CommonNumIdParams,
|
||||
CommonNumIdParams
|
||||
} from './common';
|
||||
import { PolicyRecord } from './list';
|
||||
import type { PolicyRecord } from './list';
|
||||
|
||||
export interface MerchantConfigAddRecord {
|
||||
merchantName: string;
|
||||
@@ -52,9 +52,9 @@ export function queryMerchantConfigList(params: MerchantConfigParams) {
|
||||
'/merchant/config/getList',
|
||||
{
|
||||
params,
|
||||
paramsSerializer: (obj) => {
|
||||
paramsSerializer: obj => {
|
||||
return qs.stringify(obj);
|
||||
},
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -62,9 +62,9 @@ export function queryMerchantConfigList(params: MerchantConfigParams) {
|
||||
export function queryMerchant(params: { merchantUid: string }) {
|
||||
return axios.get<MerchantConfigDetailRecord>('/merchant/config/getDetail', {
|
||||
params,
|
||||
paramsSerializer: (obj) => {
|
||||
paramsSerializer: obj => {
|
||||
return qs.stringify(obj);
|
||||
},
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import axios from 'axios';
|
||||
import qs from 'query-string';
|
||||
import { CommonPageParams, CommonPageResult } from './common';
|
||||
import type { CommonPageParams, CommonPageResult } from './common';
|
||||
|
||||
export type OrderSummaryParams = CommonPageParams;
|
||||
|
||||
@@ -21,9 +21,9 @@ export function queryOrderSummaryList(params: OrderSummaryParams) {
|
||||
'/cardInfo/AppleCard/account/getList',
|
||||
{
|
||||
params,
|
||||
paramsSerializer: (obj) => {
|
||||
paramsSerializer: obj => {
|
||||
return qs.stringify(obj);
|
||||
},
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import axios from 'axios';
|
||||
import { CommonResult } from './common';
|
||||
import type { CommonResult } from './common';
|
||||
|
||||
export interface simpleRoad {
|
||||
roadName: string;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import axios from 'axios';
|
||||
import { CommonResult } from './common';
|
||||
import type { CommonResult } from './common';
|
||||
|
||||
export interface simplePoolRoad {
|
||||
roadPoolName: string;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import axios from 'axios';
|
||||
import type { RouteRecordNormalized } from 'vue-router';
|
||||
import { UserState } from '@/store/modules/user/types';
|
||||
import type { UserState } from '@/store/modules/user/types';
|
||||
import { encryptWithBase64 } from '@/utils/encrypt';
|
||||
|
||||
export interface LoginData {
|
||||
@@ -15,7 +15,7 @@ export interface LoginRes {
|
||||
export function login(data: LoginData) {
|
||||
return axios.post<LoginRes>('/user/login', {
|
||||
username: data.username,
|
||||
password: encryptWithBase64(data.password),
|
||||
password: encryptWithBase64(data.password)
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
13344
src/assets/world.json
13344
src/assets/world.json
File diff suppressed because one or more lines are too long
@@ -10,26 +10,28 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { PropType } from 'vue';
|
||||
import { PropType } from 'vue';
|
||||
|
||||
defineProps({
|
||||
items: {
|
||||
type: Array as PropType<string[]>,
|
||||
default() {
|
||||
return [];
|
||||
},
|
||||
},
|
||||
});
|
||||
defineProps({
|
||||
items: {
|
||||
type: Array as PropType<string[]>,
|
||||
default() {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.container-breadcrumb {
|
||||
margin: 16px 0;
|
||||
:deep(.arco-breadcrumb-item) {
|
||||
color: rgb(var(--gray-6));
|
||||
&:last-child {
|
||||
color: rgb(var(--gray-8));
|
||||
}
|
||||
.container-breadcrumb {
|
||||
margin: 16px 0;
|
||||
|
||||
:deep(.arco-breadcrumb-item) {
|
||||
color: rgb(var(--gray-6));
|
||||
|
||||
&:last-child {
|
||||
color: rgb(var(--gray-8));
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -8,40 +8,40 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, nextTick } from 'vue';
|
||||
import VCharts from 'vue-echarts';
|
||||
// import { useAppStore } from '@/store';
|
||||
import { ref, nextTick } from 'vue';
|
||||
import VCharts from 'vue-echarts';
|
||||
// import { useAppStore } from '@/store';
|
||||
|
||||
defineProps({
|
||||
options: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {};
|
||||
},
|
||||
},
|
||||
autoResize: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
width: {
|
||||
type: String,
|
||||
default: '100%',
|
||||
},
|
||||
height: {
|
||||
type: String,
|
||||
default: '100%',
|
||||
},
|
||||
});
|
||||
// const appStore = useAppStore();
|
||||
// const theme = computed(() => {
|
||||
// if (appStore.theme === 'dark') return 'dark';
|
||||
// return '';
|
||||
// });
|
||||
const renderChart = ref(false);
|
||||
// wait container expand
|
||||
nextTick(() => {
|
||||
renderChart.value = true;
|
||||
});
|
||||
defineProps({
|
||||
options: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {};
|
||||
}
|
||||
},
|
||||
autoResize: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
width: {
|
||||
type: String,
|
||||
default: '100%'
|
||||
},
|
||||
height: {
|
||||
type: String,
|
||||
default: '100%'
|
||||
}
|
||||
});
|
||||
// const appStore = useAppStore();
|
||||
// const theme = computed(() => {
|
||||
// if (appStore.theme === 'dark') return 'dark';
|
||||
// return '';
|
||||
// });
|
||||
const renderChart = ref(false);
|
||||
// wait container expand
|
||||
nextTick(() => {
|
||||
renderChart.value = true;
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="less"></style>
|
||||
|
||||
@@ -5,12 +5,12 @@
|
||||
<script lang="ts" setup></script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.footer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 40px;
|
||||
color: var(--color-text-2);
|
||||
text-align: center;
|
||||
}
|
||||
.footer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 40px;
|
||||
color: var(--color-text-2);
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -14,66 +14,66 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { PropType } from 'vue';
|
||||
import { useAppStore } from '@/store';
|
||||
import FormWrapper from './form-wrapper.vue';
|
||||
import { PropType } from 'vue';
|
||||
import { useAppStore } from '@/store';
|
||||
import FormWrapper from './form-wrapper.vue';
|
||||
|
||||
interface OptionsProps {
|
||||
name: string;
|
||||
key: string;
|
||||
type?: string;
|
||||
defaultVal?: boolean | string | number;
|
||||
interface OptionsProps {
|
||||
name: string;
|
||||
key: string;
|
||||
type?: string;
|
||||
defaultVal?: boolean | string | number;
|
||||
}
|
||||
defineProps({
|
||||
title: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
options: {
|
||||
type: Array as PropType<OptionsProps[]>,
|
||||
default() {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
defineProps({
|
||||
title: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
options: {
|
||||
type: Array as PropType<OptionsProps[]>,
|
||||
default() {
|
||||
return [];
|
||||
},
|
||||
},
|
||||
});
|
||||
const appStore = useAppStore();
|
||||
const handleChange = async ({
|
||||
key,
|
||||
value,
|
||||
}: {
|
||||
key: string;
|
||||
value: unknown;
|
||||
}) => {
|
||||
if (key === 'colorWeak') {
|
||||
document.body.style.filter = value ? 'invert(80%)' : 'none';
|
||||
}
|
||||
if (key === 'menuFromServer' && value) {
|
||||
await appStore.fetchServerMenuConfig();
|
||||
}
|
||||
if (key === 'topMenu') {
|
||||
appStore.updateSettings({
|
||||
menuCollapse: false,
|
||||
});
|
||||
}
|
||||
appStore.updateSettings({ [key]: value });
|
||||
};
|
||||
});
|
||||
const appStore = useAppStore();
|
||||
const handleChange = async ({
|
||||
key,
|
||||
value
|
||||
}: {
|
||||
key: string;
|
||||
value: unknown;
|
||||
}) => {
|
||||
if (key === 'colorWeak') {
|
||||
document.body.style.filter = value ? 'invert(80%)' : 'none';
|
||||
}
|
||||
if (key === 'menuFromServer' && value) {
|
||||
await appStore.fetchServerMenuConfig();
|
||||
}
|
||||
if (key === 'topMenu') {
|
||||
appStore.updateSettings({
|
||||
menuCollapse: false
|
||||
});
|
||||
}
|
||||
appStore.updateSettings({ [key]: value });
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.block {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
.block {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.title {
|
||||
margin: 10px 0;
|
||||
padding: 0;
|
||||
font-size: 14px;
|
||||
}
|
||||
.title {
|
||||
padding: 0;
|
||||
margin: 10px 0;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.switch-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
height: 32px;
|
||||
}
|
||||
.switch-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
height: 32px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -3,37 +3,37 @@
|
||||
v-if="type === 'number'"
|
||||
:style="{ width: '80px' }"
|
||||
size="small"
|
||||
:default-value="(defaultValue as number)"
|
||||
:default-value="defaultValue as number"
|
||||
@change="handleChange"
|
||||
/>
|
||||
<a-switch
|
||||
v-else
|
||||
:default-checked="(defaultValue as boolean)"
|
||||
:default-checked="defaultValue as boolean"
|
||||
size="small"
|
||||
@change="handleChange"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
const props = defineProps({
|
||||
type: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
defaultValue: {
|
||||
type: [String, Boolean, Number],
|
||||
default: '',
|
||||
},
|
||||
const props = defineProps({
|
||||
type: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
defaultValue: {
|
||||
type: [String, Boolean, Number],
|
||||
default: ''
|
||||
}
|
||||
});
|
||||
const emit = defineEmits(['inputChange']);
|
||||
const handleChange = (value: unknown) => {
|
||||
emit('inputChange', {
|
||||
value,
|
||||
key: props.name
|
||||
});
|
||||
const emit = defineEmits(['inputChange']);
|
||||
const handleChange = (value: unknown) => {
|
||||
emit('inputChange', {
|
||||
value,
|
||||
key: props.name,
|
||||
});
|
||||
};
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
@ok="copySettings"
|
||||
@cancel="cancel"
|
||||
>
|
||||
<template #title> 设置 </template>
|
||||
<template #title>设置</template>
|
||||
<Block :options="contentOpts" :title="'settings.content'" />
|
||||
<Block :options="othersOpts" :title="'settings.otherSettings'" />
|
||||
<a-alert>{{ 'settings.alertContent' }}</a-alert>
|
||||
@@ -23,74 +23,74 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed } from 'vue';
|
||||
import { Message } from '@arco-design/web-vue';
|
||||
import { useClipboard } from '@vueuse/core';
|
||||
import { useAppStore } from '@/store';
|
||||
import Block from './block.vue';
|
||||
import { computed } from 'vue';
|
||||
import { Message } from '@arco-design/web-vue';
|
||||
import { useClipboard } from '@vueuse/core';
|
||||
import { useAppStore } from '@/store';
|
||||
import Block from './block.vue';
|
||||
|
||||
const emit = defineEmits(['cancel']);
|
||||
const emit = defineEmits(['cancel']);
|
||||
|
||||
const appStore = useAppStore();
|
||||
const { copy } = useClipboard();
|
||||
const visible = computed(() => appStore.globalSettings);
|
||||
const contentOpts = computed(() => [
|
||||
{ name: 'settings.navbar', key: 'navbar', defaultVal: appStore.navbar },
|
||||
{
|
||||
name: 'settings.menu',
|
||||
key: 'menu',
|
||||
defaultVal: appStore.menu,
|
||||
},
|
||||
{
|
||||
name: 'settings.topMenu',
|
||||
key: 'topMenu',
|
||||
defaultVal: appStore.topMenu,
|
||||
},
|
||||
{ name: 'settings.footer', key: 'footer', defaultVal: appStore.footer },
|
||||
{ name: 'settings.tabBar', key: 'tabBar', defaultVal: appStore.tabBar },
|
||||
{
|
||||
name: 'settings.menuFromServer',
|
||||
key: 'menuFromServer',
|
||||
defaultVal: appStore.menuFromServer,
|
||||
},
|
||||
{
|
||||
name: 'settings.menuWidth',
|
||||
key: 'menuWidth',
|
||||
defaultVal: appStore.menuWidth,
|
||||
type: 'number',
|
||||
},
|
||||
]);
|
||||
const othersOpts = computed(() => [
|
||||
{
|
||||
name: 'settings.colorWeak',
|
||||
key: 'colorWeak',
|
||||
defaultVal: appStore.colorWeak,
|
||||
},
|
||||
]);
|
||||
const appStore = useAppStore();
|
||||
const { copy } = useClipboard();
|
||||
const visible = computed(() => appStore.globalSettings);
|
||||
const contentOpts = computed(() => [
|
||||
{ name: 'settings.navbar', key: 'navbar', defaultVal: appStore.navbar },
|
||||
{
|
||||
name: 'settings.menu',
|
||||
key: 'menu',
|
||||
defaultVal: appStore.menu
|
||||
},
|
||||
{
|
||||
name: 'settings.topMenu',
|
||||
key: 'topMenu',
|
||||
defaultVal: appStore.topMenu
|
||||
},
|
||||
{ name: 'settings.footer', key: 'footer', defaultVal: appStore.footer },
|
||||
{ name: 'settings.tabBar', key: 'tabBar', defaultVal: appStore.tabBar },
|
||||
{
|
||||
name: 'settings.menuFromServer',
|
||||
key: 'menuFromServer',
|
||||
defaultVal: appStore.menuFromServer
|
||||
},
|
||||
{
|
||||
name: 'settings.menuWidth',
|
||||
key: 'menuWidth',
|
||||
defaultVal: appStore.menuWidth,
|
||||
type: 'number'
|
||||
}
|
||||
]);
|
||||
const othersOpts = computed(() => [
|
||||
{
|
||||
name: 'settings.colorWeak',
|
||||
key: 'colorWeak',
|
||||
defaultVal: appStore.colorWeak
|
||||
}
|
||||
]);
|
||||
|
||||
const cancel = () => {
|
||||
appStore.updateSettings({ globalSettings: false });
|
||||
emit('cancel');
|
||||
};
|
||||
const copySettings = async () => {
|
||||
const text = JSON.stringify(appStore.$state, null, 2);
|
||||
await copy(text);
|
||||
Message.success('复制成功');
|
||||
};
|
||||
const setVisible = () => {
|
||||
appStore.updateSettings({ globalSettings: true });
|
||||
};
|
||||
const cancel = () => {
|
||||
appStore.updateSettings({ globalSettings: false });
|
||||
emit('cancel');
|
||||
};
|
||||
const copySettings = async () => {
|
||||
const text = JSON.stringify(appStore.$state, null, 2);
|
||||
await copy(text);
|
||||
Message.success('复制成功');
|
||||
};
|
||||
const setVisible = () => {
|
||||
appStore.updateSettings({ globalSettings: true });
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.fixed-settings {
|
||||
position: fixed;
|
||||
top: 280px;
|
||||
right: 0;
|
||||
.fixed-settings {
|
||||
position: fixed;
|
||||
top: 280px;
|
||||
right: 0;
|
||||
|
||||
svg {
|
||||
font-size: 18px;
|
||||
vertical-align: -4px;
|
||||
}
|
||||
svg {
|
||||
font-size: 18px;
|
||||
vertical-align: -4px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { App } from 'vue';
|
||||
import type { App } from 'vue';
|
||||
import { use } from 'echarts/core';
|
||||
import { CanvasRenderer } from 'echarts/renderers';
|
||||
import { BarChart, LineChart, PieChart, RadarChart } from 'echarts/charts';
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
TooltipComponent,
|
||||
LegendComponent,
|
||||
DataZoomComponent,
|
||||
GraphicComponent,
|
||||
GraphicComponent
|
||||
} from 'echarts/components';
|
||||
import Chart from './chart/index.vue';
|
||||
import Breadcrumb from './breadcrumb/index.vue';
|
||||
@@ -24,12 +24,12 @@ use([
|
||||
TooltipComponent,
|
||||
LegendComponent,
|
||||
DataZoomComponent,
|
||||
GraphicComponent,
|
||||
GraphicComponent
|
||||
]);
|
||||
|
||||
export default {
|
||||
install(Vue: App) {
|
||||
Vue.component('Chart', Chart);
|
||||
Vue.component('Breadcrumb', Breadcrumb);
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,164 +1,164 @@
|
||||
<script lang="tsx">
|
||||
import { defineComponent, ref, h, compile, computed } from 'vue';
|
||||
import { useRoute, useRouter, RouteRecordRaw } from 'vue-router';
|
||||
import type { RouteMeta } from 'vue-router';
|
||||
import { useAppStore } from '@/store';
|
||||
import { listenerRouteChange } from '@/utils/route-listener';
|
||||
import { openWindow, regexUrl } from '@/utils';
|
||||
import useMenuTree from './use-menu-tree';
|
||||
import { defineComponent, ref, h, compile, computed } from 'vue';
|
||||
import { useRoute, useRouter, RouteRecordRaw } from 'vue-router';
|
||||
import type { RouteMeta } from 'vue-router';
|
||||
import { useAppStore } from '@/store';
|
||||
import { listenerRouteChange } from '@/utils/route-listener';
|
||||
import { openWindow, regexUrl } from '@/utils';
|
||||
import useMenuTree from './use-menu-tree';
|
||||
|
||||
export default defineComponent({
|
||||
emit: ['collapse'],
|
||||
setup() {
|
||||
const appStore = useAppStore();
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
const { menuTree } = useMenuTree();
|
||||
const collapsed = computed({
|
||||
get() {
|
||||
if (appStore.device === 'desktop') return appStore.menuCollapse;
|
||||
return false;
|
||||
},
|
||||
set(value: boolean) {
|
||||
appStore.updateSettings({ menuCollapse: value });
|
||||
},
|
||||
export default defineComponent({
|
||||
emit: ['collapse'],
|
||||
setup() {
|
||||
const appStore = useAppStore();
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
const { menuTree } = useMenuTree();
|
||||
const collapsed = computed({
|
||||
get() {
|
||||
if (appStore.device === 'desktop') return appStore.menuCollapse;
|
||||
return false;
|
||||
},
|
||||
set(value: boolean) {
|
||||
appStore.updateSettings({ menuCollapse: value });
|
||||
}
|
||||
});
|
||||
|
||||
const topMenu = computed(() => appStore.topMenu);
|
||||
const openKeys = ref<string[]>([]);
|
||||
const selectedKey = ref<string[]>([]);
|
||||
|
||||
const goto = (item: RouteRecordRaw) => {
|
||||
// Open external link
|
||||
if (regexUrl.test(item.path)) {
|
||||
openWindow(item.path);
|
||||
selectedKey.value = [item.name as string];
|
||||
return;
|
||||
}
|
||||
// Eliminate external link side effects
|
||||
const { hideInMenu, activeMenu } = item.meta as RouteMeta;
|
||||
if (route.name === item.name && !hideInMenu && !activeMenu) {
|
||||
selectedKey.value = [item.name as string];
|
||||
return;
|
||||
}
|
||||
// Trigger router change
|
||||
router.push({
|
||||
name: item.name
|
||||
});
|
||||
|
||||
const topMenu = computed(() => appStore.topMenu);
|
||||
const openKeys = ref<string[]>([]);
|
||||
const selectedKey = ref<string[]>([]);
|
||||
|
||||
const goto = (item: RouteRecordRaw) => {
|
||||
// Open external link
|
||||
if (regexUrl.test(item.path)) {
|
||||
openWindow(item.path);
|
||||
selectedKey.value = [item.name as string];
|
||||
};
|
||||
const findMenuOpenKeys = (target: string) => {
|
||||
const result: string[] = [];
|
||||
let isFind = false;
|
||||
const backtrack = (item: RouteRecordRaw, keys: string[]) => {
|
||||
if (item.name === target) {
|
||||
isFind = true;
|
||||
result.push(...keys);
|
||||
return;
|
||||
}
|
||||
// Eliminate external link side effects
|
||||
const { hideInMenu, activeMenu } = item.meta as RouteMeta;
|
||||
if (route.name === item.name && !hideInMenu && !activeMenu) {
|
||||
selectedKey.value = [item.name as string];
|
||||
return;
|
||||
if (item.children?.length) {
|
||||
item.children.forEach(el => {
|
||||
backtrack(el, [...keys, el.name as string]);
|
||||
});
|
||||
}
|
||||
// Trigger router change
|
||||
router.push({
|
||||
name: item.name,
|
||||
});
|
||||
};
|
||||
const findMenuOpenKeys = (target: string) => {
|
||||
const result: string[] = [];
|
||||
let isFind = false;
|
||||
const backtrack = (item: RouteRecordRaw, keys: string[]) => {
|
||||
if (item.name === target) {
|
||||
isFind = true;
|
||||
result.push(...keys);
|
||||
return;
|
||||
}
|
||||
if (item.children?.length) {
|
||||
item.children.forEach((el) => {
|
||||
backtrack(el, [...keys, el.name as string]);
|
||||
});
|
||||
}
|
||||
};
|
||||
menuTree.value.forEach((el: RouteRecordRaw) => {
|
||||
if (isFind) return; // Performance optimization
|
||||
backtrack(el, [el.name as string]);
|
||||
});
|
||||
return result;
|
||||
};
|
||||
listenerRouteChange((newRoute) => {
|
||||
const { requiresAuth, activeMenu, hideInMenu } = newRoute.meta;
|
||||
if (requiresAuth && (!hideInMenu || activeMenu)) {
|
||||
const menuOpenKeys = findMenuOpenKeys(
|
||||
(activeMenu || newRoute.name) as string
|
||||
);
|
||||
menuTree.value.forEach((el: RouteRecordRaw) => {
|
||||
if (isFind) return; // Performance optimization
|
||||
backtrack(el, [el.name as string]);
|
||||
});
|
||||
return result;
|
||||
};
|
||||
listenerRouteChange(newRoute => {
|
||||
const { requiresAuth, activeMenu, hideInMenu } = newRoute.meta;
|
||||
if (requiresAuth && (!hideInMenu || activeMenu)) {
|
||||
const menuOpenKeys = findMenuOpenKeys(
|
||||
(activeMenu || newRoute.name) as string
|
||||
);
|
||||
|
||||
const keySet = new Set([...menuOpenKeys, ...openKeys.value]);
|
||||
openKeys.value = [...keySet];
|
||||
const keySet = new Set([...menuOpenKeys, ...openKeys.value]);
|
||||
openKeys.value = [...keySet];
|
||||
|
||||
selectedKey.value = [
|
||||
activeMenu || menuOpenKeys[menuOpenKeys.length - 1],
|
||||
];
|
||||
selectedKey.value = [
|
||||
activeMenu || menuOpenKeys[menuOpenKeys.length - 1]
|
||||
];
|
||||
}
|
||||
}, true);
|
||||
const setCollapse = (val: boolean) => {
|
||||
if (appStore.device === 'desktop')
|
||||
appStore.updateSettings({ menuCollapse: val });
|
||||
};
|
||||
|
||||
const renderSubMenu = () => {
|
||||
function travel(_route: RouteRecordRaw[], nodes = []) {
|
||||
if (_route) {
|
||||
_route.forEach(element => {
|
||||
// This is demo, modify nodes as needed
|
||||
const icon = element?.meta?.icon
|
||||
? () => h(compile(`<${element?.meta?.icon}/>`))
|
||||
: null;
|
||||
const node =
|
||||
element?.children && element?.children.length !== 0 ? (
|
||||
<a-sub-menu
|
||||
key={element?.name}
|
||||
v-slots={{
|
||||
icon,
|
||||
title: () => h(compile(element?.meta?.locale || ''))
|
||||
}}
|
||||
>
|
||||
{travel(element?.children)}
|
||||
</a-sub-menu>
|
||||
) : (
|
||||
<a-menu-item
|
||||
key={element?.name}
|
||||
v-slots={{ icon }}
|
||||
onClick={() => goto(element)}
|
||||
>
|
||||
{element?.meta?.locale || ''}
|
||||
</a-menu-item>
|
||||
);
|
||||
nodes.push(node as never);
|
||||
});
|
||||
}
|
||||
}, true);
|
||||
const setCollapse = (val: boolean) => {
|
||||
if (appStore.device === 'desktop')
|
||||
appStore.updateSettings({ menuCollapse: val });
|
||||
};
|
||||
return nodes;
|
||||
}
|
||||
return travel(menuTree.value);
|
||||
};
|
||||
|
||||
const renderSubMenu = () => {
|
||||
function travel(_route: RouteRecordRaw[], nodes = []) {
|
||||
if (_route) {
|
||||
_route.forEach((element) => {
|
||||
// This is demo, modify nodes as needed
|
||||
const icon = element?.meta?.icon
|
||||
? () => h(compile(`<${element?.meta?.icon}/>`))
|
||||
: null;
|
||||
const node =
|
||||
element?.children && element?.children.length !== 0 ? (
|
||||
<a-sub-menu
|
||||
key={element?.name}
|
||||
v-slots={{
|
||||
icon,
|
||||
title: () => h(compile(element?.meta?.locale || '')),
|
||||
}}
|
||||
>
|
||||
{travel(element?.children)}
|
||||
</a-sub-menu>
|
||||
) : (
|
||||
<a-menu-item
|
||||
key={element?.name}
|
||||
v-slots={{ icon }}
|
||||
onClick={() => goto(element)}
|
||||
>
|
||||
{element?.meta?.locale || ''}
|
||||
</a-menu-item>
|
||||
);
|
||||
nodes.push(node as never);
|
||||
});
|
||||
}
|
||||
return nodes;
|
||||
}
|
||||
return travel(menuTree.value);
|
||||
};
|
||||
|
||||
return () => (
|
||||
<a-menu
|
||||
mode={topMenu.value ? 'horizontal' : 'vertical'}
|
||||
v-model:collapsed={collapsed.value}
|
||||
v-model:open-keys={openKeys.value}
|
||||
show-collapse-button={appStore.device !== 'mobile'}
|
||||
auto-open={false}
|
||||
selected-keys={selectedKey.value}
|
||||
auto-open-selected={true}
|
||||
level-indent={34}
|
||||
class="menu"
|
||||
onCollapse={setCollapse}
|
||||
>
|
||||
{renderSubMenu()}
|
||||
</a-menu>
|
||||
);
|
||||
},
|
||||
});
|
||||
return () => (
|
||||
<a-menu
|
||||
mode={topMenu.value ? 'horizontal' : 'vertical'}
|
||||
v-model:collapsed={collapsed.value}
|
||||
v-model:open-keys={openKeys.value}
|
||||
show-collapse-button={appStore.device !== 'mobile'}
|
||||
auto-open={false}
|
||||
selected-keys={selectedKey.value}
|
||||
auto-open-selected={true}
|
||||
level-indent={34}
|
||||
class='menu'
|
||||
onCollapse={setCollapse}
|
||||
>
|
||||
{renderSubMenu()}
|
||||
</a-menu>
|
||||
);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
:deep(.arco-menu-inner) {
|
||||
.arco-menu-inline-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.arco-icon {
|
||||
&:not(.arco-icon-down) {
|
||||
font-size: 18px;
|
||||
}
|
||||
}
|
||||
:deep(.arco-menu-inner) {
|
||||
.arco-menu-inline-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.menu {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
.arco-icon {
|
||||
&:not(.arco-icon-down) {
|
||||
font-size: 18px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.menu {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { computed } from 'vue';
|
||||
import { RouteRecordRaw, RouteRecordNormalized } from 'vue-router';
|
||||
import type { RouteRecordRaw, RouteRecordNormalized } from 'vue-router';
|
||||
import usePermission from '@/hooks/permission';
|
||||
import { useAppStore } from '@/store';
|
||||
import appClientMenus from '@/router/app-menus';
|
||||
@@ -22,7 +22,7 @@ export default function useMenuTree() {
|
||||
function travel(_routes: RouteRecordRaw[], layer: number) {
|
||||
if (!_routes) return null;
|
||||
|
||||
const collector: any = _routes.map((element) => {
|
||||
const collector: any = _routes.map(element => {
|
||||
// no access
|
||||
if (!permission.accessRouter(element)) {
|
||||
return null;
|
||||
@@ -36,7 +36,7 @@ export default function useMenuTree() {
|
||||
|
||||
// route filter hideInMenu true
|
||||
element.children = element.children.filter(
|
||||
(x) => x.meta?.hideInMenu !== true
|
||||
x => x.meta?.hideInMenu !== true
|
||||
);
|
||||
|
||||
// Associated child node
|
||||
@@ -64,6 +64,6 @@ export default function useMenuTree() {
|
||||
});
|
||||
|
||||
return {
|
||||
menuTree,
|
||||
menuTree
|
||||
};
|
||||
}
|
||||
|
||||
@@ -82,25 +82,25 @@
|
||||
<a-doption>
|
||||
<a-space @click="switchRoles">
|
||||
<icon-tag />
|
||||
<span> 切换角色 </span>
|
||||
<span>切换角色</span>
|
||||
</a-space>
|
||||
</a-doption>
|
||||
<a-doption>
|
||||
<a-space @click="$router.push({ name: 'Info' })">
|
||||
<icon-user />
|
||||
<span> 用户中心 </span>
|
||||
<span>用户中心</span>
|
||||
</a-space>
|
||||
</a-doption>
|
||||
<a-doption>
|
||||
<a-space @click="$router.push({ name: 'Setting' })">
|
||||
<icon-settings />
|
||||
<span> 用户设置 </span>
|
||||
<span>用户设置</span>
|
||||
</a-space>
|
||||
</a-doption>
|
||||
<a-doption>
|
||||
<a-space @click="handleLogout">
|
||||
<icon-export />
|
||||
<span> 退出登录 </span>
|
||||
<span>退出登录</span>
|
||||
</a-space>
|
||||
</a-doption>
|
||||
</template>
|
||||
@@ -111,123 +111,128 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, ref, inject } from 'vue';
|
||||
import { Message } from '@arco-design/web-vue';
|
||||
import { useDark, useToggle, useFullscreen } from '@vueuse/core';
|
||||
import { useAppStore, useUserStore } from '@/store';
|
||||
import useUser from '@/hooks/user';
|
||||
import Menu from '@/components/menu/index.vue';
|
||||
import { computed, ref, inject } from 'vue';
|
||||
import { Message } from '@arco-design/web-vue';
|
||||
import { useDark, useToggle, useFullscreen } from '@vueuse/core';
|
||||
import { useAppStore, useUserStore } from '@/store';
|
||||
import useUser from '@/hooks/user';
|
||||
import Menu from '@/components/menu/index.vue';
|
||||
|
||||
const appStore = useAppStore();
|
||||
const userStore = useUserStore();
|
||||
const { logout } = useUser();
|
||||
const { isFullscreen, toggle: toggleFullScreen } = useFullscreen();
|
||||
const theme = computed(() => {
|
||||
return appStore.theme;
|
||||
});
|
||||
const topMenu = computed(() => appStore.topMenu && appStore.menu);
|
||||
const isDark = useDark({
|
||||
selector: 'body',
|
||||
attribute: 'arco-theme',
|
||||
valueDark: 'dark',
|
||||
valueLight: 'light',
|
||||
storageKey: 'arco-theme',
|
||||
onChanged(dark: boolean) {
|
||||
// overridden default behavior
|
||||
appStore.toggleTheme(dark);
|
||||
},
|
||||
});
|
||||
const toggleTheme = useToggle(isDark);
|
||||
const handleToggleTheme = () => {
|
||||
toggleTheme();
|
||||
};
|
||||
const setVisible = () => {
|
||||
appStore.updateSettings({ globalSettings: true });
|
||||
};
|
||||
const refBtn = ref();
|
||||
const triggerBtn = ref();
|
||||
const setPopoverVisible = () => {
|
||||
const event = new MouseEvent('click', {
|
||||
view: window,
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
});
|
||||
refBtn.value.dispatchEvent(event);
|
||||
};
|
||||
const handleLogout = () => {
|
||||
logout();
|
||||
};
|
||||
const setDropDownVisible = () => {
|
||||
const event = new MouseEvent('click', {
|
||||
view: window,
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
});
|
||||
triggerBtn.value.dispatchEvent(event);
|
||||
};
|
||||
const switchRoles = async () => {
|
||||
const res = await userStore.switchRoles();
|
||||
Message.success(res as string);
|
||||
};
|
||||
const toggleDrawerMenu = inject('toggleDrawerMenu') as () => void;
|
||||
const appStore = useAppStore();
|
||||
const userStore = useUserStore();
|
||||
const { logout } = useUser();
|
||||
const { isFullscreen, toggle: toggleFullScreen } = useFullscreen();
|
||||
const theme = computed(() => {
|
||||
return appStore.theme;
|
||||
});
|
||||
const topMenu = computed(() => appStore.topMenu && appStore.menu);
|
||||
const isDark = useDark({
|
||||
selector: 'body',
|
||||
attribute: 'arco-theme',
|
||||
valueDark: 'dark',
|
||||
valueLight: 'light',
|
||||
storageKey: 'arco-theme',
|
||||
onChanged(dark: boolean) {
|
||||
// overridden default behavior
|
||||
appStore.toggleTheme(dark);
|
||||
}
|
||||
});
|
||||
const toggleTheme = useToggle(isDark);
|
||||
const handleToggleTheme = () => {
|
||||
toggleTheme();
|
||||
};
|
||||
// const setVisible = () => {
|
||||
// appStore.updateSettings({ globalSettings: true });
|
||||
// };
|
||||
const refBtn = ref();
|
||||
const triggerBtn = ref();
|
||||
// const setPopoverVisible = () => {
|
||||
// const event = new MouseEvent('click', {
|
||||
// view: window,
|
||||
// bubbles: true,
|
||||
// cancelable: true,
|
||||
// });
|
||||
// refBtn.value.dispatchEvent(event);
|
||||
// };
|
||||
const handleLogout = () => {
|
||||
logout();
|
||||
};
|
||||
// const setDropDownVisible = () => {
|
||||
// const event = new MouseEvent('click', {
|
||||
// view: window,
|
||||
// bubbles: true,
|
||||
// cancelable: true,
|
||||
// });
|
||||
// triggerBtn.value.dispatchEvent(event);
|
||||
// };
|
||||
const switchRoles = async () => {
|
||||
const res = await userStore.switchRoles();
|
||||
Message.success(res as string);
|
||||
};
|
||||
const toggleDrawerMenu = inject('toggleDrawerMenu') as () => void;
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.navbar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
height: 100%;
|
||||
background-color: var(--color-bg-2);
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
.navbar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
height: 100%;
|
||||
background-color: var(--color-bg-2);
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
}
|
||||
|
||||
.left-side {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
.center-side {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.right-side {
|
||||
display: flex;
|
||||
padding-right: 20px;
|
||||
list-style: none;
|
||||
|
||||
:deep(.locale-select) {
|
||||
border-radius: 20px;
|
||||
}
|
||||
|
||||
.left-side {
|
||||
li {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-left: 20px;
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
.center-side {
|
||||
flex: 1;
|
||||
a {
|
||||
color: var(--color-text-1);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.right-side {
|
||||
display: flex;
|
||||
padding-right: 20px;
|
||||
list-style: none;
|
||||
:deep(.locale-select) {
|
||||
border-radius: 20px;
|
||||
}
|
||||
li {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--color-text-1);
|
||||
text-decoration: none;
|
||||
}
|
||||
.nav-btn {
|
||||
border-color: rgb(var(--gray-2));
|
||||
color: rgb(var(--gray-8));
|
||||
font-size: 16px;
|
||||
}
|
||||
.trigger-btn,
|
||||
.ref-btn {
|
||||
position: absolute;
|
||||
bottom: 14px;
|
||||
}
|
||||
.trigger-btn {
|
||||
margin-left: 14px;
|
||||
}
|
||||
.nav-btn {
|
||||
font-size: 16px;
|
||||
color: rgb(var(--gray-8));
|
||||
border-color: rgb(var(--gray-2));
|
||||
}
|
||||
|
||||
.trigger-btn,
|
||||
.ref-btn {
|
||||
position: absolute;
|
||||
bottom: 14px;
|
||||
}
|
||||
|
||||
.trigger-btn {
|
||||
margin-left: 14px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="less">
|
||||
.message-popover {
|
||||
.arco-popover-content {
|
||||
margin-top: 0;
|
||||
}
|
||||
.message-popover {
|
||||
.arco-popover-content {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -19,83 +19,87 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed, watch, onUnmounted } from 'vue';
|
||||
import type { RouteLocationNormalized } from 'vue-router';
|
||||
import {
|
||||
listenerRouteChange,
|
||||
removeRouteListener,
|
||||
} from '@/utils/route-listener';
|
||||
import { useAppStore, useTabBarStore } from '@/store';
|
||||
import tabItem from './tab-item.vue';
|
||||
import { ref, computed, watch, onUnmounted } from 'vue';
|
||||
import type { RouteLocationNormalized } from 'vue-router';
|
||||
import {
|
||||
listenerRouteChange,
|
||||
removeRouteListener
|
||||
} from '@/utils/route-listener';
|
||||
import { useAppStore, useTabBarStore } from '@/store';
|
||||
import tabItem from './tab-item.vue';
|
||||
|
||||
const appStore = useAppStore();
|
||||
const tabBarStore = useTabBarStore();
|
||||
const appStore = useAppStore();
|
||||
const tabBarStore = useTabBarStore();
|
||||
|
||||
const affixRef = ref();
|
||||
const tagList = computed(() => {
|
||||
return tabBarStore.getTabList;
|
||||
});
|
||||
const offsetTop = computed(() => {
|
||||
return appStore.navbar ? 60 : 0;
|
||||
});
|
||||
const affixRef = ref();
|
||||
const tagList = computed(() => {
|
||||
return tabBarStore.getTabList;
|
||||
});
|
||||
const offsetTop = computed(() => {
|
||||
return appStore.navbar ? 60 : 0;
|
||||
});
|
||||
|
||||
watch(
|
||||
() => appStore.navbar,
|
||||
() => {
|
||||
affixRef.value.updatePosition();
|
||||
}
|
||||
);
|
||||
listenerRouteChange((route: RouteLocationNormalized) => {
|
||||
if (
|
||||
!route.meta.noAffix &&
|
||||
!tagList.value.some((tag) => tag.fullPath === route.fullPath)
|
||||
) {
|
||||
tabBarStore.updateTabList(route);
|
||||
}
|
||||
}, true);
|
||||
watch(
|
||||
() => appStore.navbar,
|
||||
() => {
|
||||
affixRef.value.updatePosition();
|
||||
}
|
||||
);
|
||||
listenerRouteChange((route: RouteLocationNormalized) => {
|
||||
if (
|
||||
!route.meta.noAffix &&
|
||||
!tagList.value.some(tag => tag.fullPath === route.fullPath)
|
||||
) {
|
||||
tabBarStore.updateTabList(route);
|
||||
}
|
||||
}, true);
|
||||
|
||||
onUnmounted(() => {
|
||||
removeRouteListener();
|
||||
});
|
||||
onUnmounted(() => {
|
||||
removeRouteListener();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.tab-bar-container {
|
||||
position: relative;
|
||||
background-color: var(--color-bg-2);
|
||||
.tab-bar-box {
|
||||
display: flex;
|
||||
padding: 0 0 0 20px;
|
||||
background-color: var(--color-bg-2);
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
.tab-bar-scroll {
|
||||
height: 32px;
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
.tags-wrap {
|
||||
padding: 4px 0;
|
||||
height: 48px;
|
||||
white-space: nowrap;
|
||||
overflow-x: auto;
|
||||
.tab-bar-container {
|
||||
position: relative;
|
||||
background-color: var(--color-bg-2);
|
||||
|
||||
:deep(.arco-tag) {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
margin-right: 6px;
|
||||
cursor: pointer;
|
||||
&:first-child {
|
||||
.arco-tag-close-btn {
|
||||
display: none;
|
||||
}
|
||||
.tab-bar-box {
|
||||
display: flex;
|
||||
padding: 0 0 0 20px;
|
||||
background-color: var(--color-bg-2);
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
|
||||
.tab-bar-scroll {
|
||||
flex: 1;
|
||||
height: 32px;
|
||||
overflow: hidden;
|
||||
|
||||
.tags-wrap {
|
||||
height: 48px;
|
||||
padding: 4px 0;
|
||||
overflow-x: auto;
|
||||
white-space: nowrap;
|
||||
|
||||
:deep(.arco-tag) {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
margin-right: 6px;
|
||||
cursor: pointer;
|
||||
|
||||
&:first-child {
|
||||
.arco-tag-close-btn {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tag-bar-operation {
|
||||
width: 100px;
|
||||
height: 32px;
|
||||
}
|
||||
}
|
||||
|
||||
.tag-bar-operation {
|
||||
width: 100px;
|
||||
height: 32px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -4,9 +4,8 @@
|
||||
|
||||
同时仅仅提供最基本的功能,后续进行优化及更改。
|
||||
|
||||
|
||||
## Component description
|
||||
|
||||
The component unofficial final design specification exists as a separate component.
|
||||
|
||||
At the same time, only the most basic functions are provided, and subsequent optimizations and changes will be made.
|
||||
At the same time, only the most basic functions are provided, and subsequent optimizations and changes will be made.
|
||||
|
||||
@@ -57,151 +57,150 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { PropType, computed } from 'vue';
|
||||
import { useRouter, useRoute } from 'vue-router';
|
||||
import { useTabBarStore } from '@/store';
|
||||
import type { TagProps } from '@/store/modules/tab-bar/types';
|
||||
import { DEFAULT_ROUTE_NAME, REDIRECT_ROUTE_NAME } from '@/router/constants';
|
||||
import { PropType, computed } from 'vue';
|
||||
import { useRouter, useRoute } from 'vue-router';
|
||||
import { useTabBarStore } from '@/store';
|
||||
import type { TagProps } from '@/store/modules/tab-bar/types';
|
||||
import { DEFAULT_ROUTE_NAME, REDIRECT_ROUTE_NAME } from '@/router/constants';
|
||||
|
||||
// eslint-disable-next-line no-shadow
|
||||
enum Eaction {
|
||||
reload = 'reload',
|
||||
current = 'current',
|
||||
left = 'left',
|
||||
right = 'right',
|
||||
others = 'others',
|
||||
all = 'all',
|
||||
enum Eaction {
|
||||
reload = 'reload',
|
||||
current = 'current',
|
||||
left = 'left',
|
||||
right = 'right',
|
||||
others = 'others',
|
||||
all = 'all'
|
||||
}
|
||||
|
||||
const props = defineProps({
|
||||
itemData: {
|
||||
type: Object as PropType<TagProps>,
|
||||
default() {
|
||||
return [];
|
||||
}
|
||||
},
|
||||
index: {
|
||||
type: Number,
|
||||
default: 0
|
||||
}
|
||||
});
|
||||
|
||||
const props = defineProps({
|
||||
itemData: {
|
||||
type: Object as PropType<TagProps>,
|
||||
default() {
|
||||
return [];
|
||||
},
|
||||
},
|
||||
index: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
});
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
const tabBarStore = useTabBarStore();
|
||||
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
const tabBarStore = useTabBarStore();
|
||||
const goto = (tag: TagProps) => {
|
||||
router.push({ ...tag });
|
||||
};
|
||||
const tagList = computed(() => {
|
||||
return tabBarStore.getTabList;
|
||||
});
|
||||
|
||||
const goto = (tag: TagProps) => {
|
||||
router.push({ ...tag });
|
||||
};
|
||||
const tagList = computed(() => {
|
||||
return tabBarStore.getTabList;
|
||||
});
|
||||
const disabledReload = computed(() => {
|
||||
return props.itemData.fullPath !== route.fullPath;
|
||||
});
|
||||
|
||||
const disabledReload = computed(() => {
|
||||
return props.itemData.fullPath !== route.fullPath;
|
||||
});
|
||||
const disabledCurrent = computed(() => {
|
||||
return props.index === 0;
|
||||
});
|
||||
|
||||
const disabledCurrent = computed(() => {
|
||||
return props.index === 0;
|
||||
});
|
||||
const disabledLeft = computed(() => {
|
||||
return [0, 1].includes(props.index);
|
||||
});
|
||||
|
||||
const disabledLeft = computed(() => {
|
||||
return [0, 1].includes(props.index);
|
||||
});
|
||||
const disabledRight = computed(() => {
|
||||
return props.index === tagList.value.length - 1;
|
||||
});
|
||||
|
||||
const disabledRight = computed(() => {
|
||||
return props.index === tagList.value.length - 1;
|
||||
});
|
||||
const tagClose = (tag: TagProps, idx: number) => {
|
||||
tabBarStore.deleteTag(idx, tag);
|
||||
if (props.itemData.fullPath === route.fullPath) {
|
||||
const latest = tagList.value[idx - 1]; // 获取队列的前一个tab
|
||||
router.push({ name: latest.name });
|
||||
}
|
||||
};
|
||||
|
||||
const tagClose = (tag: TagProps, idx: number) => {
|
||||
tabBarStore.deleteTag(idx, tag);
|
||||
if (props.itemData.fullPath === route.fullPath) {
|
||||
const latest = tagList.value[idx - 1]; // 获取队列的前一个tab
|
||||
router.push({ name: latest.name });
|
||||
}
|
||||
};
|
||||
const findCurrentRouteIndex = () => {
|
||||
return tagList.value.findIndex(el => el.fullPath === route.fullPath);
|
||||
};
|
||||
const actionSelect = async (value: any) => {
|
||||
const { itemData, index } = props;
|
||||
const copyTagList = [...tagList.value];
|
||||
if (value === Eaction.current) {
|
||||
tagClose(itemData, index);
|
||||
} else if (value === Eaction.left) {
|
||||
const currentRouteIdx = findCurrentRouteIndex();
|
||||
copyTagList.splice(1, props.index - 1);
|
||||
|
||||
const findCurrentRouteIndex = () => {
|
||||
return tagList.value.findIndex((el) => el.fullPath === route.fullPath);
|
||||
};
|
||||
const actionSelect = async (value: any) => {
|
||||
const { itemData, index } = props;
|
||||
const copyTagList = [...tagList.value];
|
||||
if (value === Eaction.current) {
|
||||
tagClose(itemData, index);
|
||||
} else if (value === Eaction.left) {
|
||||
const currentRouteIdx = findCurrentRouteIndex();
|
||||
copyTagList.splice(1, props.index - 1);
|
||||
|
||||
tabBarStore.freshTabList(copyTagList);
|
||||
if (currentRouteIdx < index) {
|
||||
router.push({ name: itemData.name });
|
||||
}
|
||||
} else if (value === Eaction.right) {
|
||||
const currentRouteIdx = findCurrentRouteIndex();
|
||||
copyTagList.splice(props.index + 1);
|
||||
|
||||
tabBarStore.freshTabList(copyTagList);
|
||||
if (currentRouteIdx > index) {
|
||||
router.push({ name: itemData.name });
|
||||
}
|
||||
} else if (value === Eaction.others) {
|
||||
const filterList = tagList.value.filter((el, idx) => {
|
||||
return idx === 0 || idx === props.index;
|
||||
});
|
||||
tabBarStore.freshTabList(filterList);
|
||||
tabBarStore.freshTabList(copyTagList);
|
||||
if (currentRouteIdx < index) {
|
||||
router.push({ name: itemData.name });
|
||||
} else if (value === Eaction.reload) {
|
||||
tabBarStore.deleteCache(itemData);
|
||||
await router.push({
|
||||
name: REDIRECT_ROUTE_NAME,
|
||||
params: {
|
||||
path: route.fullPath,
|
||||
},
|
||||
});
|
||||
tabBarStore.addCache(itemData.name);
|
||||
} else {
|
||||
tabBarStore.resetTabList();
|
||||
router.push({ name: DEFAULT_ROUTE_NAME });
|
||||
}
|
||||
};
|
||||
} else if (value === Eaction.right) {
|
||||
const currentRouteIdx = findCurrentRouteIndex();
|
||||
copyTagList.splice(props.index + 1);
|
||||
|
||||
tabBarStore.freshTabList(copyTagList);
|
||||
if (currentRouteIdx > index) {
|
||||
router.push({ name: itemData.name });
|
||||
}
|
||||
} else if (value === Eaction.others) {
|
||||
const filterList = tagList.value.filter((_, idx) => {
|
||||
return idx === 0 || idx === props.index;
|
||||
});
|
||||
tabBarStore.freshTabList(filterList);
|
||||
router.push({ name: itemData.name });
|
||||
} else if (value === Eaction.reload) {
|
||||
tabBarStore.deleteCache(itemData);
|
||||
await router.push({
|
||||
name: REDIRECT_ROUTE_NAME,
|
||||
params: {
|
||||
path: route.fullPath
|
||||
}
|
||||
});
|
||||
tabBarStore.addCache(itemData.name);
|
||||
} else {
|
||||
tabBarStore.resetTabList();
|
||||
router.push({ name: DEFAULT_ROUTE_NAME });
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.tag-link {
|
||||
color: var(--color-text-2);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.link-activated {
|
||||
color: rgb(var(--link-6));
|
||||
|
||||
.tag-link {
|
||||
color: var(--color-text-2);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.link-activated {
|
||||
color: rgb(var(--link-6));
|
||||
|
||||
.tag-link {
|
||||
color: rgb(var(--link-6));
|
||||
}
|
||||
|
||||
& + .arco-tag-close-btn {
|
||||
color: rgb(var(--link-6));
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.arco-dropdown-option-content) {
|
||||
span {
|
||||
margin-left: 10px;
|
||||
}
|
||||
& + .arco-tag-close-btn {
|
||||
color: rgb(var(--link-6));
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.arco-dropdown-option-content) {
|
||||
span {
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.arco-dropdown-open {
|
||||
.tag-link {
|
||||
color: rgb(var(--danger-6));
|
||||
}
|
||||
|
||||
.arco-dropdown-open {
|
||||
.tag-link {
|
||||
color: rgb(var(--danger-6));
|
||||
}
|
||||
|
||||
.arco-tag-close-btn {
|
||||
color: rgb(var(--danger-6));
|
||||
}
|
||||
.arco-tag-close-btn {
|
||||
color: rgb(var(--danger-6));
|
||||
}
|
||||
}
|
||||
|
||||
.sperate-line {
|
||||
border-bottom: 1px solid var(--color-neutral-3);
|
||||
}
|
||||
.sperate-line {
|
||||
border-bottom: 1px solid var(--color-neutral-3);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { App } from 'vue';
|
||||
import type { App } from 'vue';
|
||||
import permission from './permission';
|
||||
|
||||
export default {
|
||||
install(Vue: App) {
|
||||
Vue.directive('permission', permission);
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { DirectiveBinding } from 'vue';
|
||||
import type { DirectiveBinding } from 'vue';
|
||||
import { useUserStore } from '@/store';
|
||||
|
||||
function checkPermission(el: HTMLElement, binding: DirectiveBinding) {
|
||||
@@ -26,5 +26,5 @@ export default {
|
||||
},
|
||||
updated(el: HTMLElement, binding: DirectiveBinding) {
|
||||
checkPermission(el, binding);
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
4
src/env.d.ts
vendored
4
src/env.d.ts
vendored
@@ -1,8 +1,8 @@
|
||||
/// <reference types="vite/client" />
|
||||
|
||||
declare module '*.vue' {
|
||||
import { DefineComponent } from 'vue';
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types
|
||||
import type { DefineComponent } from 'vue';
|
||||
|
||||
const component: DefineComponent<{}, {}, any>;
|
||||
export default component;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { computed } from 'vue';
|
||||
import { EChartsOption } from 'echarts';
|
||||
import type { EChartsOption } from 'echarts';
|
||||
import { useAppStore } from '@/store';
|
||||
|
||||
// for code hints
|
||||
@@ -22,6 +22,6 @@ export default function useChartOption(sourceOption: optionsFn) {
|
||||
return sourceOption(isDark.value);
|
||||
});
|
||||
return {
|
||||
chartOption,
|
||||
chartOption
|
||||
};
|
||||
}
|
||||
|
||||
@@ -11,6 +11,6 @@ export default function useLoading(initValue = false) {
|
||||
return {
|
||||
loading,
|
||||
setLoading,
|
||||
toggle,
|
||||
toggle
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { RouteLocationNormalized, RouteRecordRaw } from 'vue-router';
|
||||
import type { RouteLocationNormalized, RouteRecordRaw } from 'vue-router';
|
||||
import { useUserStore } from '@/store';
|
||||
|
||||
export default function usePermission() {
|
||||
@@ -27,7 +27,7 @@ export default function usePermission() {
|
||||
}
|
||||
}
|
||||
return null;
|
||||
},
|
||||
}
|
||||
// You can add any rules you want
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { ref, UnwrapRef } from 'vue';
|
||||
import { AxiosResponse } from 'axios';
|
||||
import { HttpResponse } from '@/api/interceptor';
|
||||
import { ref, type UnwrapRef } from 'vue';
|
||||
import type { AxiosResponse } from 'axios';
|
||||
import type { HttpResponse } from '@/api/interceptor';
|
||||
import useLoading from './loading';
|
||||
|
||||
// use to fetch list
|
||||
@@ -16,7 +16,7 @@ export default function useRequest<T>(
|
||||
const { loading, setLoading } = useLoading(isLoading);
|
||||
const response = ref<T>(defaultValue);
|
||||
api()
|
||||
.then((res) => {
|
||||
.then(res => {
|
||||
response.value = res.data as unknown as UnwrapRef<T>;
|
||||
})
|
||||
.finally(() => {
|
||||
|
||||
@@ -7,6 +7,6 @@ export default function useThemes() {
|
||||
return appStore.theme === 'dark';
|
||||
});
|
||||
return {
|
||||
isDark,
|
||||
isDark
|
||||
};
|
||||
}
|
||||
|
||||
@@ -14,11 +14,11 @@ export default function useUser() {
|
||||
name: logoutTo && typeof logoutTo === 'string' ? logoutTo : 'login',
|
||||
query: {
|
||||
...router.currentRoute.value.query,
|
||||
redirect: currentRoute.name as string,
|
||||
},
|
||||
redirect: currentRoute.name as string
|
||||
}
|
||||
});
|
||||
};
|
||||
return {
|
||||
logout,
|
||||
logout
|
||||
};
|
||||
}
|
||||
|
||||
@@ -11,6 +11,6 @@ export default function useVisible(initValue = false) {
|
||||
return {
|
||||
visible,
|
||||
setVisible,
|
||||
toggle,
|
||||
toggle
|
||||
};
|
||||
}
|
||||
|
||||
@@ -45,134 +45,136 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed, watch, provide, onMounted } from 'vue';
|
||||
import { useRouter, useRoute } from 'vue-router';
|
||||
import { useAppStore, useUserStore } from '@/store';
|
||||
import NavBar from '@/components/navbar/index.vue';
|
||||
import Menu from '@/components/menu/index.vue';
|
||||
import Footer from '@/components/footer/index.vue';
|
||||
import TabBar from '@/components/tab-bar/index.vue';
|
||||
import usePermission from '@/hooks/permission';
|
||||
import useResponsive from '@/hooks/responsive';
|
||||
import PageLayout from './page-layout.vue';
|
||||
import { ref, computed, watch, provide, onMounted } from 'vue';
|
||||
import { useRouter, useRoute } from 'vue-router';
|
||||
import { useAppStore, useUserStore } from '@/store';
|
||||
import NavBar from '@/components/navbar/index.vue';
|
||||
import Menu from '@/components/menu/index.vue';
|
||||
import Footer from '@/components/footer/index.vue';
|
||||
import TabBar from '@/components/tab-bar/index.vue';
|
||||
import usePermission from '@/hooks/permission';
|
||||
import useResponsive from '@/hooks/responsive';
|
||||
import PageLayout from './page-layout.vue';
|
||||
|
||||
const isInit = ref(false);
|
||||
const appStore = useAppStore();
|
||||
const userStore = useUserStore();
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
const permission = usePermission();
|
||||
useResponsive(true);
|
||||
const navbarHeight = `60px`;
|
||||
const navbar = computed(() => appStore.navbar);
|
||||
const renderMenu = computed(() => appStore.menu && !appStore.topMenu);
|
||||
const hideMenu = computed(() => appStore.hideMenu);
|
||||
const footer = computed(() => appStore.footer);
|
||||
const menuWidth = computed(() => {
|
||||
return appStore.menuCollapse ? 48 : appStore.menuWidth;
|
||||
});
|
||||
const collapsed = computed(() => {
|
||||
return appStore.menuCollapse;
|
||||
});
|
||||
const paddingStyle = computed(() => {
|
||||
const paddingLeft =
|
||||
renderMenu.value && !hideMenu.value
|
||||
? { paddingLeft: `${menuWidth.value}px` }
|
||||
: {};
|
||||
const paddingTop = navbar.value ? { paddingTop: navbarHeight } : {};
|
||||
return { ...paddingLeft, ...paddingTop };
|
||||
});
|
||||
const setCollapsed = (val: boolean) => {
|
||||
if (!isInit.value) return; // for page initialization menu state problem
|
||||
appStore.updateSettings({ menuCollapse: val });
|
||||
};
|
||||
watch(
|
||||
() => userStore.role,
|
||||
(roleValue) => {
|
||||
if (roleValue && !permission.accessRouter(route))
|
||||
router.push({ name: 'notFound' });
|
||||
}
|
||||
);
|
||||
const drawerVisible = ref(false);
|
||||
const drawerCancel = () => {
|
||||
drawerVisible.value = false;
|
||||
};
|
||||
provide('toggleDrawerMenu', () => {
|
||||
drawerVisible.value = !drawerVisible.value;
|
||||
});
|
||||
onMounted(() => {
|
||||
isInit.value = true;
|
||||
});
|
||||
const isInit = ref(false);
|
||||
const appStore = useAppStore();
|
||||
const userStore = useUserStore();
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
const permission = usePermission();
|
||||
useResponsive(true);
|
||||
const navbarHeight = `60px`;
|
||||
const navbar = computed(() => appStore.navbar);
|
||||
const renderMenu = computed(() => appStore.menu && !appStore.topMenu);
|
||||
const hideMenu = computed(() => appStore.hideMenu);
|
||||
const footer = computed(() => appStore.footer);
|
||||
const menuWidth = computed(() => {
|
||||
return appStore.menuCollapse ? 48 : appStore.menuWidth;
|
||||
});
|
||||
const collapsed = computed(() => {
|
||||
return appStore.menuCollapse;
|
||||
});
|
||||
const paddingStyle = computed(() => {
|
||||
const paddingLeft =
|
||||
renderMenu.value && !hideMenu.value
|
||||
? { paddingLeft: `${menuWidth.value}px` }
|
||||
: {};
|
||||
const paddingTop = navbar.value ? { paddingTop: navbarHeight } : {};
|
||||
return { ...paddingLeft, ...paddingTop };
|
||||
});
|
||||
const setCollapsed = (val: boolean) => {
|
||||
if (!isInit.value) return; // for page initialization menu state problem
|
||||
appStore.updateSettings({ menuCollapse: val });
|
||||
};
|
||||
watch(
|
||||
() => userStore.role,
|
||||
roleValue => {
|
||||
if (roleValue && !permission.accessRouter(route))
|
||||
router.push({ name: 'notFound' });
|
||||
}
|
||||
);
|
||||
const drawerVisible = ref(false);
|
||||
const drawerCancel = () => {
|
||||
drawerVisible.value = false;
|
||||
};
|
||||
provide('toggleDrawerMenu', () => {
|
||||
drawerVisible.value = !drawerVisible.value;
|
||||
});
|
||||
onMounted(() => {
|
||||
isInit.value = true;
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
@nav-size-height: 60px;
|
||||
@layout-max-width: 1100px;
|
||||
@nav-size-height: 60px;
|
||||
@layout-max-width: 1100px;
|
||||
|
||||
.layout {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.layout {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.layout-navbar {
|
||||
position: fixed;
|
||||
.layout-navbar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 100;
|
||||
width: 100%;
|
||||
height: @nav-size-height;
|
||||
}
|
||||
|
||||
.layout-sider {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 99;
|
||||
height: 100%;
|
||||
transition: all 0.2s cubic-bezier(0.34, 0.69, 0.1, 1);
|
||||
|
||||
&::after {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 100;
|
||||
width: 100%;
|
||||
height: @nav-size-height;
|
||||
}
|
||||
|
||||
.layout-sider {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 99;
|
||||
right: -1px;
|
||||
display: block;
|
||||
width: 1px;
|
||||
height: 100%;
|
||||
transition: all 0.2s cubic-bezier(0.34, 0.69, 0.1, 1);
|
||||
&::after {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: -1px;
|
||||
display: block;
|
||||
width: 1px;
|
||||
height: 100%;
|
||||
background-color: var(--color-border);
|
||||
content: '';
|
||||
}
|
||||
|
||||
> :deep(.arco-layout-sider-children) {
|
||||
overflow-y: hidden;
|
||||
}
|
||||
content: '';
|
||||
background-color: var(--color-border);
|
||||
}
|
||||
|
||||
.menu-wrapper {
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
overflow-x: hidden;
|
||||
:deep(.arco-menu) {
|
||||
::-webkit-scrollbar {
|
||||
width: 12px;
|
||||
height: 4px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
border: 4px solid transparent;
|
||||
background-clip: padding-box;
|
||||
border-radius: 7px;
|
||||
background-color: var(--color-text-4);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background-color: var(--color-text-3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.layout-content {
|
||||
min-height: 100vh;
|
||||
> :deep(.arco-layout-sider-children) {
|
||||
overflow-y: hidden;
|
||||
background-color: var(--color-fill-2);
|
||||
transition: padding 0.2s cubic-bezier(0.34, 0.69, 0.1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
.menu-wrapper {
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
overflow-x: hidden;
|
||||
|
||||
:deep(.arco-menu) {
|
||||
::-webkit-scrollbar {
|
||||
width: 12px;
|
||||
height: 4px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background-color: var(--color-text-4);
|
||||
background-clip: padding-box;
|
||||
border: 4px solid transparent;
|
||||
border-radius: 7px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background-color: var(--color-text-3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.layout-content {
|
||||
min-height: 100vh;
|
||||
overflow-y: hidden;
|
||||
background-color: var(--color-fill-2);
|
||||
transition: padding 0.2s cubic-bezier(0.34, 0.69, 0.1, 1);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -5,20 +5,20 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useRoute } from 'vue-router';
|
||||
import { useRoute } from 'vue-router';
|
||||
|
||||
const route = useRoute();
|
||||
const route = useRoute();
|
||||
|
||||
if (route.query?.token) {
|
||||
localStorage.setItem('token', route.query.token as string);
|
||||
}
|
||||
if (route.query?.token) {
|
||||
localStorage.setItem('token', route.query.token as string);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.layout {
|
||||
min-height: 100vh;
|
||||
overflow: hidden;
|
||||
background-color: var(--color-fill-2);
|
||||
transition: padding 0.2s cubic-bezier(0.34, 0.69, 0.1, 0.1);
|
||||
}
|
||||
.layout {
|
||||
min-height: 100vh;
|
||||
overflow: hidden;
|
||||
background-color: var(--color-fill-2);
|
||||
transition: padding 0.2s cubic-bezier(0.34, 0.69, 0.1, 0.1);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -14,12 +14,12 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed } from 'vue';
|
||||
import { useTabBarStore } from '@/store';
|
||||
import { computed } from 'vue';
|
||||
import { useTabBarStore } from '@/store';
|
||||
|
||||
const tabBarStore = useTabBarStore();
|
||||
const tabBarStore = useTabBarStore();
|
||||
|
||||
const cacheList = computed(() => tabBarStore.getCacheList);
|
||||
const cacheList = computed(() => tabBarStore.getCacheList);
|
||||
</script>
|
||||
|
||||
<style scoped lang="less"></style>
|
||||
|
||||
@@ -7,7 +7,6 @@ import store from './store';
|
||||
import directive from './directive';
|
||||
import './mock';
|
||||
import App from './App.vue';
|
||||
// 样式通过 arco-plugin 插件导入。详见目录文件 config/plugin/arcoStyleImport.ts
|
||||
// https://arco.design/docs/designlab/use-theme-package
|
||||
import '@/assets/style/global.less';
|
||||
import '@/api/interceptor';
|
||||
|
||||
@@ -5,5 +5,5 @@ import './user';
|
||||
import '@/views/dashboard/workplace/mock';
|
||||
|
||||
Mock.setup({
|
||||
timeout: '600-1000',
|
||||
timeout: '600-1000'
|
||||
});
|
||||
|
||||
@@ -12,7 +12,7 @@ const getMessageList = () => {
|
||||
avatar:
|
||||
'//p1-arco.byteimg.com/tos-cn-i-uwbnlip3yd/8361eeb82904210b4f55fab888fe8416.png~tplv-uwbnlip3yd-webp.webp',
|
||||
content: '审批请求已发送,请查收',
|
||||
time: '今天 12:30:01',
|
||||
time: '今天 12:30:01'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
@@ -22,7 +22,7 @@ const getMessageList = () => {
|
||||
avatar:
|
||||
'//p1-arco.byteimg.com/tos-cn-i-uwbnlip3yd/3ee5f13fb09879ecb5185e440cef6eb9.png~tplv-uwbnlip3yd-webp.webp',
|
||||
content: '此处 bug 已经修复',
|
||||
time: '今天 12:30:01',
|
||||
time: '今天 12:30:01'
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
@@ -32,7 +32,7 @@ const getMessageList = () => {
|
||||
avatar:
|
||||
'//p1-arco.byteimg.com/tos-cn-i-uwbnlip3yd/3ee5f13fb09879ecb5185e440cef6eb9.png~tplv-uwbnlip3yd-webp.webp',
|
||||
content: '此处 bug 已经修复',
|
||||
time: '今天 12:20:01',
|
||||
time: '今天 12:20:01'
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
@@ -42,7 +42,7 @@ const getMessageList = () => {
|
||||
avatar: '',
|
||||
content: '您的产品使用期限即将截止,如需继续使用产品请前往购…',
|
||||
time: '今天 12:20:01',
|
||||
messageType: 3,
|
||||
messageType: 3
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
@@ -52,7 +52,7 @@ const getMessageList = () => {
|
||||
avatar: '',
|
||||
content: '内容屏蔽规则于 2021-12-01 开通成功并生效',
|
||||
time: '今天 12:20:01',
|
||||
messageType: 1,
|
||||
messageType: 1
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
@@ -62,11 +62,11 @@ const getMessageList = () => {
|
||||
avatar: '',
|
||||
content: '内容质检队列于 2021-12-01 19:50:23 进行变更,请重新…',
|
||||
time: '今天 12:20:01',
|
||||
messageType: 0,
|
||||
},
|
||||
].map((item) => ({
|
||||
messageType: 0
|
||||
}
|
||||
].map(item => ({
|
||||
...item,
|
||||
status: haveReadIds.indexOf(item.id) === -1 ? 0 : 1,
|
||||
status: haveReadIds.indexOf(item.id) === -1 ? 0 : 1
|
||||
}));
|
||||
};
|
||||
|
||||
@@ -81,5 +81,5 @@ setupMock({
|
||||
haveReadIds.push(...(ids || []));
|
||||
return successResponseWrap(true);
|
||||
});
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
@@ -28,7 +28,7 @@ setupMock({
|
||||
registrationDate: '2013-05-10 12:10:00',
|
||||
accountId: '15012312300',
|
||||
certification: 1,
|
||||
role,
|
||||
role
|
||||
});
|
||||
}
|
||||
return successResponseWrap({});
|
||||
@@ -44,7 +44,7 @@ setupMock({
|
||||
locale: 'menu.server.dashboard',
|
||||
requiresAuth: true,
|
||||
icon: 'icon-dashboard',
|
||||
order: 1,
|
||||
order: 1
|
||||
},
|
||||
children: [
|
||||
{
|
||||
@@ -52,21 +52,21 @@ setupMock({
|
||||
name: 'Workplace',
|
||||
meta: {
|
||||
locale: 'menu.server.workplace',
|
||||
requiresAuth: true,
|
||||
},
|
||||
requiresAuth: true
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'https://arco.design',
|
||||
name: 'arcoWebsite',
|
||||
meta: {
|
||||
locale: 'menu.arcoWebsite',
|
||||
requiresAuth: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
requiresAuth: true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
return successResponseWrap(menuList);
|
||||
});
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
@@ -2,14 +2,14 @@ import { appRoutes, appExternalRoutes } from '../routes';
|
||||
|
||||
const mixinRoutes = [...appRoutes, ...appExternalRoutes];
|
||||
|
||||
const appClientMenus = mixinRoutes.map((el) => {
|
||||
const appClientMenus = mixinRoutes.map(el => {
|
||||
const { name, path, meta, redirect, children } = el;
|
||||
return {
|
||||
name,
|
||||
path,
|
||||
meta,
|
||||
redirect,
|
||||
children,
|
||||
children
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
export const WHITE_LIST = [
|
||||
{ name: 'notFound', children: [] },
|
||||
{ name: 'login', children: [] },
|
||||
{ name: 'login', children: [] }
|
||||
];
|
||||
|
||||
export const NOT_FOUND = {
|
||||
name: 'notFound',
|
||||
name: 'notFound'
|
||||
};
|
||||
|
||||
export const REDIRECT_ROUTE_NAME = 'Redirect';
|
||||
@@ -14,5 +14,5 @@ export const DEFAULT_ROUTE_NAME = 'Workplace';
|
||||
export const DEFAULT_ROUTE = {
|
||||
title: '仪表盘',
|
||||
name: DEFAULT_ROUTE_NAME,
|
||||
fullPath: '/dashboard/workplace',
|
||||
fullPath: '/dashboard/workplace'
|
||||
};
|
||||
|
||||
@@ -4,7 +4,7 @@ import setupUserLoginInfoGuard from './userLoginInfo';
|
||||
import setupPermissionGuard from './permission';
|
||||
|
||||
function setupPageGuard(router: Router) {
|
||||
router.beforeEach(async (to) => {
|
||||
router.beforeEach(async to => {
|
||||
// emit route change
|
||||
setRouteEmitter(to);
|
||||
});
|
||||
|
||||
@@ -7,7 +7,7 @@ import { appRoutes } from '../routes';
|
||||
import { WHITE_LIST, NOT_FOUND } from '../constants';
|
||||
|
||||
export default function setupPermissionGuard(router: Router) {
|
||||
router.beforeEach(async (to, from, next) => {
|
||||
router.beforeEach(async (to, _, next) => {
|
||||
const appStore = useAppStore();
|
||||
const userStore = useUserStore();
|
||||
const Permission = usePermission();
|
||||
@@ -20,7 +20,7 @@ export default function setupPermissionGuard(router: Router) {
|
||||
// Refine the permission logic from the server's menu configuration as needed
|
||||
if (
|
||||
!appStore.appAsyncMenus.length &&
|
||||
!WHITE_LIST.find((el) => el.name === to.name)
|
||||
!WHITE_LIST.find(el => el.name === to.name)
|
||||
) {
|
||||
await appStore.fetchServerMenuConfig();
|
||||
}
|
||||
@@ -41,7 +41,6 @@ export default function setupPermissionGuard(router: Router) {
|
||||
next();
|
||||
} else next(NOT_FOUND);
|
||||
} else {
|
||||
// eslint-disable-next-line no-lonely-if
|
||||
if (permissionsAllow) next();
|
||||
else {
|
||||
const destination =
|
||||
|
||||
@@ -21,8 +21,8 @@ export default function setupUserLoginInfoGuard(router: Router) {
|
||||
name: 'login',
|
||||
query: {
|
||||
redirect: to.name,
|
||||
...to.query,
|
||||
} as LocationQueryRaw,
|
||||
...to.query
|
||||
} as LocationQueryRaw
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -35,8 +35,8 @@ export default function setupUserLoginInfoGuard(router: Router) {
|
||||
name: 'login',
|
||||
query: {
|
||||
redirect: to.name,
|
||||
...to.query,
|
||||
} as LocationQueryRaw,
|
||||
...to.query
|
||||
} as LocationQueryRaw
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -13,23 +13,23 @@ const router = createRouter({
|
||||
routes: [
|
||||
{
|
||||
path: '/',
|
||||
redirect: 'login',
|
||||
redirect: 'login'
|
||||
},
|
||||
{
|
||||
path: '/login',
|
||||
name: 'login',
|
||||
component: () => import('@/views/login/index.vue'),
|
||||
meta: {
|
||||
requiresAuth: false,
|
||||
},
|
||||
requiresAuth: false
|
||||
}
|
||||
},
|
||||
...appRoutes,
|
||||
REDIRECT_MAIN,
|
||||
NOT_FOUND_ROUTE,
|
||||
NOT_FOUND_ROUTE
|
||||
],
|
||||
scrollBehavior() {
|
||||
return { top: 0 };
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
createRouteGuard(router);
|
||||
|
||||
@@ -10,7 +10,7 @@ export const REDIRECT_MAIN: RouteRecordRaw = {
|
||||
component: DEFAULT_LAYOUT,
|
||||
meta: {
|
||||
requiresAuth: true,
|
||||
hideInMenu: true,
|
||||
hideInMenu: true
|
||||
},
|
||||
children: [
|
||||
{
|
||||
@@ -19,14 +19,14 @@ export const REDIRECT_MAIN: RouteRecordRaw = {
|
||||
component: () => import('@/views/redirect/index.vue'),
|
||||
meta: {
|
||||
requiresAuth: true,
|
||||
hideInMenu: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
hideInMenu: true
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
export const NOT_FOUND_ROUTE: RouteRecordRaw = {
|
||||
path: '/:pathMatch(.*)*',
|
||||
name: 'notFound',
|
||||
component: () => import('@/views/not-found/index.vue'),
|
||||
component: () => import('@/views/not-found/index.vue')
|
||||
};
|
||||
|
||||
@@ -2,11 +2,11 @@ import type { RouteRecordNormalized } from 'vue-router';
|
||||
|
||||
const modules = import.meta.glob('./modules/*.ts', { eager: true });
|
||||
const externalModules = import.meta.glob('./externalModules/*.ts', {
|
||||
eager: true,
|
||||
eager: true
|
||||
});
|
||||
|
||||
function formatModules(_modules: any, result: RouteRecordNormalized[]) {
|
||||
Object.keys(_modules).forEach((key) => {
|
||||
Object.keys(_modules).forEach(key => {
|
||||
const defaultModule = _modules[key].default;
|
||||
if (!defaultModule) return;
|
||||
const moduleList = Array.isArray(defaultModule)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { DEFAULT_LAYOUT } from '../base';
|
||||
import { AppRouteRecordRaw } from '../types';
|
||||
import type { AppRouteRecordRaw } from '../types';
|
||||
|
||||
const DASHBOARD: AppRouteRecordRaw = {
|
||||
path: '/dashboard',
|
||||
@@ -9,7 +9,7 @@ const DASHBOARD: AppRouteRecordRaw = {
|
||||
locale: '仪表盘',
|
||||
requiresAuth: true,
|
||||
icon: 'icon-dashboard',
|
||||
order: 0,
|
||||
order: 0
|
||||
},
|
||||
children: [
|
||||
{
|
||||
@@ -19,10 +19,10 @@ const DASHBOARD: AppRouteRecordRaw = {
|
||||
meta: {
|
||||
locale: '工作台',
|
||||
requiresAuth: true,
|
||||
roles: ['*'],
|
||||
},
|
||||
},
|
||||
],
|
||||
roles: ['*']
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
export default DASHBOARD;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { IFRAME_LAYOUT } from '../base';
|
||||
import { AppRouteRecordRaw } from '../types';
|
||||
import type { AppRouteRecordRaw } from '../types';
|
||||
|
||||
const IFRAME: AppRouteRecordRaw = {
|
||||
path: '/iframe',
|
||||
@@ -7,7 +7,7 @@ const IFRAME: AppRouteRecordRaw = {
|
||||
component: IFRAME_LAYOUT,
|
||||
meta: {
|
||||
locale: '外链',
|
||||
requiresAuth: false,
|
||||
requiresAuth: false
|
||||
},
|
||||
children: [
|
||||
{
|
||||
@@ -16,8 +16,8 @@ const IFRAME: AppRouteRecordRaw = {
|
||||
component: () => import('@/views/merchant-config/index.vue'),
|
||||
meta: {
|
||||
locale: '商户配置',
|
||||
requiresAuth: false,
|
||||
},
|
||||
requiresAuth: false
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'appleAccount',
|
||||
@@ -25,8 +25,8 @@ const IFRAME: AppRouteRecordRaw = {
|
||||
component: () => import('@/views/apple-card-info/card-info/index.vue'),
|
||||
meta: {
|
||||
locale: '账号(苹果)',
|
||||
requiresAuth: false,
|
||||
},
|
||||
requiresAuth: false
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'rechargeOrder',
|
||||
@@ -35,10 +35,10 @@ const IFRAME: AppRouteRecordRaw = {
|
||||
import('@/views/apple-card-info/recharge-history/index.vue'),
|
||||
meta: {
|
||||
locale: '充值订单',
|
||||
requiresAuth: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
requiresAuth: false
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
export default IFRAME;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { DEFAULT_LAYOUT } from '../base';
|
||||
import { AppRouteRecordRaw } from '../types';
|
||||
import type { AppRouteRecordRaw } from '../types';
|
||||
|
||||
const MERCHANT: AppRouteRecordRaw = {
|
||||
path: '/merchant',
|
||||
@@ -9,7 +9,7 @@ const MERCHANT: AppRouteRecordRaw = {
|
||||
locale: '商户管理',
|
||||
requiresAuth: true,
|
||||
icon: 'icon-dashboard',
|
||||
order: 0,
|
||||
order: 0
|
||||
},
|
||||
children: [
|
||||
{
|
||||
@@ -19,10 +19,10 @@ const MERCHANT: AppRouteRecordRaw = {
|
||||
meta: {
|
||||
locale: '商户配置',
|
||||
requiresAuth: true,
|
||||
roles: ['*'],
|
||||
},
|
||||
},
|
||||
],
|
||||
roles: ['*']
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
export default MERCHANT;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { DEFAULT_LAYOUT } from '../base';
|
||||
import { AppRouteRecordRaw } from '../types';
|
||||
import type { AppRouteRecordRaw } from '../types';
|
||||
|
||||
const DASHBOARD: AppRouteRecordRaw = {
|
||||
path: '/userCenter',
|
||||
@@ -9,7 +9,7 @@ const DASHBOARD: AppRouteRecordRaw = {
|
||||
locale: '用户管理',
|
||||
requiresAuth: true,
|
||||
icon: 'icon-dashboard',
|
||||
order: 0,
|
||||
order: 0
|
||||
},
|
||||
children: [
|
||||
{
|
||||
@@ -19,10 +19,10 @@ const DASHBOARD: AppRouteRecordRaw = {
|
||||
meta: {
|
||||
locale: '用户管理',
|
||||
requiresAuth: true,
|
||||
roles: ['*'],
|
||||
},
|
||||
},
|
||||
],
|
||||
roles: ['*']
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
export default DASHBOARD;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { defineComponent } from 'vue';
|
||||
import type { defineComponent } from 'vue';
|
||||
import type { RouteMeta, NavigationGuard } from 'vue-router';
|
||||
|
||||
export type Component<T = any> =
|
||||
|
||||
@@ -4,7 +4,7 @@ import type { NotificationReturn } from '@arco-design/web-vue/es/notification/in
|
||||
import type { RouteRecordNormalized } from 'vue-router';
|
||||
import defaultSettings from '@/config/settings.json';
|
||||
import { getMenuList } from '@/api/user';
|
||||
import { AppState } from './types';
|
||||
import type { AppState } from './types';
|
||||
|
||||
const useAppStore = defineStore('app', {
|
||||
state: (): AppState => ({ ...defaultSettings }),
|
||||
@@ -18,7 +18,7 @@ const useAppStore = defineStore('app', {
|
||||
},
|
||||
appAsyncMenus(state: AppState): RouteRecordNormalized[] {
|
||||
return state.serverMenu as unknown as RouteRecordNormalized[];
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
actions: {
|
||||
@@ -50,28 +50,28 @@ const useAppStore = defineStore('app', {
|
||||
notifyInstance = Notification.info({
|
||||
id: 'menuNotice', // Keep the instance id the same
|
||||
content: 'loading',
|
||||
closable: true,
|
||||
closable: true
|
||||
});
|
||||
const { data } = await getMenuList();
|
||||
this.serverMenu = data;
|
||||
notifyInstance = Notification.success({
|
||||
id: 'menuNotice',
|
||||
content: 'success',
|
||||
closable: true,
|
||||
closable: true
|
||||
});
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
notifyInstance = Notification.error({
|
||||
id: 'menuNotice',
|
||||
content: 'error',
|
||||
closable: true,
|
||||
closable: true
|
||||
});
|
||||
}
|
||||
},
|
||||
clearServerMenu() {
|
||||
this.serverMenu = [];
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
export default useAppStore;
|
||||
|
||||
@@ -3,10 +3,10 @@ import { defineStore } from 'pinia';
|
||||
import {
|
||||
DEFAULT_ROUTE,
|
||||
DEFAULT_ROUTE_NAME,
|
||||
REDIRECT_ROUTE_NAME,
|
||||
REDIRECT_ROUTE_NAME
|
||||
} from '@/router/constants';
|
||||
import { isString } from '@/utils/is';
|
||||
import { TabBarState, TagProps } from './types';
|
||||
import type { TabBarState, TagProps } from './types';
|
||||
|
||||
const formatTag = (route: RouteLocationNormalized): TagProps => {
|
||||
const { name, meta, fullPath, query } = route;
|
||||
@@ -15,7 +15,7 @@ const formatTag = (route: RouteLocationNormalized): TagProps => {
|
||||
name: String(name),
|
||||
fullPath,
|
||||
query,
|
||||
ignoreCache: meta.ignoreCache,
|
||||
ignoreCache: meta.ignoreCache
|
||||
};
|
||||
};
|
||||
|
||||
@@ -24,7 +24,7 @@ const BAN_LIST = [REDIRECT_ROUTE_NAME];
|
||||
const useAppStore = defineStore('tabBar', {
|
||||
state: (): TabBarState => ({
|
||||
cacheTabList: new Set([DEFAULT_ROUTE_NAME]),
|
||||
tagList: [DEFAULT_ROUTE],
|
||||
tagList: [DEFAULT_ROUTE]
|
||||
}),
|
||||
|
||||
getters: {
|
||||
@@ -33,7 +33,7 @@ const useAppStore = defineStore('tabBar', {
|
||||
},
|
||||
getCacheList(): string[] {
|
||||
return Array.from(this.cacheTabList);
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
actions: {
|
||||
@@ -59,16 +59,16 @@ const useAppStore = defineStore('tabBar', {
|
||||
this.cacheTabList.clear();
|
||||
// 要先判断ignoreCache
|
||||
this.tagList
|
||||
.filter((el) => !el.ignoreCache)
|
||||
.map((el) => el.name)
|
||||
.forEach((x) => this.cacheTabList.add(x));
|
||||
.filter(el => !el.ignoreCache)
|
||||
.map(el => el.name)
|
||||
.forEach(x => this.cacheTabList.add(x));
|
||||
},
|
||||
resetTabList() {
|
||||
this.tagList = [DEFAULT_ROUTE];
|
||||
this.cacheTabList.clear();
|
||||
this.cacheTabList.add(DEFAULT_ROUTE_NAME);
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
export default useAppStore;
|
||||
|
||||
@@ -2,12 +2,12 @@ import { defineStore } from 'pinia';
|
||||
import {
|
||||
login as userLogin,
|
||||
logout as userLogout,
|
||||
LoginData,
|
||||
getUserInfo,
|
||||
type LoginData,
|
||||
getUserInfo
|
||||
} from '@/api/user';
|
||||
import { setToken, clearToken } from '@/utils/auth';
|
||||
import { removeRouteListener } from '@/utils/route-listener';
|
||||
import { UserState } from './types';
|
||||
import type { UserState } from './types';
|
||||
import useAppStore from '../app';
|
||||
|
||||
const useUserStore = defineStore('user', {
|
||||
@@ -17,18 +17,18 @@ const useUserStore = defineStore('user', {
|
||||
phone: undefined,
|
||||
registrationDate: undefined,
|
||||
accountId: undefined,
|
||||
role: '',
|
||||
role: ''
|
||||
}),
|
||||
|
||||
getters: {
|
||||
userInfo(state: UserState): UserState {
|
||||
return { ...state };
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
actions: {
|
||||
switchRoles() {
|
||||
return new Promise((resolve) => {
|
||||
return new Promise(resolve => {
|
||||
this.role = this.role === 'user' ? 'admin' : 'user';
|
||||
resolve(this.role);
|
||||
});
|
||||
@@ -73,8 +73,8 @@ const useUserStore = defineStore('user', {
|
||||
} finally {
|
||||
this.logoutCallBack();
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
export default useUserStore;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { CallbackDataParams } from 'echarts/types/dist/shared';
|
||||
import type { CallbackDataParams } from 'echarts/types/dist/shared.js';
|
||||
|
||||
export interface ToolTipFormatterParams extends CallbackDataParams {
|
||||
axisDim: string;
|
||||
|
||||
@@ -7,7 +7,7 @@ export function encryptWithBase64(ciphertext: string) {
|
||||
const options = {
|
||||
iv: CryptoJS.enc.Utf8.parse(iv),
|
||||
mode: CryptoJS.mode.CBC,
|
||||
padding: CryptoJS.pad.Pkcs7,
|
||||
padding: CryptoJS.pad.Pkcs7
|
||||
};
|
||||
const key = CryptoJS.enc.Utf8.parse(CRYPTOJSKEY);
|
||||
const encryptedData = CryptoJS.AES.encrypt(ciphertext, key, options);
|
||||
@@ -19,7 +19,7 @@ export function decryptWithBase64(ciphertext: string) {
|
||||
const options = {
|
||||
iv: CryptoJS.enc.Utf8.parse(iv),
|
||||
mode: CryptoJS.mode.CBC,
|
||||
padding: CryptoJS.pad.Pkcs7,
|
||||
padding: CryptoJS.pad.Pkcs7
|
||||
};
|
||||
const key = CryptoJS.enc.Utf8.parse(CRYPTOJSKEY);
|
||||
const decryptData = CryptoJS.AES.decrypt(ciphertext, key, options);
|
||||
|
||||
@@ -13,7 +13,7 @@ export function isString(obj: any): obj is string {
|
||||
}
|
||||
|
||||
export function isNumber(obj: any): obj is number {
|
||||
return opt.call(obj) === '[object Number]' && obj === obj; // eslint-disable-line
|
||||
return opt.call(obj) === '[object Number]' && obj === obj;
|
||||
}
|
||||
|
||||
export function isRegExp(obj: any) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { App, ComponentPublicInstance } from 'vue';
|
||||
import type { App, ComponentPublicInstance } from 'vue';
|
||||
import axios from 'axios';
|
||||
|
||||
export default function handleError(Vue: App, baseUrl: string) {
|
||||
@@ -14,7 +14,7 @@ export default function handleError(Vue: App, baseUrl: string) {
|
||||
axios.post(`${baseUrl}/report-error`, {
|
||||
err,
|
||||
instance,
|
||||
info,
|
||||
info
|
||||
// location: window.location.href,
|
||||
// message: err.message,
|
||||
// stack: err.stack,
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* Listening to routes alone would waste rendering performance. Use the publish-subscribe model for distribution management
|
||||
* 单独监听路由会浪费渲染性能。使用发布订阅模式去进行分发管理。
|
||||
*/
|
||||
import mitt, { Handler } from 'mitt';
|
||||
import mitt, { type Handler } from 'mitt';
|
||||
import type { RouteLocationNormalized } from 'vue-router';
|
||||
|
||||
const emitter = mitt();
|
||||
|
||||
@@ -9,7 +9,7 @@ export const successResponseWrap = (data: unknown) => {
|
||||
data,
|
||||
status: 'ok',
|
||||
msg: '请求成功',
|
||||
code: 0,
|
||||
code: 0
|
||||
};
|
||||
};
|
||||
|
||||
@@ -18,6 +18,6 @@ export const failResponseWrap = (data: unknown, msg: string, code = 50000) => {
|
||||
data,
|
||||
status: 'fail',
|
||||
msg,
|
||||
code,
|
||||
code
|
||||
};
|
||||
};
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
@ok="handleOk"
|
||||
@cancel="handleCancel"
|
||||
>
|
||||
<template #title> {{ props.id === '' ? '新增' : '编辑' }}账户 </template>
|
||||
<template #title>{{ props.id === '' ? '新增' : '编辑' }}账户</template>
|
||||
<a-form :model="formData">
|
||||
<a-form-item field="account" label="账号" required>
|
||||
<a-input v-model="formData.account" :readonly="props.id !== ''" />
|
||||
@@ -18,65 +18,65 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import {
|
||||
addAppleCard,
|
||||
AppleCardAddRecord,
|
||||
updateAppleCard,
|
||||
} from '@/api/apple-card-info';
|
||||
import useLoading from '@/hooks/loading';
|
||||
import { ref, watch } from 'vue';
|
||||
import {
|
||||
addAppleCard,
|
||||
AppleCardAddRecord,
|
||||
updateAppleCard
|
||||
} from '@/api/apple-card-info';
|
||||
import useLoading from '@/hooks/loading';
|
||||
import { ref, watch } from 'vue';
|
||||
|
||||
const props = defineProps({
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
id: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
account: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
});
|
||||
const props = defineProps({
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
id: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
account: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
});
|
||||
|
||||
const generateFormData = () => {
|
||||
return {
|
||||
account: '',
|
||||
password: '',
|
||||
};
|
||||
const generateFormData = () => {
|
||||
return {
|
||||
account: '',
|
||||
password: ''
|
||||
};
|
||||
};
|
||||
|
||||
const formData = ref<AppleCardAddRecord>(generateFormData());
|
||||
const formData = ref<AppleCardAddRecord>(generateFormData());
|
||||
|
||||
const { loading, setLoading } = useLoading(false);
|
||||
const { loading, setLoading } = useLoading(false);
|
||||
|
||||
const emit = defineEmits(['update:visible']);
|
||||
const handleOk = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const emit = defineEmits(['update:visible']);
|
||||
const handleOk = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
|
||||
if (props.id === '') {
|
||||
await addAppleCard(formData.value);
|
||||
} else {
|
||||
await updateAppleCard({ id: props.id, ...formData.value });
|
||||
}
|
||||
emit('update:visible', false);
|
||||
formData.value = generateFormData();
|
||||
} finally {
|
||||
setLoading(false);
|
||||
if (props.id === '') {
|
||||
await addAppleCard(formData.value);
|
||||
} else {
|
||||
await updateAppleCard({ id: props.id, ...formData.value });
|
||||
}
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.account,
|
||||
(val) => {
|
||||
formData.value.account = val;
|
||||
}
|
||||
);
|
||||
const handleCancel = () => {
|
||||
emit('update:visible', false);
|
||||
formData.value = generateFormData();
|
||||
};
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.account,
|
||||
val => {
|
||||
formData.value.account = val;
|
||||
}
|
||||
);
|
||||
const handleCancel = () => {
|
||||
emit('update:visible', false);
|
||||
formData.value = generateFormData();
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -97,14 +97,16 @@
|
||||
size="small"
|
||||
status="warning"
|
||||
@click="showAddModel(record)"
|
||||
>修改</a-button
|
||||
>
|
||||
修改
|
||||
</a-button>
|
||||
<a-button
|
||||
size="small"
|
||||
status="danger"
|
||||
@click="deleteAppleCardHandler(record.id)"
|
||||
>删除</a-button
|
||||
>
|
||||
删除
|
||||
</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
</a-table>
|
||||
@@ -118,231 +120,231 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import useLoading from '@/hooks/loading';
|
||||
import { Pagination } from '@/types/global';
|
||||
import useLoading from '@/hooks/loading';
|
||||
import { Pagination } from '@/types/global';
|
||||
|
||||
import { TableColumnData } from '@arco-design/web-vue/es/table/interface';
|
||||
import { reactive, ref, watch } from 'vue';
|
||||
import {
|
||||
AppleCardParams,
|
||||
AppleCardRecordWithID,
|
||||
AppleCardResRecord,
|
||||
deleteAppleCard,
|
||||
downloadAppleAccountTemplteAPI,
|
||||
queryAppleCardList,
|
||||
} from '@/api/apple-card-info';
|
||||
import { FileItem, Notification } from '@arco-design/web-vue';
|
||||
import { getToken } from '@/utils/auth';
|
||||
import { getAPIBaseUrl } from '@/api/utils';
|
||||
import AddModal from './components/add-modal.vue';
|
||||
import { TableColumnData } from '@arco-design/web-vue/es/table/interface';
|
||||
import { reactive, ref, watch } from 'vue';
|
||||
import {
|
||||
AppleCardParams,
|
||||
AppleCardRecordWithID,
|
||||
AppleCardResRecord,
|
||||
deleteAppleCard,
|
||||
downloadAppleAccountTemplteAPI,
|
||||
queryAppleCardList
|
||||
} from '@/api/apple-card-info';
|
||||
import { FileItem, Notification } from '@arco-design/web-vue';
|
||||
import { getToken } from '@/utils/auth';
|
||||
import { getAPIBaseUrl } from '@/api/utils';
|
||||
import AddModal from './components/add-modal.vue';
|
||||
|
||||
const basePagination: Pagination = {
|
||||
current: 1,
|
||||
pageSize: 20,
|
||||
};
|
||||
const pagination = reactive({
|
||||
...basePagination,
|
||||
showPageSize: true,
|
||||
pageSizeOptions: [10, 20, 50, 100],
|
||||
});
|
||||
const columns: TableColumnData[] = [
|
||||
{
|
||||
title: '序号',
|
||||
dataIndex: 'index',
|
||||
slotName: 'index',
|
||||
},
|
||||
{
|
||||
title: '账号',
|
||||
dataIndex: 'account',
|
||||
},
|
||||
{
|
||||
title: '密码',
|
||||
dataIndex: 'password',
|
||||
},
|
||||
{
|
||||
title: '总充值金额',
|
||||
dataIndex: 'balance',
|
||||
ellipsis: true,
|
||||
tooltip: true,
|
||||
},
|
||||
{
|
||||
title: '今日充值金额',
|
||||
dataIndex: 'todayRechargeAmount',
|
||||
ellipsis: true,
|
||||
tooltip: true,
|
||||
},
|
||||
{
|
||||
title: '今日充值笔数',
|
||||
dataIndex: 'todayRechargeCount',
|
||||
ellipsis: true,
|
||||
tooltip: true,
|
||||
},
|
||||
{
|
||||
title: '最后充值时间',
|
||||
dataIndex: 'todayRechargeDatetime',
|
||||
ellipsis: true,
|
||||
tooltip: true,
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
dataIndex: 'createdAt',
|
||||
slotName: 'createdAt',
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'status',
|
||||
slotName: 'status',
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
dataIndex: 'operations',
|
||||
slotName: 'operations',
|
||||
},
|
||||
];
|
||||
const generateFormModel = () => {
|
||||
return {
|
||||
account: '',
|
||||
password: '',
|
||||
};
|
||||
};
|
||||
const { loading, setLoading } = useLoading(true);
|
||||
const renderData = ref<AppleCardResRecord[]>([]);
|
||||
const formModel = ref(generateFormModel());
|
||||
const state = reactive({
|
||||
addModalVisible: false,
|
||||
deployModalVisible: false,
|
||||
accountId: '',
|
||||
const basePagination: Pagination = {
|
||||
current: 1,
|
||||
pageSize: 20
|
||||
};
|
||||
const pagination = reactive({
|
||||
...basePagination,
|
||||
showPageSize: true,
|
||||
pageSizeOptions: [10, 20, 50, 100]
|
||||
});
|
||||
const columns: TableColumnData[] = [
|
||||
{
|
||||
title: '序号',
|
||||
dataIndex: 'index',
|
||||
slotName: 'index'
|
||||
},
|
||||
{
|
||||
title: '账号',
|
||||
dataIndex: 'account'
|
||||
},
|
||||
{
|
||||
title: '密码',
|
||||
dataIndex: 'password'
|
||||
},
|
||||
{
|
||||
title: '总充值金额',
|
||||
dataIndex: 'balance',
|
||||
ellipsis: true,
|
||||
tooltip: true
|
||||
},
|
||||
{
|
||||
title: '今日充值金额',
|
||||
dataIndex: 'todayRechargeAmount',
|
||||
ellipsis: true,
|
||||
tooltip: true
|
||||
},
|
||||
{
|
||||
title: '今日充值笔数',
|
||||
dataIndex: 'todayRechargeCount',
|
||||
ellipsis: true,
|
||||
tooltip: true
|
||||
},
|
||||
{
|
||||
title: '最后充值时间',
|
||||
dataIndex: 'todayRechargeDatetime',
|
||||
ellipsis: true,
|
||||
tooltip: true
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
dataIndex: 'createdAt',
|
||||
slotName: 'createdAt'
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'status',
|
||||
slotName: 'status'
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
dataIndex: 'operations',
|
||||
slotName: 'operations'
|
||||
}
|
||||
];
|
||||
const generateFormModel = () => {
|
||||
return {
|
||||
account: '',
|
||||
});
|
||||
password: ''
|
||||
};
|
||||
};
|
||||
const { loading, setLoading } = useLoading(true);
|
||||
const renderData = ref<AppleCardResRecord[]>([]);
|
||||
const formModel = ref(generateFormModel());
|
||||
const state = reactive({
|
||||
addModalVisible: false,
|
||||
deployModalVisible: false,
|
||||
accountId: '',
|
||||
account: ''
|
||||
});
|
||||
|
||||
const fetchData = async (
|
||||
params: AppleCardParams = { current: 1, pageSize: 20 }
|
||||
) => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const {
|
||||
data: { list, total },
|
||||
} = await queryAppleCardList(params);
|
||||
renderData.value = list;
|
||||
pagination.current = params.current;
|
||||
pagination.pageSize = params.pageSize;
|
||||
pagination.total = total;
|
||||
} catch (err) {
|
||||
// you can report use errorHandler or other
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
const onPageChange = (current: number) => {
|
||||
// fetchData({ ...basePagination, current });
|
||||
fetchData({ ...pagination, current });
|
||||
};
|
||||
const onPageSizeChange = (pageSize: number) => {
|
||||
fetchData({ ...pagination, pageSize });
|
||||
};
|
||||
const search = () => {
|
||||
fetchData({
|
||||
...basePagination,
|
||||
...formModel.value,
|
||||
} as unknown as AppleCardParams);
|
||||
};
|
||||
const reset = () => {
|
||||
formModel.value = generateFormModel();
|
||||
};
|
||||
const fetchData = async (
|
||||
params: AppleCardParams = { current: 1, pageSize: 20 }
|
||||
) => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const {
|
||||
data: { list, total }
|
||||
} = await queryAppleCardList(params);
|
||||
renderData.value = list;
|
||||
pagination.current = params.current;
|
||||
pagination.pageSize = params.pageSize;
|
||||
pagination.total = total;
|
||||
} catch (err) {
|
||||
// you can report use errorHandler or other
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
const onPageChange = (current: number) => {
|
||||
// fetchData({ ...basePagination, current });
|
||||
fetchData({ ...pagination, current });
|
||||
};
|
||||
const onPageSizeChange = (pageSize: number) => {
|
||||
fetchData({ ...pagination, pageSize });
|
||||
};
|
||||
const search = () => {
|
||||
fetchData({
|
||||
...basePagination,
|
||||
...formModel.value
|
||||
} as unknown as AppleCardParams);
|
||||
};
|
||||
const reset = () => {
|
||||
formModel.value = generateFormModel();
|
||||
};
|
||||
|
||||
const deleteAppleCardHandler = async (id: string) => {
|
||||
try {
|
||||
await deleteAppleCard({ id });
|
||||
} catch {
|
||||
Notification.error({
|
||||
id: 'appleAccountNotice',
|
||||
content: '删除苹果卡失败',
|
||||
closable: true,
|
||||
});
|
||||
} finally {
|
||||
fetchData({ ...pagination });
|
||||
}
|
||||
};
|
||||
|
||||
const showAddModel = (record: AppleCardRecordWithID) => {
|
||||
state.addModalVisible = true;
|
||||
state.accountId = record.id;
|
||||
state.account = record.account;
|
||||
};
|
||||
watch(
|
||||
() => state.addModalVisible,
|
||||
(val) => {
|
||||
if (!val) {
|
||||
search();
|
||||
}
|
||||
}
|
||||
);
|
||||
const getStatusColorMap = (status: number) => {
|
||||
type colorMap = {
|
||||
color: string;
|
||||
text: string;
|
||||
};
|
||||
let c: colorMap = {
|
||||
color: '',
|
||||
text: '',
|
||||
};
|
||||
|
||||
switch (status) {
|
||||
case 1:
|
||||
c = {
|
||||
color: 'red',
|
||||
text: '禁用',
|
||||
};
|
||||
break;
|
||||
case 2:
|
||||
c = {
|
||||
color: 'green',
|
||||
text: '正常',
|
||||
};
|
||||
break;
|
||||
case 3:
|
||||
c = {
|
||||
color: 'red',
|
||||
text: '账/密错误',
|
||||
};
|
||||
break;
|
||||
case 4:
|
||||
c = {
|
||||
color: 'orange',
|
||||
text: '额度超出',
|
||||
};
|
||||
break;
|
||||
case 5:
|
||||
c = {
|
||||
color: 'pinkpurple',
|
||||
text: '禁用(50元限制)',
|
||||
};
|
||||
break;
|
||||
default:
|
||||
c = {
|
||||
color: 'grey',
|
||||
text: '其他状态,待更新',
|
||||
};
|
||||
break;
|
||||
}
|
||||
return c;
|
||||
};
|
||||
const downloadAccountTemplte = () => {
|
||||
downloadAppleAccountTemplteAPI();
|
||||
};
|
||||
const uploadSucceed = (fileItem: FileItem) => {
|
||||
Notification.success({
|
||||
const deleteAppleCardHandler = async (id: string) => {
|
||||
try {
|
||||
await deleteAppleCard({ id });
|
||||
} catch {
|
||||
Notification.error({
|
||||
id: 'appleAccountNotice',
|
||||
content: fileItem.response?.data?.msg || '上传成功',
|
||||
closable: true,
|
||||
content: '删除苹果卡失败',
|
||||
closable: true
|
||||
});
|
||||
search();
|
||||
} finally {
|
||||
fetchData({ ...pagination });
|
||||
}
|
||||
};
|
||||
|
||||
const showAddModel = (record: AppleCardRecordWithID) => {
|
||||
state.addModalVisible = true;
|
||||
state.accountId = record.id;
|
||||
state.account = record.account;
|
||||
};
|
||||
watch(
|
||||
() => state.addModalVisible,
|
||||
val => {
|
||||
if (!val) {
|
||||
search();
|
||||
}
|
||||
}
|
||||
);
|
||||
const getStatusColorMap = (status: number) => {
|
||||
type colorMap = {
|
||||
color: string;
|
||||
text: string;
|
||||
};
|
||||
fetchData();
|
||||
let c: colorMap = {
|
||||
color: '',
|
||||
text: ''
|
||||
};
|
||||
|
||||
switch (status) {
|
||||
case 1:
|
||||
c = {
|
||||
color: 'red',
|
||||
text: '禁用'
|
||||
};
|
||||
break;
|
||||
case 2:
|
||||
c = {
|
||||
color: 'green',
|
||||
text: '正常'
|
||||
};
|
||||
break;
|
||||
case 3:
|
||||
c = {
|
||||
color: 'red',
|
||||
text: '账/密错误'
|
||||
};
|
||||
break;
|
||||
case 4:
|
||||
c = {
|
||||
color: 'orange',
|
||||
text: '额度超出'
|
||||
};
|
||||
break;
|
||||
case 5:
|
||||
c = {
|
||||
color: 'pinkpurple',
|
||||
text: '禁用(50元限制)'
|
||||
};
|
||||
break;
|
||||
default:
|
||||
c = {
|
||||
color: 'grey',
|
||||
text: '其他状态,待更新'
|
||||
};
|
||||
break;
|
||||
}
|
||||
return c;
|
||||
};
|
||||
const downloadAccountTemplte = () => {
|
||||
downloadAppleAccountTemplteAPI();
|
||||
};
|
||||
const uploadSucceed = (fileItem: FileItem) => {
|
||||
Notification.success({
|
||||
id: 'appleAccountNotice',
|
||||
content: fileItem.response?.data?.msg || '上传成功',
|
||||
closable: true
|
||||
});
|
||||
search();
|
||||
};
|
||||
fetchData();
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.container {
|
||||
padding: 0 20px 20px;
|
||||
}
|
||||
.container {
|
||||
padding: 0 20px 20px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -17,92 +17,89 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { Pagination } from '@/types/global';
|
||||
import { reactive, ref } from 'vue';
|
||||
import { TableColumnData } from '@arco-design/web-vue/es/table/interface';
|
||||
import {
|
||||
OrderSummaryRecord,
|
||||
queryOrderSummaryList,
|
||||
} from '@/api/order-summary';
|
||||
import useLoading from '@/hooks/loading';
|
||||
import { Pagination } from '@/types/global';
|
||||
import { reactive, ref } from 'vue';
|
||||
import { TableColumnData } from '@arco-design/web-vue/es/table/interface';
|
||||
import { OrderSummaryRecord, queryOrderSummaryList } from '@/api/order-summary';
|
||||
import useLoading from '@/hooks/loading';
|
||||
|
||||
const basePagination: Pagination = {
|
||||
current: 1,
|
||||
pageSize: 20,
|
||||
};
|
||||
const columns: TableColumnData[] = [
|
||||
{
|
||||
title: '序号',
|
||||
dataIndex: 'index',
|
||||
slotName: 'index',
|
||||
},
|
||||
{
|
||||
title: '商户ID',
|
||||
dataIndex: 'merchantUid',
|
||||
},
|
||||
{
|
||||
title: '商户名称',
|
||||
dataIndex: 'merchantName',
|
||||
},
|
||||
{
|
||||
title: '成功金额(面额)',
|
||||
dataIndex: 'showSucceedAmount',
|
||||
},
|
||||
{
|
||||
title: '成功金额(实际)',
|
||||
dataIndex: 'factSucceedAmount',
|
||||
},
|
||||
{
|
||||
title: '成交订单数量',
|
||||
dataIndex: 'showSucceedCount',
|
||||
},
|
||||
{
|
||||
title: '总额(面额)',
|
||||
dataIndex: 'showTotalAmount',
|
||||
},
|
||||
{
|
||||
title: '总额(实际)',
|
||||
dataIndex: 'factTotalAmount',
|
||||
},
|
||||
{
|
||||
title: '订单总数',
|
||||
dataIndex: 'showTotalCount',
|
||||
},
|
||||
{
|
||||
title: '成功率',
|
||||
dataIndex: 'showSucceedRate',
|
||||
},
|
||||
{
|
||||
title: '日期',
|
||||
dataIndex: 'date',
|
||||
},
|
||||
];
|
||||
const { loading, setLoading } = useLoading(true);
|
||||
const pagination = reactive({ ...basePagination });
|
||||
const renderData = ref<OrderSummaryRecord[]>([]);
|
||||
const basePagination: Pagination = {
|
||||
current: 1,
|
||||
pageSize: 20
|
||||
};
|
||||
const columns: TableColumnData[] = [
|
||||
{
|
||||
title: '序号',
|
||||
dataIndex: 'index',
|
||||
slotName: 'index'
|
||||
},
|
||||
{
|
||||
title: '商户ID',
|
||||
dataIndex: 'merchantUid'
|
||||
},
|
||||
{
|
||||
title: '商户名称',
|
||||
dataIndex: 'merchantName'
|
||||
},
|
||||
{
|
||||
title: '成功金额(面额)',
|
||||
dataIndex: 'showSucceedAmount'
|
||||
},
|
||||
{
|
||||
title: '成功金额(实际)',
|
||||
dataIndex: 'factSucceedAmount'
|
||||
},
|
||||
{
|
||||
title: '成交订单数量',
|
||||
dataIndex: 'showSucceedCount'
|
||||
},
|
||||
{
|
||||
title: '总额(面额)',
|
||||
dataIndex: 'showTotalAmount'
|
||||
},
|
||||
{
|
||||
title: '总额(实际)',
|
||||
dataIndex: 'factTotalAmount'
|
||||
},
|
||||
{
|
||||
title: '订单总数',
|
||||
dataIndex: 'showTotalCount'
|
||||
},
|
||||
{
|
||||
title: '成功率',
|
||||
dataIndex: 'showSucceedRate'
|
||||
},
|
||||
{
|
||||
title: '日期',
|
||||
dataIndex: 'date'
|
||||
}
|
||||
];
|
||||
const { loading, setLoading } = useLoading(true);
|
||||
const pagination = reactive({ ...basePagination });
|
||||
const renderData = ref<OrderSummaryRecord[]>([]);
|
||||
|
||||
const fetchData = async (
|
||||
params: { current: number; pageSize: number } = basePagination
|
||||
) => {
|
||||
setLoading(true);
|
||||
const {
|
||||
data: { list, total },
|
||||
} = await queryOrderSummaryList({
|
||||
...pagination,
|
||||
});
|
||||
renderData.value = list;
|
||||
pagination.current = params.current;
|
||||
pagination.pageSize = params.pageSize;
|
||||
pagination.total = total;
|
||||
setLoading(false);
|
||||
};
|
||||
const onPageChange = (current: number) => {
|
||||
fetchData({ ...pagination, current });
|
||||
};
|
||||
const onPageSizeChange = (pageSize: number) => {
|
||||
fetchData({ ...pagination, pageSize });
|
||||
};
|
||||
fetchData();
|
||||
const fetchData = async (
|
||||
params: { current: number; pageSize: number } = basePagination
|
||||
) => {
|
||||
setLoading(true);
|
||||
const {
|
||||
data: { list, total }
|
||||
} = await queryOrderSummaryList({
|
||||
...pagination
|
||||
});
|
||||
renderData.value = list;
|
||||
pagination.current = params.current;
|
||||
pagination.pageSize = params.pageSize;
|
||||
pagination.total = total;
|
||||
setLoading(false);
|
||||
};
|
||||
const onPageChange = (current: number) => {
|
||||
fetchData({ ...pagination, current });
|
||||
};
|
||||
const onPageSizeChange = (pageSize: number) => {
|
||||
fetchData({ ...pagination, pageSize });
|
||||
};
|
||||
fetchData();
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped></style>
|
||||
|
||||
@@ -17,44 +17,44 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { modityActualAmountAPI } from '@/api/apple-card-recharge-history';
|
||||
import { reactive, watch } from 'vue';
|
||||
import { modityActualAmountAPI } from '@/api/apple-card-recharge-history';
|
||||
import { reactive, watch } from 'vue';
|
||||
|
||||
const props = defineProps<{
|
||||
visible: boolean;
|
||||
orderNo: string;
|
||||
}>();
|
||||
const emits = defineEmits(['update:visible']);
|
||||
const props = defineProps<{
|
||||
visible: boolean;
|
||||
orderNo: string;
|
||||
}>();
|
||||
const emits = defineEmits(['update:visible']);
|
||||
|
||||
const state = reactive({
|
||||
visible: false,
|
||||
});
|
||||
const state = reactive({
|
||||
visible: false
|
||||
});
|
||||
|
||||
const formData = reactive({
|
||||
actualAmount: 0,
|
||||
});
|
||||
const formData = reactive({
|
||||
actualAmount: 0
|
||||
});
|
||||
|
||||
const handleCancel = () => {
|
||||
const handleCancel = () => {
|
||||
emits('update:visible', false);
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.visible,
|
||||
newValue => {
|
||||
state.visible = newValue;
|
||||
}
|
||||
);
|
||||
|
||||
const handleOk = async (): Promise<boolean> => {
|
||||
try {
|
||||
await modityActualAmountAPI({
|
||||
orderNo: props.orderNo,
|
||||
actualAmount: formData.actualAmount
|
||||
});
|
||||
emits('update:visible', false);
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.visible,
|
||||
(newValue) => {
|
||||
state.visible = newValue;
|
||||
}
|
||||
);
|
||||
|
||||
const handleOk = async (): Promise<boolean> => {
|
||||
try {
|
||||
await modityActualAmountAPI({
|
||||
orderNo: props.orderNo,
|
||||
actualAmount: formData.actualAmount,
|
||||
});
|
||||
emits('update:visible', false);
|
||||
return true;
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
return true;
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -33,71 +33,71 @@
|
||||
</a-timeline-item>
|
||||
</a-timeline>
|
||||
</div>
|
||||
<div v-else> 404 </div>
|
||||
<div v-else>404</div>
|
||||
</a-drawer>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
AppleCardOperationHistoryList,
|
||||
queryAppleCardOperationHistoryList,
|
||||
} from '@/api/apple-card-recharge-history';
|
||||
import { reactive, ref, watch } from 'vue';
|
||||
import {
|
||||
AppleCardOperationHistoryList,
|
||||
queryAppleCardOperationHistoryList
|
||||
} from '@/api/apple-card-recharge-history';
|
||||
import { reactive, ref, watch } from 'vue';
|
||||
|
||||
const props = defineProps({
|
||||
orderNo: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
const props = defineProps({
|
||||
orderNo: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
});
|
||||
|
||||
const emit = defineEmits(['update:visible']);
|
||||
|
||||
const state = reactive<{ isReverse: boolean; visible: boolean }>({
|
||||
isReverse: true,
|
||||
visible: false
|
||||
});
|
||||
|
||||
const renderData = ref<AppleCardOperationHistoryList[]>([]);
|
||||
|
||||
const getAppleCardOperationHistoryList = async () => {
|
||||
const res = await queryAppleCardOperationHistoryList({
|
||||
orderNo: props.orderNo
|
||||
});
|
||||
renderData.value = res.data.list;
|
||||
};
|
||||
|
||||
const emit = defineEmits(['update:visible']);
|
||||
watch(
|
||||
() => state.visible,
|
||||
newValue => {
|
||||
emit('update:visible', newValue);
|
||||
}
|
||||
);
|
||||
|
||||
const state = reactive<{ isReverse: boolean; visible: boolean }>({
|
||||
isReverse: true,
|
||||
visible: false,
|
||||
});
|
||||
|
||||
const renderData = ref<AppleCardOperationHistoryList[]>([]);
|
||||
|
||||
const getAppleCardOperationHistoryList = async () => {
|
||||
const res = await queryAppleCardOperationHistoryList({
|
||||
orderNo: props.orderNo,
|
||||
});
|
||||
renderData.value = res.data.list;
|
||||
};
|
||||
|
||||
watch(
|
||||
() => state.visible,
|
||||
(newValue) => {
|
||||
emit('update:visible', newValue);
|
||||
watch(
|
||||
() => props.visible,
|
||||
newValue => {
|
||||
state.visible = newValue;
|
||||
if (newValue) {
|
||||
getAppleCardOperationHistoryList();
|
||||
} else {
|
||||
renderData.value = [];
|
||||
}
|
||||
);
|
||||
|
||||
watch(
|
||||
() => props.visible,
|
||||
(newValue) => {
|
||||
state.visible = newValue;
|
||||
if (newValue) {
|
||||
getAppleCardOperationHistoryList();
|
||||
} else {
|
||||
renderData.value = [];
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.sort-btn {
|
||||
float: right;
|
||||
}
|
||||
.sort-btn {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.remark {
|
||||
font-weight: 300;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
.remark {
|
||||
font-size: 0.8rem;
|
||||
font-weight: 300;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -97,16 +97,17 @@
|
||||
</template>
|
||||
<template #operations="{ record }">
|
||||
<a-space size="small">
|
||||
<a-button size="small" @click="showRechargeHistory(record.orderNo)"
|
||||
>日志</a-button
|
||||
>
|
||||
<a-button size="small" @click="showRechargeHistory(record.orderNo)">
|
||||
日志
|
||||
</a-button>
|
||||
<a-button
|
||||
v-if="record.status === 0 || record.status === 1"
|
||||
size="small"
|
||||
status="warning"
|
||||
@click="callBackOrderManual(record.orderNo)"
|
||||
>手动回调</a-button
|
||||
>
|
||||
手动回调
|
||||
</a-button>
|
||||
<!-- <a-button
|
||||
v-if="record.status === 15"
|
||||
size="small"
|
||||
@@ -121,7 +122,7 @@
|
||||
type="warning"
|
||||
@ok="modifyStatusToSucceed(record.orderNo)"
|
||||
>
|
||||
<a-button size="small" status="danger"> 修正状态 </a-button>
|
||||
<a-button size="small" status="danger">修正状态</a-button>
|
||||
</a-popconfirm>
|
||||
</a-space>
|
||||
</template>
|
||||
@@ -139,236 +140,235 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { Notification } from '@arco-design/web-vue';
|
||||
import useLoading from '@/hooks/loading';
|
||||
import { Pagination } from '@/types/global';
|
||||
import { TableColumnData } from '@arco-design/web-vue/es/table/interface';
|
||||
import { reactive, ref, watch } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import {
|
||||
AppleCardRechargeOrderParams,
|
||||
AppleCardRechargeOrderRecord,
|
||||
callBackOrderManualAPI,
|
||||
modifyStatusToSucceedAPI,
|
||||
queryAppleCardRechargeOrderList,
|
||||
} from '@/api/apple-card-recharge-history';
|
||||
import RechargeHistory from './components/recharge-history.vue';
|
||||
import ChangeAmount from './components/change-amount.vue';
|
||||
import { Notification } from '@arco-design/web-vue';
|
||||
import useLoading from '@/hooks/loading';
|
||||
import { Pagination } from '@/types/global';
|
||||
import { reactive, ref, watch } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import {
|
||||
AppleCardRechargeOrderParams,
|
||||
AppleCardRechargeOrderRecord,
|
||||
callBackOrderManualAPI,
|
||||
modifyStatusToSucceedAPI,
|
||||
queryAppleCardRechargeOrderList
|
||||
} from '@/api/apple-card-recharge-history';
|
||||
import RechargeHistory from './components/recharge-history.vue';
|
||||
import ChangeAmount from './components/change-amount.vue';
|
||||
|
||||
const route = useRoute();
|
||||
const { accountID } = route.params;
|
||||
const route = useRoute();
|
||||
const { accountID } = route.params;
|
||||
|
||||
const basePagination: Pagination = {
|
||||
const basePagination: Pagination = {
|
||||
current: 1,
|
||||
pageSize: 20
|
||||
};
|
||||
const pagination = reactive({
|
||||
accountID: accountID as string,
|
||||
...basePagination,
|
||||
// show-page-size
|
||||
showPageSize: true,
|
||||
pageSizeOptions: [10, 20, 50, 100]
|
||||
});
|
||||
const columns: TableColumnData[] = [
|
||||
{
|
||||
title: '序号',
|
||||
dataIndex: 'index',
|
||||
slotName: 'index'
|
||||
},
|
||||
{
|
||||
title: '订单号',
|
||||
dataIndex: 'orderNo',
|
||||
slotName: 'orderNo'
|
||||
},
|
||||
{
|
||||
title: '商户订单号',
|
||||
dataIndex: 'attach'
|
||||
},
|
||||
{
|
||||
title: '分配账号',
|
||||
dataIndex: 'accountName',
|
||||
slotName: 'account'
|
||||
},
|
||||
{
|
||||
title: '卡密',
|
||||
dataIndex: 'cardPass',
|
||||
slotName: 'cardPass'
|
||||
},
|
||||
{
|
||||
title: '卡面金额',
|
||||
dataIndex: 'cardAmount',
|
||||
slotName: 'cardAmount'
|
||||
},
|
||||
{
|
||||
title: '支付金额',
|
||||
dataIndex: 'actualAmount',
|
||||
slotName: 'actualAmount'
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
dataIndex: 'createdAt',
|
||||
slotName: 'createdAt'
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'status',
|
||||
slotName: 'status'
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
dataIndex: 'operations',
|
||||
slotName: 'operations'
|
||||
}
|
||||
];
|
||||
const generateFormModel = () => {
|
||||
return {
|
||||
account: '',
|
||||
orderNo: '',
|
||||
attach: '',
|
||||
cardNo: '',
|
||||
cardPass: ''
|
||||
};
|
||||
};
|
||||
const { loading, setLoading } = useLoading(true);
|
||||
const renderData = ref<AppleCardRechargeOrderRecord[]>([]);
|
||||
const formModel = ref(generateFormModel());
|
||||
const state = reactive({
|
||||
rechargeHistoryModalVisible: false,
|
||||
selectedOrderNo: '',
|
||||
changeAmountModalVisible: false
|
||||
});
|
||||
|
||||
const fetchData = async (
|
||||
params: AppleCardRechargeOrderParams = {
|
||||
current: 1,
|
||||
pageSize: 20,
|
||||
};
|
||||
const pagination = reactive({
|
||||
accountID: accountID as string,
|
||||
pageSize: 20
|
||||
// Id: accountID as string,
|
||||
}
|
||||
) => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const {
|
||||
data: { list, total }
|
||||
} = await queryAppleCardRechargeOrderList(params);
|
||||
renderData.value = list;
|
||||
pagination.current = params.current;
|
||||
pagination.pageSize = params.pageSize;
|
||||
pagination.total = total;
|
||||
} catch (err) {
|
||||
// you can report use errorHandler or other
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const onPageChange = (current: number) => {
|
||||
fetchData({ ...pagination, current });
|
||||
};
|
||||
const onPageSizeChange = (pageSize: number) => {
|
||||
fetchData({ ...pagination, pageSize });
|
||||
};
|
||||
const search = () => {
|
||||
fetchData({
|
||||
...basePagination,
|
||||
// show-page-size
|
||||
showPageSize: true,
|
||||
pageSizeOptions: [10, 20, 50, 100],
|
||||
});
|
||||
const columns: TableColumnData[] = [
|
||||
{
|
||||
title: '序号',
|
||||
dataIndex: 'index',
|
||||
slotName: 'index',
|
||||
},
|
||||
{
|
||||
title: '订单号',
|
||||
dataIndex: 'orderNo',
|
||||
slotName: 'orderNo',
|
||||
},
|
||||
{
|
||||
title: '商户订单号',
|
||||
dataIndex: 'attach',
|
||||
},
|
||||
{
|
||||
title: '分配账号',
|
||||
dataIndex: 'accountName',
|
||||
slotName: 'account',
|
||||
},
|
||||
{
|
||||
title: '卡密',
|
||||
dataIndex: 'cardPass',
|
||||
slotName: 'cardPass',
|
||||
},
|
||||
{
|
||||
title: '卡面金额',
|
||||
dataIndex: 'cardAmount',
|
||||
slotName: 'cardAmount',
|
||||
},
|
||||
{
|
||||
title: '支付金额',
|
||||
dataIndex: 'actualAmount',
|
||||
slotName: 'actualAmount',
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
dataIndex: 'createdAt',
|
||||
slotName: 'createdAt',
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'status',
|
||||
slotName: 'status',
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
dataIndex: 'operations',
|
||||
slotName: 'operations',
|
||||
},
|
||||
];
|
||||
const generateFormModel = () => {
|
||||
return {
|
||||
account: '',
|
||||
orderNo: '',
|
||||
attach: '',
|
||||
cardNo: '',
|
||||
cardPass: '',
|
||||
};
|
||||
...formModel.value
|
||||
} as unknown as AppleCardRechargeOrderParams);
|
||||
};
|
||||
const reset = () => {
|
||||
formModel.value = generateFormModel();
|
||||
};
|
||||
const showRechargeHistory = (orderNo: string) => {
|
||||
state.rechargeHistoryModalVisible = true;
|
||||
state.selectedOrderNo = orderNo;
|
||||
};
|
||||
const getStatusColorMap = (status: number) => {
|
||||
type colorMap = {
|
||||
color: string;
|
||||
text: string;
|
||||
};
|
||||
const { loading, setLoading } = useLoading(true);
|
||||
const renderData = ref<AppleCardRechargeOrderRecord[]>([]);
|
||||
const formModel = ref(generateFormModel());
|
||||
const state = reactive({
|
||||
rechargeHistoryModalVisible: false,
|
||||
selectedOrderNo: '',
|
||||
changeAmountModalVisible: false,
|
||||
});
|
||||
|
||||
const fetchData = async (
|
||||
params: AppleCardRechargeOrderParams = {
|
||||
current: 1,
|
||||
pageSize: 20,
|
||||
// Id: accountID as string,
|
||||
}
|
||||
) => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const {
|
||||
data: { list, total },
|
||||
} = await queryAppleCardRechargeOrderList(params);
|
||||
renderData.value = list;
|
||||
pagination.current = params.current;
|
||||
pagination.pageSize = params.pageSize;
|
||||
pagination.total = total;
|
||||
} catch (err) {
|
||||
// you can report use errorHandler or other
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
let c: colorMap = {
|
||||
color: '',
|
||||
text: ''
|
||||
};
|
||||
|
||||
const onPageChange = (current: number) => {
|
||||
fetchData({ ...pagination, current });
|
||||
};
|
||||
const onPageSizeChange = (pageSize: number) => {
|
||||
fetchData({ ...pagination, pageSize });
|
||||
};
|
||||
const search = () => {
|
||||
fetchData({
|
||||
...basePagination,
|
||||
...formModel.value,
|
||||
} as unknown as AppleCardRechargeOrderParams);
|
||||
};
|
||||
const reset = () => {
|
||||
formModel.value = generateFormModel();
|
||||
};
|
||||
const showRechargeHistory = (orderNo: string) => {
|
||||
state.rechargeHistoryModalVisible = true;
|
||||
state.selectedOrderNo = orderNo;
|
||||
};
|
||||
const getStatusColorMap = (status: number) => {
|
||||
type colorMap = {
|
||||
color: string;
|
||||
text: string;
|
||||
};
|
||||
let c: colorMap = {
|
||||
color: '',
|
||||
text: '',
|
||||
};
|
||||
|
||||
switch (status) {
|
||||
case 0:
|
||||
c = {
|
||||
color: 'red',
|
||||
text: '失败',
|
||||
};
|
||||
break;
|
||||
case 1:
|
||||
c = {
|
||||
color: 'green',
|
||||
text: '成功',
|
||||
};
|
||||
break;
|
||||
case 15:
|
||||
c = {
|
||||
color: 'red',
|
||||
text: '失败(金额异议)',
|
||||
};
|
||||
break;
|
||||
case 14:
|
||||
c = {
|
||||
color: 'blue',
|
||||
text: '卡密重复(需人工介入)',
|
||||
};
|
||||
break;
|
||||
case 2:
|
||||
c = {
|
||||
color: 'orange',
|
||||
text: '调度中',
|
||||
};
|
||||
break;
|
||||
case 3:
|
||||
c = {
|
||||
color: 'blue',
|
||||
text: '等待',
|
||||
};
|
||||
break;
|
||||
default:
|
||||
c = {
|
||||
color: 'grey',
|
||||
text: '其他状态,待更新',
|
||||
};
|
||||
break;
|
||||
}
|
||||
return c;
|
||||
};
|
||||
const callBackOrderManual = async (orderNo: string) => {
|
||||
await callBackOrderManualAPI({ orderNo });
|
||||
fetchData({ ...pagination });
|
||||
Notification.info({
|
||||
title: '回调成功',
|
||||
content: '回调成功',
|
||||
});
|
||||
};
|
||||
switch (status) {
|
||||
case 0:
|
||||
c = {
|
||||
color: 'red',
|
||||
text: '失败'
|
||||
};
|
||||
break;
|
||||
case 1:
|
||||
c = {
|
||||
color: 'green',
|
||||
text: '成功'
|
||||
};
|
||||
break;
|
||||
case 15:
|
||||
c = {
|
||||
color: 'red',
|
||||
text: '失败(金额异议)'
|
||||
};
|
||||
break;
|
||||
case 14:
|
||||
c = {
|
||||
color: 'blue',
|
||||
text: '卡密重复(需人工介入)'
|
||||
};
|
||||
break;
|
||||
case 2:
|
||||
c = {
|
||||
color: 'orange',
|
||||
text: '调度中'
|
||||
};
|
||||
break;
|
||||
case 3:
|
||||
c = {
|
||||
color: 'blue',
|
||||
text: '等待'
|
||||
};
|
||||
break;
|
||||
default:
|
||||
c = {
|
||||
color: 'grey',
|
||||
text: '其他状态,待更新'
|
||||
};
|
||||
break;
|
||||
}
|
||||
return c;
|
||||
};
|
||||
const callBackOrderManual = async (orderNo: string) => {
|
||||
await callBackOrderManualAPI({ orderNo });
|
||||
fetchData({ ...pagination });
|
||||
const showActualAmountModal = (orderNo: string) => {
|
||||
state.changeAmountModalVisible = true;
|
||||
state.selectedOrderNo = orderNo;
|
||||
};
|
||||
const modifyStatusToSucceed = async (orderNo: string) => {
|
||||
await modifyStatusToSucceedAPI({ orderNo });
|
||||
fetchData({ ...pagination });
|
||||
Notification.info({
|
||||
title: '处理成功',
|
||||
content: '处理成功',
|
||||
});
|
||||
};
|
||||
watch(
|
||||
() => state.changeAmountModalVisible,
|
||||
(newValue) => {
|
||||
if (newValue === false) {
|
||||
search();
|
||||
}
|
||||
Notification.info({
|
||||
title: '回调成功',
|
||||
content: '回调成功'
|
||||
});
|
||||
};
|
||||
fetchData({ ...pagination });
|
||||
const showActualAmountModal = (orderNo: string) => {
|
||||
state.changeAmountModalVisible = true;
|
||||
state.selectedOrderNo = orderNo;
|
||||
};
|
||||
const modifyStatusToSucceed = async (orderNo: string) => {
|
||||
await modifyStatusToSucceedAPI({ orderNo });
|
||||
fetchData({ ...pagination });
|
||||
Notification.info({
|
||||
title: '处理成功',
|
||||
content: '处理成功'
|
||||
});
|
||||
};
|
||||
watch(
|
||||
() => state.changeAmountModalVisible,
|
||||
newValue => {
|
||||
if (newValue === false) {
|
||||
search();
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.container {
|
||||
padding: 0 20px 20px;
|
||||
}
|
||||
.container {
|
||||
padding: 0 20px 20px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -20,53 +20,53 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
const list = [
|
||||
{
|
||||
type: 'orangered',
|
||||
label: '活动',
|
||||
content: '内容最新优惠活动',
|
||||
},
|
||||
{
|
||||
type: 'cyan',
|
||||
label: '消息',
|
||||
content: '新增内容尚未通过审核,详情请点击查看。',
|
||||
},
|
||||
{
|
||||
type: 'blue',
|
||||
label: '通知',
|
||||
content: '当前产品试用期即将结束,如需续费请点击查看。',
|
||||
},
|
||||
{
|
||||
type: 'blue',
|
||||
label: '通知',
|
||||
content: '1月新系统升级计划通知',
|
||||
},
|
||||
{
|
||||
type: 'cyan',
|
||||
label: '消息',
|
||||
content: '新增内容已经通过审核,详情请点击查看。',
|
||||
},
|
||||
];
|
||||
const list = [
|
||||
{
|
||||
type: 'orangered',
|
||||
label: '活动',
|
||||
content: '内容最新优惠活动'
|
||||
},
|
||||
{
|
||||
type: 'cyan',
|
||||
label: '消息',
|
||||
content: '新增内容尚未通过审核,详情请点击查看。'
|
||||
},
|
||||
{
|
||||
type: 'blue',
|
||||
label: '通知',
|
||||
content: '当前产品试用期即将结束,如需续费请点击查看。'
|
||||
},
|
||||
{
|
||||
type: 'blue',
|
||||
label: '通知',
|
||||
content: '1月新系统升级计划通知'
|
||||
},
|
||||
{
|
||||
type: 'cyan',
|
||||
label: '消息',
|
||||
content: '新增内容已经通过审核,详情请点击查看。'
|
||||
}
|
||||
];
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
height: 24px;
|
||||
margin-bottom: 4px;
|
||||
.item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
height: 24px;
|
||||
margin-bottom: 4px;
|
||||
|
||||
.item-content {
|
||||
flex: 1;
|
||||
margin-left: 4px;
|
||||
overflow: hidden;
|
||||
color: var(--color-text-2);
|
||||
font-size: 13px;
|
||||
white-space: nowrap;
|
||||
text-decoration: none;
|
||||
text-overflow: ellipsis;
|
||||
cursor: pointer;
|
||||
}
|
||||
.item-content {
|
||||
flex: 1;
|
||||
margin-left: 4px;
|
||||
overflow: hidden;
|
||||
font-size: 13px;
|
||||
color: var(--color-text-2);
|
||||
text-decoration: none;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -10,26 +10,26 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed } from 'vue';
|
||||
import { useUserStore } from '@/store';
|
||||
import { computed } from 'vue';
|
||||
import { useUserStore } from '@/store';
|
||||
|
||||
const userStore = useUserStore();
|
||||
const userInfo = computed(() => {
|
||||
return {
|
||||
name: userStore.name,
|
||||
};
|
||||
});
|
||||
const userStore = useUserStore();
|
||||
const userInfo = computed(() => {
|
||||
return {
|
||||
name: userStore.name
|
||||
};
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.banner {
|
||||
width: 100%;
|
||||
padding: 20px 20px 0;
|
||||
background-color: var(--color-bg-2);
|
||||
border-radius: 4px 4px 0 0;
|
||||
}
|
||||
.banner {
|
||||
width: 100%;
|
||||
padding: 20px 20px 0;
|
||||
background-color: var(--color-bg-2);
|
||||
border-radius: 4px 4px 0 0;
|
||||
}
|
||||
|
||||
:deep(.arco-icon-home) {
|
||||
margin-right: 6px;
|
||||
}
|
||||
:deep(.arco-icon-home) {
|
||||
margin-right: 6px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
indicator-type="slider"
|
||||
show-arrow="hover"
|
||||
auto-play
|
||||
style="width: 100%; height: 170px; border-radius: 4px; overflow: hidden"
|
||||
style="width: 100%; height: 170px; overflow: hidden; border-radius: 4px"
|
||||
>
|
||||
<a-carousel-item v-for="(src, idx) in imageSrc" :key="idx">
|
||||
<div>
|
||||
@@ -14,11 +14,11 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
const imageSrc = [
|
||||
'//p3-armor.byteimg.com/tos-cn-i-49unhts6dw/5cc3cd1d994b7ef9db6a1f619a22addd.jpg~tplv-49unhts6dw-image.image',
|
||||
'//p3-armor.byteimg.com/tos-cn-i-49unhts6dw/f256cbcc287139e191fecea9d255a1f0.jpg~tplv-49unhts6dw-image.image',
|
||||
'//p3-armor.byteimg.com/tos-cn-i-49unhts6dw/b557ff0cd44146a2e471b477af2f30d0.jpg~tplv-49unhts6dw-image.image',
|
||||
'//p3-armor.byteimg.com/tos-cn-i-49unhts6dw/665106f4bbd2a2df96eaf7aec52f7bc3.jpg~tplv-49unhts6dw-image.image',
|
||||
'//p3-armor.byteimg.com/tos-cn-i-49unhts6dw/ea095a2c9c72b5d8f2f2818040db736d.jpg~tplv-49unhts6dw-image.image',
|
||||
];
|
||||
const imageSrc = [
|
||||
'//p3-armor.byteimg.com/tos-cn-i-49unhts6dw/5cc3cd1d994b7ef9db6a1f619a22addd.jpg~tplv-49unhts6dw-image.image',
|
||||
'//p3-armor.byteimg.com/tos-cn-i-49unhts6dw/f256cbcc287139e191fecea9d255a1f0.jpg~tplv-49unhts6dw-image.image',
|
||||
'//p3-armor.byteimg.com/tos-cn-i-49unhts6dw/b557ff0cd44146a2e471b477af2f30d0.jpg~tplv-49unhts6dw-image.image',
|
||||
'//p3-armor.byteimg.com/tos-cn-i-49unhts6dw/665106f4bbd2a2df96eaf7aec52f7bc3.jpg~tplv-49unhts6dw-image.image',
|
||||
'//p3-armor.byteimg.com/tos-cn-i-49unhts6dw/ea095a2c9c72b5d8f2f2818040db736d.jpg~tplv-49unhts6dw-image.image'
|
||||
];
|
||||
</script>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
class="general-card"
|
||||
:header-style="{ paddingBottom: '0' }"
|
||||
:body-style="{
|
||||
padding: '20px',
|
||||
padding: '20px'
|
||||
}"
|
||||
>
|
||||
<template #title>
|
||||
@@ -16,99 +16,99 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import useLoading from '@/hooks/loading';
|
||||
import useChartOption from '@/hooks/chart-option';
|
||||
import useLoading from '@/hooks/loading';
|
||||
import useChartOption from '@/hooks/chart-option';
|
||||
|
||||
const { loading } = useLoading();
|
||||
const { chartOption } = useChartOption((isDark) => {
|
||||
// echarts support https://echarts.apache.org/zh/theme-builder.html
|
||||
// It's not used here
|
||||
return {
|
||||
legend: {
|
||||
left: 'center',
|
||||
data: ['纯文本', '图文类', '视频类'],
|
||||
bottom: 0,
|
||||
icon: 'circle',
|
||||
itemWidth: 8,
|
||||
textStyle: {
|
||||
color: isDark ? 'rgba(255, 255, 255, 0.7)' : '#4E5969',
|
||||
const { loading } = useLoading();
|
||||
const { chartOption } = useChartOption(isDark => {
|
||||
// echarts support https://echarts.apache.org/zh/theme-builder.html
|
||||
// It's not used here
|
||||
return {
|
||||
legend: {
|
||||
left: 'center',
|
||||
data: ['纯文本', '图文类', '视频类'],
|
||||
bottom: 0,
|
||||
icon: 'circle',
|
||||
itemWidth: 8,
|
||||
textStyle: {
|
||||
color: isDark ? 'rgba(255, 255, 255, 0.7)' : '#4E5969'
|
||||
},
|
||||
itemStyle: {
|
||||
borderWidth: 0
|
||||
}
|
||||
},
|
||||
tooltip: {
|
||||
show: true,
|
||||
trigger: 'item'
|
||||
},
|
||||
graphic: {
|
||||
elements: [
|
||||
{
|
||||
type: 'text',
|
||||
left: 'center',
|
||||
top: '40%',
|
||||
style: {
|
||||
text: '内容量',
|
||||
textAlign: 'center',
|
||||
fill: isDark ? '#ffffffb3' : '#4E5969',
|
||||
fontSize: 14
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
left: 'center',
|
||||
top: '50%',
|
||||
style: {
|
||||
text: '928,531',
|
||||
textAlign: 'center',
|
||||
fill: isDark ? '#ffffffb3' : '#1D2129',
|
||||
fontSize: 16,
|
||||
fontWeight: 500
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: 'pie',
|
||||
radius: ['50%', '70%'],
|
||||
center: ['50%', '50%'],
|
||||
label: {
|
||||
formatter: '{d}%',
|
||||
fontSize: 14,
|
||||
color: isDark ? 'rgba(255, 255, 255, 0.7)' : '#4E5969'
|
||||
},
|
||||
itemStyle: {
|
||||
borderWidth: 0,
|
||||
borderColor: isDark ? '#232324' : '#fff',
|
||||
borderWidth: 1
|
||||
},
|
||||
},
|
||||
tooltip: {
|
||||
show: true,
|
||||
trigger: 'item',
|
||||
},
|
||||
graphic: {
|
||||
elements: [
|
||||
data: [
|
||||
{
|
||||
type: 'text',
|
||||
left: 'center',
|
||||
top: '40%',
|
||||
style: {
|
||||
text: '内容量',
|
||||
textAlign: 'center',
|
||||
fill: isDark ? '#ffffffb3' : '#4E5969',
|
||||
fontSize: 14,
|
||||
},
|
||||
value: [148564],
|
||||
name: '纯文本',
|
||||
itemStyle: {
|
||||
color: isDark ? '#3D72F6' : '#249EFF'
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
left: 'center',
|
||||
top: '50%',
|
||||
style: {
|
||||
text: '928,531',
|
||||
textAlign: 'center',
|
||||
fill: isDark ? '#ffffffb3' : '#1D2129',
|
||||
fontSize: 16,
|
||||
fontWeight: 500,
|
||||
},
|
||||
value: [334271],
|
||||
name: '图文类',
|
||||
itemStyle: {
|
||||
color: isDark ? '#A079DC' : '#313CA9'
|
||||
}
|
||||
},
|
||||
],
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: 'pie',
|
||||
radius: ['50%', '70%'],
|
||||
center: ['50%', '50%'],
|
||||
label: {
|
||||
formatter: '{d}%',
|
||||
fontSize: 14,
|
||||
color: isDark ? 'rgba(255, 255, 255, 0.7)' : '#4E5969',
|
||||
},
|
||||
itemStyle: {
|
||||
borderColor: isDark ? '#232324' : '#fff',
|
||||
borderWidth: 1,
|
||||
},
|
||||
data: [
|
||||
{
|
||||
value: [148564],
|
||||
name: '纯文本',
|
||||
itemStyle: {
|
||||
color: isDark ? '#3D72F6' : '#249EFF',
|
||||
},
|
||||
},
|
||||
{
|
||||
value: [334271],
|
||||
name: '图文类',
|
||||
itemStyle: {
|
||||
color: isDark ? '#A079DC' : '#313CA9',
|
||||
},
|
||||
},
|
||||
{
|
||||
value: [445694],
|
||||
name: '视频类',
|
||||
itemStyle: {
|
||||
color: isDark ? '#6CAAF5' : '#21CCFF',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
});
|
||||
{
|
||||
value: [445694],
|
||||
name: '视频类',
|
||||
itemStyle: {
|
||||
color: isDark ? '#6CAAF5' : '#21CCFF'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="less"></style>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
class="general-card"
|
||||
:header-style="{ paddingBottom: 0 }"
|
||||
:body-style="{
|
||||
paddingTop: '20px',
|
||||
paddingTop: '20px'
|
||||
}"
|
||||
:title="'workplace.contentData'"
|
||||
>
|
||||
@@ -17,184 +17,184 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
import { graphic } from 'echarts';
|
||||
import useLoading from '@/hooks/loading';
|
||||
import { queryContentData, ContentDataRecord } from '@/api/dashboard';
|
||||
import useChartOption from '@/hooks/chart-option';
|
||||
import { ToolTipFormatterParams } from '@/types/echarts';
|
||||
import { AnyObject } from '@/types/global';
|
||||
import { ref } from 'vue';
|
||||
import { graphic } from 'echarts';
|
||||
import useLoading from '@/hooks/loading';
|
||||
import { queryContentData, ContentDataRecord } from '@/api/dashboard';
|
||||
import useChartOption from '@/hooks/chart-option';
|
||||
import { ToolTipFormatterParams } from '@/types/echarts';
|
||||
import { AnyObject } from '@/types/global';
|
||||
|
||||
function graphicFactory(side: AnyObject) {
|
||||
return {
|
||||
type: 'text',
|
||||
bottom: '8',
|
||||
...side,
|
||||
style: {
|
||||
text: '',
|
||||
textAlign: 'center',
|
||||
fill: '#4E5969',
|
||||
fontSize: 12,
|
||||
function graphicFactory(side: AnyObject) {
|
||||
return {
|
||||
type: 'text',
|
||||
bottom: '8',
|
||||
...side,
|
||||
style: {
|
||||
text: '',
|
||||
textAlign: 'center',
|
||||
fill: '#4E5969',
|
||||
fontSize: 12
|
||||
}
|
||||
};
|
||||
}
|
||||
const { loading, setLoading } = useLoading(true);
|
||||
const xAxis = ref<string[]>([]);
|
||||
const chartsData = ref<number[]>([]);
|
||||
const graphicElements = ref([
|
||||
graphicFactory({ left: '2.6%' }),
|
||||
graphicFactory({ right: 0 })
|
||||
]);
|
||||
const { chartOption } = useChartOption(() => {
|
||||
return {
|
||||
grid: {
|
||||
left: '2.6%',
|
||||
right: '0',
|
||||
top: '10',
|
||||
bottom: '30'
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
offset: 2,
|
||||
data: xAxis.value,
|
||||
boundaryGap: false,
|
||||
axisLabel: {
|
||||
color: '#4E5969',
|
||||
formatter(value: number, idx: number) {
|
||||
if (idx === 0) return '';
|
||||
if (idx === xAxis.value.length - 1) return '';
|
||||
return `${value}`;
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
const { loading, setLoading } = useLoading(true);
|
||||
const xAxis = ref<string[]>([]);
|
||||
const chartsData = ref<number[]>([]);
|
||||
const graphicElements = ref([
|
||||
graphicFactory({ left: '2.6%' }),
|
||||
graphicFactory({ right: 0 }),
|
||||
]);
|
||||
const { chartOption } = useChartOption(() => {
|
||||
return {
|
||||
grid: {
|
||||
left: '2.6%',
|
||||
right: '0',
|
||||
top: '10',
|
||||
bottom: '30',
|
||||
axisLine: {
|
||||
show: false
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
offset: 2,
|
||||
data: xAxis.value,
|
||||
boundaryGap: false,
|
||||
axisLabel: {
|
||||
color: '#4E5969',
|
||||
formatter(value: number, idx: number) {
|
||||
if (idx === 0) return '';
|
||||
if (idx === xAxis.value.length - 1) return '';
|
||||
return `${value}`;
|
||||
},
|
||||
},
|
||||
axisLine: {
|
||||
show: false,
|
||||
},
|
||||
axisTick: {
|
||||
show: false,
|
||||
},
|
||||
splitLine: {
|
||||
show: true,
|
||||
interval: (idx: number) => {
|
||||
if (idx === 0) return false;
|
||||
if (idx === xAxis.value.length - 1) return false;
|
||||
return true;
|
||||
},
|
||||
lineStyle: {
|
||||
color: '#E5E8EF',
|
||||
},
|
||||
},
|
||||
axisPointer: {
|
||||
show: true,
|
||||
lineStyle: {
|
||||
color: '#23ADFF',
|
||||
width: 2,
|
||||
},
|
||||
},
|
||||
axisTick: {
|
||||
show: false
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
axisLine: {
|
||||
show: false,
|
||||
},
|
||||
axisLabel: {
|
||||
formatter(value: any, idx: number) {
|
||||
if (idx === 0) return value;
|
||||
return `${value}k`;
|
||||
},
|
||||
},
|
||||
splitLine: {
|
||||
show: true,
|
||||
lineStyle: {
|
||||
type: 'dashed',
|
||||
color: '#E5E8EF',
|
||||
},
|
||||
splitLine: {
|
||||
show: true,
|
||||
interval: (idx: number) => {
|
||||
if (idx === 0) return false;
|
||||
if (idx === xAxis.value.length - 1) return false;
|
||||
return true;
|
||||
},
|
||||
lineStyle: {
|
||||
color: '#E5E8EF'
|
||||
}
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
formatter(params) {
|
||||
const [firstElement] = params as ToolTipFormatterParams[];
|
||||
return `<div>
|
||||
axisPointer: {
|
||||
show: true,
|
||||
lineStyle: {
|
||||
color: '#23ADFF',
|
||||
width: 2
|
||||
}
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
axisLine: {
|
||||
show: false
|
||||
},
|
||||
axisLabel: {
|
||||
formatter(value: any, idx: number) {
|
||||
if (idx === 0) return value;
|
||||
return `${value}k`;
|
||||
}
|
||||
},
|
||||
splitLine: {
|
||||
show: true,
|
||||
lineStyle: {
|
||||
type: 'dashed',
|
||||
color: '#E5E8EF'
|
||||
}
|
||||
}
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
formatter(params) {
|
||||
const [firstElement] = params as ToolTipFormatterParams[];
|
||||
return `<div>
|
||||
<p class="tooltip-title">${firstElement.axisValueLabel}</p>
|
||||
<div class="content-panel"><span>总内容量</span><span class="tooltip-value">${(
|
||||
Number(firstElement.value) * 10000
|
||||
).toLocaleString()}</span></div>
|
||||
</div>`;
|
||||
},
|
||||
className: 'echarts-tooltip-diy'
|
||||
},
|
||||
graphic: {
|
||||
elements: graphicElements.value
|
||||
},
|
||||
series: [
|
||||
{
|
||||
data: chartsData.value,
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
// symbol: 'circle',
|
||||
symbolSize: 12,
|
||||
emphasis: {
|
||||
focus: 'series',
|
||||
itemStyle: {
|
||||
borderWidth: 2
|
||||
}
|
||||
},
|
||||
className: 'echarts-tooltip-diy',
|
||||
},
|
||||
graphic: {
|
||||
elements: graphicElements.value,
|
||||
},
|
||||
series: [
|
||||
{
|
||||
data: chartsData.value,
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
// symbol: 'circle',
|
||||
symbolSize: 12,
|
||||
emphasis: {
|
||||
focus: 'series',
|
||||
itemStyle: {
|
||||
borderWidth: 2,
|
||||
lineStyle: {
|
||||
width: 3,
|
||||
color: new graphic.LinearGradient(0, 0, 1, 0, [
|
||||
{
|
||||
offset: 0,
|
||||
color: 'rgba(30, 231, 255, 1)'
|
||||
},
|
||||
},
|
||||
lineStyle: {
|
||||
width: 3,
|
||||
color: new graphic.LinearGradient(0, 0, 1, 0, [
|
||||
{
|
||||
offset: 0,
|
||||
color: 'rgba(30, 231, 255, 1)',
|
||||
},
|
||||
{
|
||||
offset: 0.5,
|
||||
color: 'rgba(36, 154, 255, 1)',
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: 'rgba(111, 66, 251, 1)',
|
||||
},
|
||||
]),
|
||||
},
|
||||
showSymbol: false,
|
||||
areaStyle: {
|
||||
opacity: 0.8,
|
||||
color: new graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{
|
||||
offset: 0,
|
||||
color: 'rgba(17, 126, 255, 0.16)',
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: 'rgba(17, 128, 255, 0)',
|
||||
},
|
||||
]),
|
||||
},
|
||||
{
|
||||
offset: 0.5,
|
||||
color: 'rgba(36, 154, 255, 1)'
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: 'rgba(111, 66, 251, 1)'
|
||||
}
|
||||
])
|
||||
},
|
||||
],
|
||||
};
|
||||
});
|
||||
const fetchData = async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const { data: chartData } = await queryContentData();
|
||||
chartData.forEach((el: ContentDataRecord, idx: number) => {
|
||||
xAxis.value.push(el.x);
|
||||
chartsData.value.push(el.y);
|
||||
if (idx === 0) {
|
||||
graphicElements.value[0].style.text = el.x;
|
||||
showSymbol: false,
|
||||
areaStyle: {
|
||||
opacity: 0.8,
|
||||
color: new graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{
|
||||
offset: 0,
|
||||
color: 'rgba(17, 126, 255, 0.16)'
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: 'rgba(17, 128, 255, 0)'
|
||||
}
|
||||
])
|
||||
}
|
||||
if (idx === chartData.length - 1) {
|
||||
graphicElements.value[1].style.text = el.x;
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
// you can report use errorHandler or other
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
fetchData();
|
||||
});
|
||||
const fetchData = async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const { data: chartData } = await queryContentData();
|
||||
chartData.forEach((el: ContentDataRecord, idx: number) => {
|
||||
xAxis.value.push(el.x);
|
||||
chartsData.value.push(el.y);
|
||||
if (idx === 0) {
|
||||
graphicElements.value[0].style.text = el.x;
|
||||
}
|
||||
if (idx === chartData.length - 1) {
|
||||
graphicElements.value[1].style.text = el.x;
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
// you can report use errorHandler or other
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
fetchData();
|
||||
</script>
|
||||
|
||||
<style scoped lang="less"></style>
|
||||
|
||||
@@ -20,7 +20,8 @@
|
||||
show-group-separator
|
||||
>
|
||||
<template #suffix>
|
||||
W+ <span class="unit">{{ '个' }}</span>
|
||||
W+
|
||||
<span class="unit">{{ '个' }}</span>
|
||||
</template>
|
||||
</a-statistic>
|
||||
</a-space>
|
||||
@@ -92,7 +93,10 @@
|
||||
:value-from="0"
|
||||
animation
|
||||
>
|
||||
<template #suffix> % <icon-caret-up class="up-icon" /> </template>
|
||||
<template #suffix>
|
||||
%
|
||||
<icon-caret-up class="up-icon" />
|
||||
</template>
|
||||
</a-statistic>
|
||||
</a-space>
|
||||
</a-grid-item>
|
||||
@@ -105,27 +109,32 @@
|
||||
<script lang="ts" setup></script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.arco-grid.panel {
|
||||
margin-bottom: 0;
|
||||
padding: 16px 20px 0 20px;
|
||||
}
|
||||
.panel-col {
|
||||
padding-left: 43px;
|
||||
border-right: 1px solid rgb(var(--gray-2));
|
||||
}
|
||||
.col-avatar {
|
||||
margin-right: 12px;
|
||||
background-color: var(--color-fill-2);
|
||||
}
|
||||
.up-icon {
|
||||
color: rgb(var(--red-6));
|
||||
}
|
||||
.unit {
|
||||
margin-left: 8px;
|
||||
color: rgb(var(--gray-8));
|
||||
font-size: 12px;
|
||||
}
|
||||
:deep(.panel-border) {
|
||||
margin: 4px 0 0 0;
|
||||
}
|
||||
.arco-grid.panel {
|
||||
padding: 16px 20px 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.panel-col {
|
||||
padding-left: 43px;
|
||||
border-right: 1px solid rgb(var(--gray-2));
|
||||
}
|
||||
|
||||
.col-avatar {
|
||||
margin-right: 12px;
|
||||
background-color: var(--color-fill-2);
|
||||
}
|
||||
|
||||
.up-icon {
|
||||
color: rgb(var(--red-6));
|
||||
}
|
||||
|
||||
.unit {
|
||||
margin-left: 8px;
|
||||
font-size: 12px;
|
||||
color: rgb(var(--gray-8));
|
||||
}
|
||||
|
||||
:deep(.panel-border) {
|
||||
margin: 4px 0 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -35,8 +35,8 @@
|
||||
</template>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.arco-card-body .arco-link {
|
||||
margin: 10px 0;
|
||||
color: rgb(var(--gray-8));
|
||||
}
|
||||
.arco-card-body .arco-link {
|
||||
margin: 10px 0;
|
||||
color: rgb(var(--gray-8));
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -39,20 +39,22 @@
|
||||
<template #cell="{ record }">
|
||||
<a-typography-paragraph
|
||||
:ellipsis="{
|
||||
rows: 1,
|
||||
rows: 1
|
||||
}"
|
||||
>
|
||||
{{ record.title }}
|
||||
</a-typography-paragraph>
|
||||
</template>
|
||||
</a-table-column>
|
||||
<a-table-column title="点击量" data-index="clickNumber">
|
||||
</a-table-column>
|
||||
<a-table-column
|
||||
title="点击量"
|
||||
data-index="clickNumber"
|
||||
></a-table-column>
|
||||
<a-table-column
|
||||
title="日涨幅"
|
||||
data-index="increases"
|
||||
:sortable="{
|
||||
sortDirections: ['ascend', 'descend'],
|
||||
sortDirections: ['ascend', 'descend']
|
||||
}"
|
||||
>
|
||||
<template #cell="{ record }">
|
||||
@@ -60,7 +62,7 @@
|
||||
<span>{{ record.increases }}%</span>
|
||||
<icon-caret-up
|
||||
v-if="record.increases !== 0"
|
||||
style="color: #f53f3f; font-size: 8px"
|
||||
style="font-size: 8px; color: #f53f3f"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
@@ -73,46 +75,50 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
import useLoading from '@/hooks/loading';
|
||||
import { queryPopularList } from '@/api/dashboard';
|
||||
import type { TableData } from '@arco-design/web-vue/es/table/interface';
|
||||
import { ref } from 'vue';
|
||||
import useLoading from '@/hooks/loading';
|
||||
import { queryPopularList } from '@/api/dashboard';
|
||||
import type { TableData } from '@arco-design/web-vue/es/table/interface';
|
||||
|
||||
const type = ref('text');
|
||||
const { loading, setLoading } = useLoading();
|
||||
const renderList = ref<TableData[]>();
|
||||
const fetchData = async (contentType: string) => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const { data } = await queryPopularList({ type: contentType });
|
||||
renderList.value = data;
|
||||
} catch (err) {
|
||||
// you can report use errorHandler or other
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
const typeChange = (contentType: string) => {
|
||||
fetchData(contentType);
|
||||
};
|
||||
fetchData('text');
|
||||
const type = ref('text');
|
||||
const { loading, setLoading } = useLoading();
|
||||
const renderList = ref<TableData[]>();
|
||||
const fetchData = async (contentType: string) => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const { data } = await queryPopularList({ type: contentType });
|
||||
renderList.value = data;
|
||||
} catch (err) {
|
||||
// you can report use errorHandler or other
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
const typeChange = (contentType: string) => {
|
||||
fetchData(contentType);
|
||||
};
|
||||
fetchData('text');
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.general-card {
|
||||
min-height: 395px;
|
||||
.general-card {
|
||||
min-height: 395px;
|
||||
}
|
||||
|
||||
:deep(.arco-table-tr) {
|
||||
height: 44px;
|
||||
|
||||
.arco-typography {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
:deep(.arco-table-tr) {
|
||||
height: 44px;
|
||||
.arco-typography {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
.increases-cell {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
span {
|
||||
margin-right: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.increases-cell {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
span {
|
||||
margin-right: 4px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -23,13 +23,13 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
const links = [
|
||||
{ text: 'workplace.contentManagement', icon: 'icon-file' },
|
||||
{ text: 'workplace.contentStatistical', icon: 'icon-storage' },
|
||||
{ text: 'workplace.advanced', icon: 'icon-settings' },
|
||||
{ text: 'workplace.onlinePromotion', icon: 'icon-mobile' },
|
||||
{ text: 'workplace.contentPutIn', icon: 'icon-fire' },
|
||||
];
|
||||
const links = [
|
||||
{ text: 'workplace.contentManagement', icon: 'icon-file' },
|
||||
{ text: 'workplace.contentStatistical', icon: 'icon-storage' },
|
||||
{ text: 'workplace.advanced', icon: 'icon-settings' },
|
||||
{ text: 'workplace.onlinePromotion', icon: 'icon-mobile' },
|
||||
{ text: 'workplace.contentPutIn', icon: 'icon-fire' }
|
||||
];
|
||||
</script>
|
||||
|
||||
<style scoped lang="less"></style>
|
||||
|
||||
@@ -1,44 +1,44 @@
|
||||
<template>
|
||||
<a-card
|
||||
class="general-card"
|
||||
title="workplace.recently.visited"
|
||||
:header-style="{ paddingBottom: '0' }"
|
||||
:body-style="{ paddingTop: '26px' }"
|
||||
>
|
||||
<div style="margin-bottom: -1rem">
|
||||
<a-row :gutter="8">
|
||||
<a-col v-for="link in links" :key="link.text" :span="8" class="wrapper">
|
||||
<div class="icon">
|
||||
<component :is="link.icon" />
|
||||
</div>
|
||||
<a-typography-paragraph class="text">
|
||||
{{ 'link.text' }}
|
||||
</a-typography-paragraph>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</div>
|
||||
</a-card>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
const links = [
|
||||
{
|
||||
text: 'workplace.contentManagement',
|
||||
icon: 'icon-storage',
|
||||
},
|
||||
{
|
||||
text: 'workplace.contentStatistical',
|
||||
icon: 'icon-file',
|
||||
},
|
||||
{
|
||||
text: 'workplace.advanced',
|
||||
icon: 'icon-settings',
|
||||
},
|
||||
];
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
:deep(.arco-card-header-title) {
|
||||
line-height: inherit;
|
||||
}
|
||||
</style>
|
||||
<template>
|
||||
<a-card
|
||||
class="general-card"
|
||||
title="workplace.recently.visited"
|
||||
:header-style="{ paddingBottom: '0' }"
|
||||
:body-style="{ paddingTop: '26px' }"
|
||||
>
|
||||
<div style="margin-bottom: -1rem">
|
||||
<a-row :gutter="8">
|
||||
<a-col v-for="link in links" :key="link.text" :span="8" class="wrapper">
|
||||
<div class="icon">
|
||||
<component :is="link.icon" />
|
||||
</div>
|
||||
<a-typography-paragraph class="text">
|
||||
{{ 'link.text' }}
|
||||
</a-typography-paragraph>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</div>
|
||||
</a-card>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
const links = [
|
||||
{
|
||||
text: 'workplace.contentManagement',
|
||||
icon: 'icon-storage'
|
||||
},
|
||||
{
|
||||
text: 'workplace.contentStatistical',
|
||||
icon: 'icon-file'
|
||||
},
|
||||
{
|
||||
text: 'workplace.advanced',
|
||||
icon: 'icon-settings'
|
||||
}
|
||||
];
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
:deep(.arco-card-header-title) {
|
||||
line-height: inherit;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -42,107 +42,113 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import Banner from './components/banner.vue';
|
||||
import DataPanel from './components/data-panel.vue';
|
||||
import ContentChart from './components/content-chart.vue';
|
||||
import PopularContent from './components/popular-content.vue';
|
||||
import CategoriesPercent from './components/categories-percent.vue';
|
||||
import RecentlyVisited from './components/recently-visited.vue';
|
||||
import QuickOperation from './components/quick-operation.vue';
|
||||
import Announcement from './components/announcement.vue';
|
||||
import Carousel from './components/carousel.vue';
|
||||
import Docs from './components/docs.vue';
|
||||
import Banner from './components/banner.vue';
|
||||
import DataPanel from './components/data-panel.vue';
|
||||
import ContentChart from './components/content-chart.vue';
|
||||
import PopularContent from './components/popular-content.vue';
|
||||
import CategoriesPercent from './components/categories-percent.vue';
|
||||
import RecentlyVisited from './components/recently-visited.vue';
|
||||
import QuickOperation from './components/quick-operation.vue';
|
||||
import Announcement from './components/announcement.vue';
|
||||
import Carousel from './components/carousel.vue';
|
||||
import Docs from './components/docs.vue';
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'Dashboard', // If you want the include property of keep-alive to take effect, you must name the component
|
||||
};
|
||||
export default {
|
||||
name: 'Dashboard' // If you want the include property of keep-alive to take effect, you must name the component
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.container {
|
||||
background-color: var(--color-fill-2);
|
||||
padding: 16px 20px;
|
||||
padding-bottom: 0;
|
||||
display: flex;
|
||||
.container {
|
||||
display: flex;
|
||||
padding: 16px 20px;
|
||||
padding-bottom: 0;
|
||||
background-color: var(--color-fill-2);
|
||||
}
|
||||
|
||||
.left-side {
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.right-side {
|
||||
width: 280px;
|
||||
margin-left: 16px;
|
||||
}
|
||||
|
||||
.panel {
|
||||
overflow: auto;
|
||||
background-color: var(--color-bg-2);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
:deep(.panel-border) {
|
||||
margin-bottom: 0;
|
||||
border-bottom: 1px solid rgb(var(--gray-2));
|
||||
}
|
||||
|
||||
.moduler-wrap {
|
||||
background-color: var(--color-bg-2);
|
||||
border-radius: 4px;
|
||||
|
||||
:deep(.text) {
|
||||
font-size: 12px;
|
||||
color: rgb(var(--gray-8));
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.left-side {
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
}
|
||||
:deep(.wrapper) {
|
||||
margin-bottom: 8px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
|
||||
.right-side {
|
||||
width: 280px;
|
||||
margin-left: 16px;
|
||||
}
|
||||
|
||||
.panel {
|
||||
background-color: var(--color-bg-2);
|
||||
border-radius: 4px;
|
||||
overflow: auto;
|
||||
}
|
||||
:deep(.panel-border) {
|
||||
margin-bottom: 0;
|
||||
border-bottom: 1px solid rgb(var(--gray-2));
|
||||
}
|
||||
.moduler-wrap {
|
||||
border-radius: 4px;
|
||||
background-color: var(--color-bg-2);
|
||||
:deep(.text) {
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
color: rgb(var(--gray-8));
|
||||
}
|
||||
|
||||
:deep(.wrapper) {
|
||||
margin-bottom: 8px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
|
||||
&:last-child {
|
||||
.text {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
&:hover {
|
||||
.icon {
|
||||
color: rgb(var(--arcoblue-6));
|
||||
background-color: #e8f3ff;
|
||||
}
|
||||
.text {
|
||||
color: rgb(var(--arcoblue-6));
|
||||
}
|
||||
&:last-child {
|
||||
.text {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.icon) {
|
||||
display: inline-block;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
margin-bottom: 4px;
|
||||
color: rgb(var(--dark-gray-1));
|
||||
line-height: 32px;
|
||||
font-size: 16px;
|
||||
text-align: center;
|
||||
background-color: rgb(var(--gray-1));
|
||||
border-radius: 4px;
|
||||
&:hover {
|
||||
.icon {
|
||||
color: rgb(var(--arcoblue-6));
|
||||
background-color: #e8f3ff;
|
||||
}
|
||||
|
||||
.text {
|
||||
color: rgb(var(--arcoblue-6));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.icon) {
|
||||
display: inline-block;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
margin-bottom: 4px;
|
||||
font-size: 16px;
|
||||
line-height: 32px;
|
||||
color: rgb(var(--dark-gray-1));
|
||||
text-align: center;
|
||||
background-color: rgb(var(--gray-1));
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="less" scoped>
|
||||
// responsive
|
||||
.mobile {
|
||||
.container {
|
||||
display: block;
|
||||
}
|
||||
.right-side {
|
||||
// display: none;
|
||||
width: 100%;
|
||||
margin-left: 0;
|
||||
margin-top: 16px;
|
||||
}
|
||||
// responsive
|
||||
.mobile {
|
||||
.container {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.right-side {
|
||||
// display: none;
|
||||
width: 100%;
|
||||
margin-top: 16px;
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -34,5 +34,5 @@ export default {
|
||||
'workplace.popularContent.image': 'image',
|
||||
'workplace.popularContent.video': 'video',
|
||||
'workplace.categoriesPercent': 'Categories Percent',
|
||||
'个': 'pecs',
|
||||
'个': 'pecs'
|
||||
};
|
||||
|
||||
@@ -33,5 +33,5 @@ export default {
|
||||
'workplace.popularContent.image': '图片',
|
||||
'workplace.popularContent.video': '视频',
|
||||
'workplace.categoriesPercent': '内容类型占比',
|
||||
'个': '个',
|
||||
'个': '个'
|
||||
};
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user