refactor: ⬆️升级依赖

This commit is contained in:
孙晓龙
2024-06-19 16:41:30 +08:00
parent 78ad60c00e
commit 9a6b854559
117 changed files with 18717 additions and 16367 deletions

View File

@@ -1,3 +0,0 @@
/*.json
/*.js
dist

View File

@@ -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,
},
};

View File

@@ -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
};

View File

@@ -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'],
},
],
},
};

View File

@@ -1,3 +0,0 @@
module.exports = {
plugins: ['@vue/babel-plugin-jsx'],
};

View File

@@ -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'
]
]
}
};

View File

@@ -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;
}

View File

@@ -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
})
);
}

View File

@@ -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;
}

View File

@@ -11,7 +11,7 @@ export default function configVisualizerPlugin() {
filename: './node_modules/.cache/visualizer/stats.html',
open: true,
gzipSize: true,
brotliSize: true,
brotliSize: true
});
}
return [];

View File

@@ -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({
},
},
},
});
}
}

View 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)
}
}

View File

@@ -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
);

View File

@@ -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
View 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'
}
}
]);

View File

@@ -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

File diff suppressed because it is too large Load Diff

View File

@@ -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>

View File

@@ -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');

View File

@@ -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);
},
}
}
);
}

View File

@@ -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);
}

View File

@@ -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);
},
}
});
}

View File

@@ -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);
},
}
});
}

View File

@@ -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);
},
}
});
}

View File

@@ -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);
},
}
}
);
}

View File

@@ -1,5 +1,5 @@
import axios from 'axios';
import { CommonResult } from './common';
import type { CommonResult } from './common';
export interface simpleRoad {
roadName: string;

View File

@@ -1,5 +1,5 @@
import axios from 'axios';
import { CommonResult } from './common';
import type { CommonResult } from './common';
export interface simplePoolRoad {
roadPoolName: string;

View File

@@ -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)
});
}

File diff suppressed because one or more lines are too long

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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);
},
}
};

View File

@@ -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>

View File

@@ -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
};
}

View File

@@ -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>

View File

@@ -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>

View File

@@ -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.

View File

@@ -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>

View File

@@ -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);
},
}
};

View File

@@ -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
View File

@@ -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;
}

View File

@@ -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
};
}

View File

@@ -11,6 +11,6 @@ export default function useLoading(initValue = false) {
return {
loading,
setLoading,
toggle,
toggle
};
}

View File

@@ -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
};
}

View File

@@ -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(() => {

View File

@@ -7,6 +7,6 @@ export default function useThemes() {
return appStore.theme === 'dark';
});
return {
isDark,
isDark
};
}

View File

@@ -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
};
}

View File

@@ -11,6 +11,6 @@ export default function useVisible(initValue = false) {
return {
visible,
setVisible,
toggle,
toggle
};
}

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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';

View File

@@ -5,5 +5,5 @@ import './user';
import '@/views/dashboard/workplace/mock';
Mock.setup({
timeout: '600-1000',
timeout: '600-1000'
});

View File

@@ -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);
});
},
}
});

View File

@@ -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);
});
},
}
});

View File

@@ -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
};
});

View File

@@ -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'
};

View File

@@ -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);
});

View File

@@ -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 =

View File

@@ -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
});
}
});

View File

@@ -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);

View File

@@ -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')
};

View File

@@ -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)

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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> =

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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);

View File

@@ -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) {

View File

@@ -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,

View File

@@ -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();

View File

@@ -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
};
};

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -34,5 +34,5 @@ export default {
'workplace.popularContent.image': 'image',
'workplace.popularContent.video': 'video',
'workplace.categoriesPercent': 'Categories Percent',
'个': 'pecs',
'个': 'pecs'
};

View File

@@ -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