Explorar o código

Merge remote-tracking branch 'origin/main'

zuihou %!s(int64=3) %!d(string=hai) anos
pai
achega
418ca2405e
Modificáronse 83 ficheiros con 1495 adicións e 1420 borrados
  1. 2 6
      .eslintrc.js
  2. 3 2
      .vscode/settings.json
  3. 2 2
      build/generate/icon/index.ts
  4. 4 4
      build/script/buildConf.ts
  5. 3 3
      build/script/postBuild.ts
  6. 3 3
      build/vite/plugin/compress.ts
  7. 3 3
      build/vite/plugin/html.ts
  8. 6 2
      build/vite/plugin/index.ts
  9. 2 2
      build/vite/plugin/styleImport.ts
  10. 2 2
      build/vite/plugin/svgSprite.ts
  11. 3 3
      build/vite/plugin/theme.ts
  12. 0 36
      jest.config.mjs
  13. 8 0
      mock/sys/user.ts
  14. 73 76
      package.json
  15. 775 1013
      pnpm-lock.yaml
  16. 14 0
      src/api/sys/user.ts
  17. 2 2
      src/components/Application/src/AppLocalePicker.vue
  18. 1 2
      src/components/CardList/src/CardList.vue
  19. 1 0
      src/components/ContextMenu/src/ContextMenu.vue
  20. 1 1
      src/components/Cropper/src/CropperAvatar.vue
  21. 1 1
      src/components/Excel/src/Export2Excel.ts
  22. 1 1
      src/components/Excel/src/ImportExcel.vue
  23. 1 0
      src/components/Form/index.ts
  24. 2 0
      src/components/Form/src/componentMap.ts
  25. 3 2
      src/components/Form/src/components/ApiCascader.vue
  26. 86 0
      src/components/Form/src/components/ApiTree.vue
  27. 29 1
      src/components/Form/src/hooks/useFormEvents.ts
  28. 41 1
      src/components/Form/src/hooks/useFormValues.ts
  29. 1 0
      src/components/Form/src/types/index.ts
  30. 1 12
      src/components/Icon/src/IconPicker.vue
  31. 1 1
      src/components/Markdown/src/MarkdownViewer.vue
  32. 1 1
      src/components/Menu/src/BasicMenu.vue
  33. 1 1
      src/components/SimpleMenu/src/components/SubMenuItem.vue
  34. 6 6
      src/components/SimpleMenu/src/components/menu.less
  35. 4 2
      src/components/Table/src/BasicTable.vue
  36. 1 1
      src/components/Table/src/components/HeaderCell.vue
  37. 76 54
      src/components/Table/src/components/editable/EditableCell.vue
  38. 1 1
      src/components/Table/src/components/settings/ColumnSetting.vue
  39. 1 1
      src/components/Table/src/components/settings/SizeSetting.vue
  40. 8 8
      src/components/Table/src/hooks/useColumns.ts
  41. 1 1
      src/components/Table/src/hooks/useDataSource.ts
  42. 16 2
      src/components/Table/src/types/table.ts
  43. 0 5
      src/design/ant/index.less
  44. 3 0
      src/layouts/default/header/components/lock/LockModal.vue
  45. 1 0
      src/layouts/default/header/components/user-dropdown/DropMenuItem.vue
  46. 1 1
      src/layouts/default/sider/MixSider.vue
  47. 7 1
      src/layouts/default/tabs/components/TabContent.vue
  48. 25 0
      src/layouts/default/tabs/index.less
  49. 1 0
      src/locales/lang/en/routes/demo.ts
  50. 1 0
      src/locales/lang/zh-CN/routes/demo.ts
  51. 9 0
      src/router/routes/modules/demo/feat.ts
  52. 6 1
      src/utils/cache/memory.ts
  53. 4 1
      src/utils/http/axios/Axios.ts
  54. 28 0
      src/utils/http/axios/axiosRetry.ts
  55. 1 1
      src/utils/http/axios/axiosTransform.ts
  56. 15 1
      src/utils/http/axios/index.ts
  57. 1 1
      src/views/dashboard/analysis/components/SiteAnalysis.vue
  58. 6 12
      src/views/dashboard/analysis/components/VisitRadar.vue
  59. 6 12
      src/views/dashboard/workbench/components/SaleRadar.vue
  60. 6 12
      src/views/demo/charts/SaleRadar.vue
  61. 1 1
      src/views/demo/comp/card-list/index.vue
  62. 1 1
      src/views/demo/feat/click-out-side/index.vue
  63. 23 0
      src/views/demo/feat/request-demo/index.vue
  64. 35 0
      src/views/demo/form/index.vue
  65. 41 22
      src/views/demo/page/form/basic/data.ts
  66. 2 2
      src/views/demo/page/form/basic/index.vue
  67. 2 0
      src/views/demo/page/form/high/index.vue
  68. 15 0
      src/views/demo/page/form/step/data.tsx
  69. 6 6
      src/views/demo/table/CustomerCell.vue
  70. 11 1
      src/views/demo/table/EditCellTable.vue
  71. 1 1
      src/views/demo/table/ExpandTable.vue
  72. 1 1
      src/views/demo/table/FixedColumn.vue
  73. 2 2
      src/views/demo/table/tableData.tsx
  74. 1 17
      src/views/sys/lock/LockPage.vue
  75. 1 17
      src/views/sys/login/Login.vue
  76. 9 2
      stylelint.config.js
  77. 0 1
      tests/__mocks__/fileMock.ts
  78. 0 1
      tests/__mocks__/styleMock.ts
  79. 0 5
      tests/__mocks__/workerMock.ts
  80. 7 7
      tests/server/package.json
  81. 0 16
      tests/test.spec.ts
  82. 7 0
      types/axios.d.ts
  83. 17 10
      vite.config.ts

+ 2 - 6
.eslintrc.js

@@ -1,6 +1,4 @@
-// @ts-check
-const { defineConfig } = require('eslint-define-config');
-module.exports = defineConfig({
+module.exports = {
   root: true,
   env: {
     browser: true,
@@ -20,9 +18,7 @@ module.exports = defineConfig({
   extends: [
     'plugin:vue/vue3-recommended',
     'plugin:@typescript-eslint/recommended',
-    'prettier',
     'plugin:prettier/recommended',
-    'plugin:jest/recommended',
   ],
   rules: {
     'vue/script-setup-uses-vars': 'error',
@@ -77,4 +73,4 @@ module.exports = defineConfig({
     ],
     'vue/multi-word-component-names': 'off',
   },
-});
+};

+ 3 - 2
.vscode/settings.json

@@ -55,7 +55,7 @@
     "**/yarn.lock": true
   },
   "stylelint.enable": true,
-  "stylelint.packageManager": "yarn",
+  "stylelint.validate": ["css", "less", "postcss", "scss", "vue", "sass"],
   "path-intellisense.mappings": {
     "/@/": "${workspaceRoot}/src"
   },
@@ -88,7 +88,8 @@
   },
   "[vue]": {
     "editor.codeActionsOnSave": {
-      "source.fixAll.eslint": false
+      "source.fixAll.eslint": true,
+      "source.fixAll.stylelint": true
     }
   },
   "i18n-ally.localesPaths": ["src/locales/lang"],

+ 2 - 2
build/generate/icon/index.ts

@@ -1,7 +1,7 @@
 import path from 'path';
 import fs from 'fs-extra';
 import inquirer from 'inquirer';
-import chalk from 'chalk';
+import colors from 'picocolors';
 import pkg from '../../../package.json';
 
 async function generateIcon() {
@@ -64,7 +64,7 @@ async function generateIcon() {
       }
       fs.emptyDir(path.join(process.cwd(), 'node_modules/.vite'));
       console.log(
-        `✨ ${chalk.cyan(`[${pkg.name}]`)}` + ' - Icon generated successfully:' + `[${prefixSet}]`,
+        `✨ ${colors.cyan(`[${pkg.name}]`)}` + ' - Icon generated successfully:' + `[${prefixSet}]`,
       );
     });
 }

+ 4 - 4
build/script/buildConf.ts

@@ -3,7 +3,7 @@
  */
 import { GLOB_CONFIG_FILE_NAME, OUTPUT_DIR } from '../constant';
 import fs, { writeFileSync } from 'fs-extra';
-import chalk from 'chalk';
+import colors from 'picocolors';
 
 import { getEnvConfig, getRootPath } from '../utils';
 import { getConfigFileName } from '../getConfigFileName';
@@ -31,10 +31,10 @@ function createConfig(params: CreateConfigParams) {
     fs.mkdirp(getRootPath(OUTPUT_DIR));
     writeFileSync(getRootPath(`${OUTPUT_DIR}/${configFileName}`), configStr);
 
-    console.log(chalk.cyan(`✨ [${pkg.name}]`) + ` - configuration file is build successfully:`);
-    console.log(chalk.gray(OUTPUT_DIR + '/' + chalk.green(configFileName)) + '\n');
+    console.log(colors.cyan(`✨ [${pkg.name}]`) + ` - configuration file is build successfully:`);
+    console.log(colors.gray(OUTPUT_DIR + '/' + colors.green(configFileName)) + '\n');
   } catch (error) {
-    console.log(chalk.red('configuration file configuration file failed to package:\n' + error));
+    console.log(colors.red('configuration file configuration file failed to package:\n' + error));
   }
 }
 

+ 3 - 3
build/script/postBuild.ts

@@ -1,7 +1,7 @@
 // #!/usr/bin/env node
 
 import { runBuildConfig } from './buildConf';
-import chalk from 'chalk';
+import colors from 'picocolors';
 
 import pkg from '../../package.json';
 
@@ -14,9 +14,9 @@ export const runBuild = async () => {
       runBuildConfig();
     }
 
-    console.log(`✨ ${chalk.cyan(`[${pkg.name}]`)}` + ' - build successfully!');
+    console.log(`✨ ${colors.cyan(`[${pkg.name}]`)}` + ' - build successfully!');
   } catch (error) {
-    console.log(chalk.red('vite build error:\n' + error));
+    console.log(colors.red('vite build error:\n' + error));
     process.exit(1);
   }
 };

+ 3 - 3
build/vite/plugin/compress.ts

@@ -2,16 +2,16 @@
  * Used to package and output gzip. Note that this does not work properly in Vite, the specific reason is still being investigated
  * https://github.com/anncwb/vite-plugin-compression
  */
-import type { Plugin } from 'vite';
+import type { PluginOption } from 'vite';
 import compressPlugin from 'vite-plugin-compression';
 
 export function configCompressPlugin(
   compress: 'gzip' | 'brotli' | 'none',
   deleteOriginFile = false,
-): Plugin | Plugin[] {
+): PluginOption | PluginOption[] {
   const compressList = compress.split(',');
 
-  const plugins: Plugin[] = [];
+  const plugins: PluginOption[] = [];
 
   if (compressList.includes('gzip')) {
     plugins.push(

+ 3 - 3
build/vite/plugin/html.ts

@@ -2,8 +2,8 @@
  * Plugin to minimize and use ejs template syntax in index.html.
  * https://github.com/anncwb/vite-plugin-html
  */
-import type { Plugin } from 'vite';
-import html from 'vite-plugin-html';
+import type { PluginOption } from 'vite';
+import { createHtmlPlugin } from 'vite-plugin-html';
 import pkg from '../../../package.json';
 import { GLOB_CONFIG_FILE_NAME } from '../../constant';
 
@@ -16,7 +16,7 @@ export function configHtmlPlugin(env: ViteEnv, isBuild: boolean) {
     return `${path || '/'}${GLOB_CONFIG_FILE_NAME}?v=${pkg.version}-${new Date().getTime()}`;
   };
 
-  const htmlPlugin: Plugin[] = html({
+  const htmlPlugin: PluginOption[] = createHtmlPlugin({
     minify: isBuild,
     inject: {
       // Inject data into ejs template

+ 6 - 2
build/vite/plugin/index.ts

@@ -1,9 +1,10 @@
-import type { Plugin } from 'vite';
+import { PluginOption } from 'vite';
 import vue from '@vitejs/plugin-vue';
 import vueJsx from '@vitejs/plugin-vue-jsx';
 import legacy from '@vitejs/plugin-legacy';
 import purgeIcons from 'vite-plugin-purge-icons';
 import windiCSS from 'vite-plugin-windicss';
+import VitePluginCertificate from 'vite-plugin-mkcert';
 import vueSetupExtend from 'vite-plugin-vue-setup-extend';
 import { configHtmlPlugin } from './html';
 import { configPwaConfig } from './pwa';
@@ -24,13 +25,16 @@ export function createVitePlugins(viteEnv: ViteEnv, isBuild: boolean) {
     VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE,
   } = viteEnv;
 
-  const vitePlugins: (Plugin | Plugin[])[] = [
+  const vitePlugins: (PluginOption | PluginOption[])[] = [
     // have to
     vue(),
     // have to
     vueJsx(),
     // support name
     vueSetupExtend(),
+    VitePluginCertificate({
+      source: 'coding',
+    }),
   ];
 
   // vite-plugin-windicss

+ 2 - 2
build/vite/plugin/styleImport.ts

@@ -2,13 +2,13 @@
  *  Introduces component library styles on demand.
  * https://github.com/anncwb/vite-plugin-style-import
  */
-import styleImport from 'vite-plugin-style-import';
+import { createStyleImportPlugin } from 'vite-plugin-style-import';
 
 export function configStyleImportPlugin(_isBuild: boolean) {
   // if (!isBuild) {
   //   return [];
   // }
-  const styleImportPlugin = styleImport({
+  const styleImportPlugin = createStyleImportPlugin({
     libs: [
       {
         libraryName: 'ant-design-vue',

+ 2 - 2
build/vite/plugin/svgSprite.ts

@@ -3,11 +3,11 @@
  * https://github.com/anncwb/vite-plugin-svg-icons
  */
 
-import SvgIconsPlugin from 'vite-plugin-svg-icons';
+import { createSvgIconsPlugin } from 'vite-plugin-svg-icons';
 import path from 'path';
 
 export function configSvgIconsPlugin(isBuild: boolean) {
-  const svgIconsPlugin = SvgIconsPlugin({
+  const svgIconsPlugin = createSvgIconsPlugin({
     iconDirs: [path.resolve(process.cwd(), 'src/assets/icons')],
     svgoOptions: isBuild,
     // default

+ 3 - 3
build/vite/plugin/theme.ts

@@ -2,7 +2,7 @@
  * Vite plugin for website theme color switching
  * https://github.com/anncwb/vite-plugin-theme
  */
-import type { Plugin } from 'vite';
+import type { PluginOption } from 'vite';
 import path from 'path';
 import {
   viteThemePlugin,
@@ -14,7 +14,7 @@ import {
 import { getThemeColors, generateColors } from '../../config/themeConfig';
 import { generateModifyVars } from '../../generate/generateModifyVars';
 
-export function configThemePlugin(isBuild: boolean): Plugin[] {
+export function configThemePlugin(isBuild: boolean): PluginOption[] {
   const colors = generateColors({
     mixDarken,
     mixLighten,
@@ -85,5 +85,5 @@ export function configThemePlugin(isBuild: boolean): Plugin[] {
     }),
   ];
 
-  return plugin as unknown as Plugin[];
+  return plugin as unknown as PluginOption[];
 }

+ 0 - 36
jest.config.mjs

@@ -1,36 +0,0 @@
-export default {
-  preset: 'ts-jest',
-  roots: ['<rootDir>/tests/'],
-  clearMocks: true,
-  moduleDirectories: ['node_modules', 'src'],
-  moduleFileExtensions: ['js', 'ts', 'vue', 'tsx', 'jsx', 'json', 'node'],
-  modulePaths: ['<rootDir>/src', '<rootDir>/node_modules'],
-  testMatch: [
-    '**/tests/**/*.[jt]s?(x)',
-    '**/?(*.)+(spec|test).[tj]s?(x)',
-    '(/__tests__/.*|(\\.|/)(test|spec))\\.(js|ts)$',
-  ],
-  testPathIgnorePatterns: [
-    '<rootDir>/tests/server/',
-    '<rootDir>/tests/__mocks__/',
-    '/node_modules/',
-  ],
-  transform: {
-    '^.+\\.tsx?$': 'ts-jest',
-  },
-  transformIgnorePatterns: ['<rootDir>/tests/__mocks__/', '/node_modules/'],
-  // A map from regular expressions to module names that allow to stub out resources with a single module
-  moduleNameMapper: {
-    '\\.(vs|fs|vert|frag|glsl|jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$':
-      '<rootDir>/tests/__mocks__/fileMock.ts',
-    '\\.(sass|s?css|less)$': '<rootDir>/tests/__mocks__/styleMock.ts',
-    '\\?worker$': '<rootDir>/tests/__mocks__/workerMock.ts',
-    '^/@/(.*)$': '<rootDir>/src/$1',
-  },
-  testEnvironment: 'jsdom',
-  verbose: true,
-  collectCoverage: false,
-  coverageDirectory: 'coverage',
-  collectCoverageFrom: ['src/**/*.{js,ts,vue}'],
-  coveragePathIgnorePatterns: ['^.+\\.d\\.ts$'],
-};

+ 8 - 0
mock/sys/user.ts

@@ -111,4 +111,12 @@ export default [
       return resultSuccess(undefined, { message: 'Token has been destroyed' });
     },
   },
+  {
+    url: '/basic-api/testRetry',
+    statusCode: 405,
+    method: 'get',
+    response: () => {
+      return resultError('Error!');
+    },
+  },
 ] as MockMethod[];

+ 73 - 76
package.json

@@ -25,7 +25,6 @@
     "lint:stylelint": "stylelint --cache --fix \"**/*.{vue,less,postcss,css,scss}\" --cache --cache-location node_modules/.cache/stylelint/",
     "lint:lint-staged": "lint-staged",
     "test:unit": "jest",
-    "test:unit-coverage": "jest --coverage",
     "test:gzip": "npx http-server dist --cors --gzip -c-1",
     "test:br": "npx http-server dist --cors --brotli -c-1",
     "reinstall": "rimraf pnpm-lock.yaml && rimraf package.lock.json && rimraf node_modules && npm run bootstrap",
@@ -34,117 +33,115 @@
   },
   "dependencies": {
     "@ant-design/colors": "^6.0.0",
-    "@ant-design/icons-vue": "^6.0.1",
-    "@iconify/iconify": "^2.1.0",
-    "@logicflow/core": "^0.7.16",
-    "@logicflow/extension": "^0.7.16",
-    "@vue/runtime-core": "^3.2.26",
-    "@vue/shared": "^3.2.26",
-    "@vueuse/core": "^7.4.1",
-    "@vueuse/shared": "^7.4.1",
-    "@zxcvbn-ts/core": "^1.2.0",
-    "ant-design-vue": "3.0.0-beta.3",
-    "axios": "^0.24.0",
-    "codemirror": "^5.65.0",
+    "@ant-design/icons-vue": "^6.1.0",
+    "@iconify/iconify": "^2.1.2",
+    "@logicflow/core": "^1.1.7",
+    "@logicflow/extension": "^1.1.7",
+    "@vue/runtime-core": "^3.2.31",
+    "@vue/shared": "^3.2.31",
+    "@vueuse/core": "^8.1.1",
+    "@vueuse/shared": "^8.1.1",
+    "@zxcvbn-ts/core": "^2.0.1",
+    "ant-design-vue": "3.1.0-rc.1",
+    "axios": "^0.26.1",
+    "codemirror": "^5.65.2",
     "cropperjs": "^1.5.12",
     "crypto-js": "^4.1.1",
-    "dayjs": "^1.10.7",
-    "echarts": "^5.2.2",
-    "intro.js": "^4.3.0",
+    "dayjs": "^1.11.0",
+    "echarts": "^5.3.1",
+    "intro.js": "^5.0.0",
     "lodash-es": "^4.17.21",
     "mockjs": "^1.1.0",
     "nprogress": "^0.2.0",
     "path-to-regexp": "^6.2.0",
-    "pinia": "2.0.9",
+    "pinia": "2.0.12",
     "print-js": "^1.6.0",
     "qrcode": "^1.5.0",
-    "qs": "^6.10.2",
+    "qs": "^6.10.3",
     "resize-observer-polyfill": "^1.5.1",
-    "showdown": "^1.9.1",
+    "showdown": "^2.0.3",
     "sortablejs": "^1.14.0",
-    "tinymce": "^5.10.2",
-    "vditor": "^3.8.10",
-    "vue": "^3.2.26",
+    "tinymce": "^5.10.3",
+    "vditor": "^3.8.12",
+    "vue": "^3.2.31",
     "vue-i18n": "^9.1.9",
-    "vue-json-pretty": "^1.8.2",
-    "vue-router": "^4.0.12",
+    "vue-json-pretty": "^2.0.6",
+    "vue-router": "^4.0.14",
     "vue-types": "^4.1.1",
-    "xlsx": "^0.17.4"
+    "xlsx": "^0.18.4"
   },
   "devDependencies": {
-    "@commitlint/cli": "^16.0.1",
-    "@commitlint/config-conventional": "^16.0.0",
-    "@iconify/json": "^2.0.16",
-    "@purge-icons/generated": "^0.7.0",
+    "@commitlint/cli": "^16.2.3",
+    "@commitlint/config-conventional": "^16.2.1",
+    "@iconify/json": "^2.1.17",
+    "@purge-icons/generated": "^0.8.1",
     "@types/codemirror": "^5.60.5",
-    "@types/crypto-js": "^4.0.2",
+    "@types/crypto-js": "^4.1.1",
     "@types/fs-extra": "^9.0.13",
-    "@types/inquirer": "^8.1.3",
+    "@types/inquirer": "^8.2.0",
     "@types/intro.js": "^3.0.2",
-    "@types/jest": "^27.0.3",
-    "@types/lodash-es": "^4.17.5",
-    "@types/mockjs": "^1.0.4",
-    "@types/node": "^17.0.5",
+    "@types/lodash-es": "^4.17.6",
+    "@types/mockjs": "^1.0.6",
+    "@types/node": "^17.0.21",
     "@types/nprogress": "^0.2.0",
     "@types/qrcode": "^1.4.2",
     "@types/qs": "^6.9.7",
     "@types/showdown": "^1.9.4",
     "@types/sortablejs": "^1.10.7",
-    "@typescript-eslint/eslint-plugin": "^5.8.1",
-    "@typescript-eslint/parser": "^5.8.1",
-    "@vitejs/plugin-legacy": "^1.6.4",
-    "@vitejs/plugin-vue": "^2.0.1",
-    "@vitejs/plugin-vue-jsx": "^1.3.3",
-    "@vue/compiler-sfc": "3.2.26",
+    "@typescript-eslint/eslint-plugin": "^5.15.0",
+    "@typescript-eslint/parser": "^5.15.0",
+    "@vitejs/plugin-legacy": "^1.7.1",
+    "@vitejs/plugin-vue": "^2.2.4",
+    "@vitejs/plugin-vue-jsx": "^1.3.8",
+    "@vue/compiler-sfc": "3.2.31",
     "@vue/test-utils": "^2.0.0-rc.18",
-    "autoprefixer": "^10.4.0",
+    "autoprefixer": "^10.4.4",
     "commitizen": "^4.2.4",
     "conventional-changelog-cli": "^2.2.2",
     "cross-env": "^7.0.3",
-    "dotenv": "^10.0.0",
-    "eslint": "^8.5.0",
-    "eslint-config-prettier": "^8.3.0",
-    "eslint-define-config": "^1.2.1",
-    "eslint-plugin-jest": "^25.3.2",
+    "dotenv": "^16.0.0",
+    "eslint": "^8.11.0",
+    "eslint-config-prettier": "^8.5.0",
     "eslint-plugin-prettier": "^4.0.0",
-    "eslint-plugin-vue": "^8.2.0",
-    "esno": "^0.13.0",
-    "fs-extra": "^10.0.0",
+    "eslint-plugin-vue": "^8.5.0",
+    "esno": "^0.14.1",
+    "fs-extra": "^10.0.1",
     "husky": "^7.0.4",
-    "inquirer": "^8.2.0",
-    "jest": "^27.4.5",
+    "inquirer": "^8.2.1",
     "less": "^4.1.2",
-    "lint-staged": "12.1.4",
+    "lint-staged": "12.3.7",
     "npm-run-all": "^4.1.5",
-    "postcss": "^8.4.5",
+    "picocolors": "^1.0.0",
+    "postcss": "^8.4.12",
     "postcss-html": "^1.3.0",
-    "postcss-less": "^5.0.0",
-    "prettier": "^2.5.1",
+    "postcss-less": "^6.0.0",
+    "prettier": "^2.6.0",
     "rimraf": "^3.0.2",
-    "rollup-plugin-visualizer": "^5.5.2",
-    "stylelint": "^14.2.0",
-    "stylelint-config-html": "^1.0.0",
+    "rollup": "^2.70.1",
+    "rollup-plugin-visualizer": "^5.6.0",
+    "stylelint": "^14.6.0",
     "stylelint-config-prettier": "^9.0.3",
-    "stylelint-config-recommended": "^6.0.0",
-    "stylelint-config-standard": "^24.0.0",
+    "stylelint-config-recommended": "^7.0.0",
+    "stylelint-config-recommended-vue": "^1.3.0",
+    "stylelint-config-standard": "^25.0.0",
     "stylelint-order": "^5.0.0",
-    "ts-jest": "^27.1.2",
-    "ts-node": "^10.4.0",
-    "typescript": "^4.5.4",
-    "vite": "^2.7.8",
-    "vite-plugin-compression": "^0.4.0",
-    "vite-plugin-html": "^2.1.2",
-    "vite-plugin-imagemin": "^0.5.1",
+    "ts-node": "^10.7.0",
+    "typescript": "^4.6.2",
+    "vite": "^2.9.0-beta.3",
+    "vite-plugin-compression": "^0.5.1",
+    "vite-plugin-html": "^3.2.0",
+    "vite-plugin-imagemin": "^0.6.1",
+    "vite-plugin-mkcert": "^1.6.0",
     "vite-plugin-mock": "^2.9.6",
-    "vite-plugin-purge-icons": "^0.7.0",
-    "vite-plugin-pwa": "^0.11.12",
-    "vite-plugin-style-import": "^1.4.1",
-    "vite-plugin-svg-icons": "^1.0.5",
+    "vite-plugin-purge-icons": "^0.8.1",
+    "vite-plugin-pwa": "^0.11.13",
+    "vite-plugin-style-import": "^2.0.0",
+    "vite-plugin-svg-icons": "^2.0.1",
     "vite-plugin-theme": "^0.8.1",
-    "vite-plugin-vue-setup-extend": "^0.3.0",
-    "vite-plugin-windicss": "^1.6.1",
-    "vue-eslint-parser": "^8.0.1",
-    "vue-tsc": "^0.30.1"
+    "vite-plugin-vue-setup-extend": "^0.4.0",
+    "vite-plugin-windicss": "^1.8.3",
+    "vue-eslint-parser": "^8.3.0",
+    "vue-tsc": "^0.33.2"
   },
   "resolutions": {
     "bin-wrapper": "npm:bin-wrapper-china",

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 775 - 1013
pnpm-lock.yaml


+ 14 - 0
src/api/sys/user.ts

@@ -8,6 +8,7 @@ enum Api {
   Logout = '/logout',
   GetUserInfo = '/getUserInfo',
   GetPermCode = '/getPermCode',
+  TestRetry = '/testRetry',
 }
 
 /**
@@ -39,3 +40,16 @@ export function getPermCode() {
 export function doLogout() {
   return defHttp.get({ url: Api.Logout });
 }
+
+export function testRetry() {
+  return defHttp.get(
+    { url: Api.TestRetry },
+    {
+      retryRequest: {
+        isOpenRetry: true,
+        count: 5,
+        waitTime: 1000,
+      },
+    },
+  );
+}

+ 2 - 2
src/components/Application/src/AppLocalePicker.vue

@@ -4,11 +4,11 @@
 -->
 <template>
   <Dropdown
-    placement="bottomCenter"
+    placement="bottom"
     :trigger="['click']"
     :dropMenuList="localeList"
     :selectedKeys="selectedKeys"
-    @menuEvent="handleMenuEvent"
+    @menu-event="handleMenuEvent"
     overlayClassName="app-locale-picker-overlay"
   >
     <span class="cursor-pointer flex items-center">

+ 1 - 2
src/components/CardList/src/CardList.vue

@@ -3,7 +3,6 @@
     <div class="p-4 mb-2 bg-white">
       <BasicForm @register="registerForm" />
     </div>
-    {{ sliderProp.width }}
     <div class="p-2 bg-white">
       <List
         :grid="{ gutter: 5, xs: 1, sm: 2, md: 4, lg: 4, xl: 6, xxl: grid }"
@@ -39,7 +38,7 @@
                   <Image :src="item.imgs[0]" />
                 </div>
               </template>
-              <template class="ant-card-actions" #actions>
+              <template #actions>
                 <!--              <SettingOutlined key="setting" />-->
                 <EditOutlined key="edit" />
                 <Dropdown

+ 1 - 0
src/components/ContextMenu/src/ContextMenu.vue

@@ -64,6 +64,7 @@
           width: `${width}px`,
           left: `${left + 1}px`,
           top: `${top + 1}px`,
+          zIndex: 9999,
         };
       });
 

+ 1 - 1
src/components/Cropper/src/CropperAvatar.vue

@@ -22,7 +22,7 @@
 
     <CopperModal
       @register="register"
-      @uploadSuccess="handleUploadSuccess"
+      @upload-success="handleUploadSuccess"
       :uploadApi="uploadApi"
       :src="sourceValue"
     />

+ 1 - 1
src/components/Excel/src/Export2Excel.ts

@@ -1,4 +1,4 @@
-import xlsx from 'xlsx';
+import * as xlsx from 'xlsx';
 import type { WorkBook } from 'xlsx';
 import type { JsonToSheet, AoAToSheet } from './typing';
 

+ 1 - 1
src/components/Excel/src/ImportExcel.vue

@@ -14,7 +14,7 @@
 </template>
 <script lang="ts">
   import { defineComponent, ref, unref } from 'vue';
-  import XLSX from 'xlsx';
+  import * as XLSX from 'xlsx';
   import { dateUtil } from '/@/utils/dateUtil';
 
   import type { ExcelData } from './typing';

+ 1 - 0
src/components/Form/index.ts

@@ -9,6 +9,7 @@ export { useForm } from './src/hooks/useForm';
 export { default as ApiSelect } from './src/components/ApiSelect.vue';
 export { default as RadioButtonGroup } from './src/components/RadioButtonGroup.vue';
 export { default as ApiTreeSelect } from './src/components/ApiTreeSelect.vue';
+export { default as ApiTree } from './src/components/ApiTree.vue';
 export { default as ApiRadioGroup } from './src/components/ApiRadioGroup.vue';
 export { default as ApiCascader } from './src/components/ApiCascader.vue';
 

+ 2 - 0
src/components/Form/src/componentMap.ts

@@ -24,6 +24,7 @@ import {
 import ApiRadioGroup from './components/ApiRadioGroup.vue';
 import RadioButtonGroup from './components/RadioButtonGroup.vue';
 import ApiSelect from './components/ApiSelect.vue';
+import ApiTree from './components/ApiTree.vue';
 import ApiTreeSelect from './components/ApiTreeSelect.vue';
 import ApiCascader from './components/ApiCascader.vue';
 import { BasicUpload } from '/@/components/Upload';
@@ -43,6 +44,7 @@ componentMap.set('AutoComplete', AutoComplete);
 
 componentMap.set('Select', Select);
 componentMap.set('ApiSelect', ApiSelect);
+componentMap.set('ApiTree', ApiTree);
 componentMap.set('TreeSelect', TreeSelect);
 componentMap.set('ApiTreeSelect', ApiTreeSelect);
 componentMap.set('ApiRadioGroup', ApiRadioGroup);

+ 3 - 2
src/components/Form/src/components/ApiCascader.vue

@@ -26,7 +26,7 @@
   import { get, omit } from 'lodash-es';
   import { useRuleFormItem } from '/@/hooks/component/useFormItem';
   import { LoadingOutlined } from '@ant-design/icons-vue';
-
+  import { useI18n } from '/@/hooks/web/useI18n';
   interface Option {
     value: string;
     label: string;
@@ -76,7 +76,7 @@
       const loading = ref<boolean>(false);
       const emitData = ref<any[]>([]);
       const isFirstLoad = ref(true);
-
+      const { t } = useI18n();
       // Embedded in the form, just use the hook binding to perform form verification
       const [state] = useRuleFormItem(props, 'value', 'change', emitData);
 
@@ -188,6 +188,7 @@
         state,
         options,
         loading,
+        t,
         handleChange,
         loadData,
         handleRenderDisplay,

+ 86 - 0
src/components/Form/src/components/ApiTree.vue

@@ -0,0 +1,86 @@
+<template>
+  <a-tree v-bind="getAttrs" @change="handleChange">
+    <template #[item]="data" v-for="item in Object.keys($slots)">
+      <slot :name="item" v-bind="data || {}"></slot>
+    </template>
+    <template #suffixIcon v-if="loading">
+      <LoadingOutlined spin />
+    </template>
+  </a-tree>
+</template>
+
+<script lang="ts">
+  import { computed, defineComponent, watch, ref, onMounted, unref } from 'vue';
+  import { Tree } from 'ant-design-vue';
+  import { isArray, isFunction } from '/@/utils/is';
+  import { get } from 'lodash-es';
+  import { propTypes } from '/@/utils/propTypes';
+  import { LoadingOutlined } from '@ant-design/icons-vue';
+  export default defineComponent({
+    name: 'ApiTree',
+    components: { ATree: Tree, LoadingOutlined },
+    props: {
+      api: { type: Function as PropType<(arg?: Recordable) => Promise<Recordable>> },
+      params: { type: Object },
+      immediate: { type: Boolean, default: true },
+      resultField: propTypes.string.def(''),
+    },
+    emits: ['options-change', 'change'],
+    setup(props, { attrs, emit }) {
+      const treeData = ref<Recordable[]>([]);
+      const isFirstLoaded = ref<Boolean>(false);
+      const loading = ref(false);
+      const getAttrs = computed(() => {
+        return {
+          ...(props.api ? { treeData: unref(treeData) } : {}),
+          ...attrs,
+        };
+      });
+
+      function handleChange(...args) {
+        emit('change', ...args);
+      }
+
+      watch(
+        () => props.params,
+        () => {
+          !unref(isFirstLoaded) && fetch();
+        },
+        { deep: true },
+      );
+
+      watch(
+        () => props.immediate,
+        (v) => {
+          v && !isFirstLoaded.value && fetch();
+        },
+      );
+
+      onMounted(() => {
+        props.immediate && fetch();
+      });
+
+      async function fetch() {
+        const { api } = props;
+        if (!api || !isFunction(api)) return;
+        loading.value = true;
+        treeData.value = [];
+        let result;
+        try {
+          result = await api(props.params);
+        } catch (e) {
+          console.error(e);
+        }
+        loading.value = false;
+        if (!result) return;
+        if (!isArray(result)) {
+          result = get(result, props.resultField);
+        }
+        treeData.value = (result as Recordable[]) || [];
+        isFirstLoaded.value = true;
+        emit('options-change', treeData.value);
+      }
+      return { getAttrs, loading, handleChange };
+    },
+  });
+</script>

+ 29 - 1
src/components/Form/src/hooks/useFormEvents.ts

@@ -2,7 +2,7 @@ import type { ComputedRef, Ref } from 'vue';
 import type { FormProps, FormSchema, FormActionType } from '../types/form';
 import type { NamePath } from 'ant-design-vue/lib/form/interface';
 import { unref, toRaw, nextTick } from 'vue';
-import { isArray, isFunction, isObject, isString } from '/@/utils/is';
+import { isArray, isFunction, isNullOrUnDef, isObject, isString } from '/@/utils/is';
 import { deepMerge } from '/@/utils';
 import { dateItemType, handleInputNumberValue, defaultValueComponents } from '../helper';
 import { dateUtil } from '/@/utils/dateUtil';
@@ -132,11 +132,14 @@ export function useFormEvents({
     if (!prefixField || index === -1 || first) {
       first ? schemaList.unshift(schema) : schemaList.push(schema);
       schemaRef.value = schemaList;
+      _setDefaultValue(schema);
       return;
     }
     if (index !== -1) {
       schemaList.splice(index + 1, 0, schema);
     }
+    _setDefaultValue(schema);
+
     schemaRef.value = schemaList;
   }
 
@@ -192,9 +195,34 @@ export function useFormEvents({
         }
       });
     });
+    _setDefaultValue(schema);
+
     schemaRef.value = uniqBy(schema, 'field');
   }
 
+  function _setDefaultValue(data: FormSchema | FormSchema[]) {
+    let schemas: FormSchema[] = [];
+    if (isObject(data)) {
+      schemas.push(data as FormSchema);
+    }
+    if (isArray(data)) {
+      schemas = [...data];
+    }
+
+    const obj: Recordable = {};
+    schemas.forEach((item) => {
+      if (
+        item.component != 'Divider' &&
+        Reflect.has(item, 'field') &&
+        item.field &&
+        !isNullOrUnDef(item.defaultValue)
+      ) {
+        obj[item.field] = item.defaultValue;
+      }
+    });
+    setFieldsValue(obj);
+  }
+
   function getFieldsValue(): Recordable {
     const formEl = unref(formElRef);
     if (!formEl) return {};

+ 41 - 1
src/components/Form/src/hooks/useFormValues.ts

@@ -11,6 +11,43 @@ interface UseFormValuesContext {
   getProps: ComputedRef<FormProps>;
   formModel: Recordable;
 }
+
+/**
+ * @desription deconstruct array-link key. This method will mutate the target.
+ */
+function tryDeconstructArray(key: string, value: any, target: Recordable) {
+  const pattern = /^\[(.+)\]$/;
+  if (pattern.test(key)) {
+    const match = key.match(pattern);
+    if (match && match[1]) {
+      const keys = match[1].split(',');
+      value = Array.isArray(value) ? value : [value];
+      keys.forEach((k, index) => {
+        set(target, k.trim(), value[index]);
+      });
+      return true;
+    }
+  }
+}
+
+/**
+ * @desription deconstruct object-link key. This method will mutate the target.
+ */
+function tryDeconstructObject(key: string, value: any, target: Recordable) {
+  const pattern = /^\{(.+)\}$/;
+  if (pattern.test(key)) {
+    const match = key.match(pattern);
+    if (match && match[1]) {
+      const keys = match[1].split(',');
+      value = isObject(value) ? value : {};
+      keys.forEach((k) => {
+        set(target, k.trim(), value[k.trim()]);
+      });
+      return true;
+    }
+  }
+}
+
 export function useFormValues({
   defaultValueRef,
   getSchema,
@@ -41,7 +78,10 @@ export function useFormValues({
       if (isString(value)) {
         value = value.trim();
       }
-      set(res, key, value);
+      if (!tryDeconstructArray(key, value, res) && !tryDeconstructObject(key, value, res)) {
+        // 没有解构成功的,按原样赋值
+        set(res, key, value);
+      }
     }
     return handleRangeTimeValue(res);
   }

+ 1 - 0
src/components/Form/src/types/index.ts

@@ -91,6 +91,7 @@ export type ComponentType =
   | 'Select'
   | 'ApiSelect'
   | 'TreeSelect'
+  | 'ApiTree'
   | 'ApiTreeSelect'
   | 'ApiRadioGroup'
   | 'RadioButtonGroup'

+ 1 - 12
src/components/Icon/src/IconPicker.vue

@@ -31,18 +31,7 @@
                   v-for="icon in getPaginationList"
                   :key="icon"
                   :class="currentSelect === icon ? 'border border-primary' : ''"
-                  class="
-                    p-2
-                    w-1/8
-                    cursor-pointer
-                    mr-1
-                    mt-1
-                    flex
-                    justify-center
-                    items-center
-                    border border-solid
-                    hover:border-primary
-                  "
+                  class="p-2 w-1/8 cursor-pointer mr-1 mt-1 flex justify-center items-center border border-solid hover:border-primary"
                   @click="handleClick(icon)"
                   :title="icon"
                 >

+ 1 - 1
src/components/Markdown/src/MarkdownViewer.vue

@@ -4,7 +4,7 @@
 </template>
 
 <script lang="ts" setup>
-  import { computed,defineProps } from 'vue';
+  import { computed, defineProps } from 'vue';
   import showdown from 'showdown';
 
   const converter = new showdown.Converter();

+ 1 - 1
src/components/Menu/src/BasicMenu.vue

@@ -6,7 +6,7 @@
     :openKeys="getOpenKeys"
     :inlineIndent="inlineIndent"
     :theme="theme"
-    @openChange="handleOpenChange"
+    @open-change="handleOpenChange"
     :class="getMenuClass"
     @click="handleMenuClick"
     :subMenuOpenDelay="0.2"

+ 1 - 1
src/components/SimpleMenu/src/components/SubMenuItem.vue

@@ -21,7 +21,7 @@
       :overlayClassName="`${prefixCls}-menu-popover`"
       v-else
       :visible="getIsOpend"
-      @visibleChange="handleVisibleChange"
+      @visible-change="handleVisibleChange"
       :overlayStyle="getOverlayStyle"
       :align="{ offset: [0, 0] }"
     >

+ 6 - 6
src/components/SimpleMenu/src/components/menu.less

@@ -13,8 +13,8 @@
     bottom: 0;
     display: block;
     width: 2px;
-    background-color: @primary-color;
     content: '';
+    background-color: @primary-color;
   }
 }
 
@@ -45,8 +45,8 @@
         position: absolute;
         top: 50%;
         right: 18px;
-        transform: translateY(-50%) rotate(-90deg);
         transition: transform @transition-time @ease-in-out;
+        transform: translateY(-50%) rotate(-90deg);
       }
     }
 
@@ -128,12 +128,12 @@
       position: relative;
       z-index: 1;
       display: flex;
+      align-items: center;
       font-size: @font-size-base;
       color: inherit;
       list-style: none;
       cursor: pointer;
       outline: none;
-      align-items: center;
 
       &:hover,
       &:active {
@@ -178,8 +178,8 @@
     &-vertical &-submenu-collapse {
       .@{submenu-popup-prefix-cls} {
         display: flex;
-        justify-content: center;
         align-items: center;
+        justify-content: center;
       }
       .@{menu-prefix-cls}-submenu-collapsed-show-tit {
         flex-direction: column;
@@ -244,8 +244,8 @@
           left: 0;
           width: 3px;
           height: 100%;
-          background-color: @primary-color;
           content: '';
+          background-color: @primary-color;
         }
       }
     }
@@ -276,8 +276,8 @@
           left: 0;
           width: 3px;
           height: 100%;
-          background-color: @primary-color;
           content: '';
+          background-color: @primary-color;
         }
 
         .@{menu-prefix-cls}-submenu-collapse {

+ 4 - 2
src/components/Table/src/BasicTable.vue

@@ -25,10 +25,12 @@
       <template #[item]="data" v-for="item in Object.keys($slots)" :key="item">
         <slot :name="item" v-bind="data || {}"></slot>
       </template>
-
-      <template #[`header-${column.dataIndex}`] v-for="(column, index) in columns" :key="index">
+      <template #headerCell="{ column }">
         <HeaderCell :column="column" />
       </template>
+      <!--      <template #[`header-${column.dataIndex}`] v-for="(column, index) in columns" :key="index">-->
+      <!--        <HeaderCell :column="column" />-->
+      <!--      </template>-->
     </Table>
   </div>
 </template>

+ 1 - 1
src/components/Table/src/components/HeaderCell.vue

@@ -29,7 +29,7 @@
       const { prefixCls } = useDesign('basic-table-header-cell');
 
       const getIsEdit = computed(() => !!props.column?.edit);
-      const getTitle = computed(() => props.column?.customTitle);
+      const getTitle = computed(() => props.column?.customTitle || props.column?.title);
       const getHelpMessage = computed(() => props.column?.helpMessage);
 
       return { prefixCls, getIsEdit, getTitle, getHelpMessage };

+ 76 - 54
src/components/Table/src/components/editable/EditableCell.vue

@@ -1,40 +1,4 @@
-<template>
-  <div :class="prefixCls">
-    <div
-      v-show="!isEdit"
-      :class="{ [`${prefixCls}__normal`]: true, 'ellipsis-cell': column.ellipsis }"
-      @click="handleEdit"
-    >
-      <div class="cell-content" :title="column.ellipsis ? getValues ?? '' : ''">
-        {{ getValues || getValues === 0 ? getValues : '&nbsp;' }}
-      </div>
-      <FormOutlined :class="`${prefixCls}__normal-icon`" v-if="!column.editRow" />
-    </div>
-
-    <a-spin v-if="isEdit" :spinning="spinning">
-      <div :class="`${prefixCls}__wrapper`" v-click-outside="onClickOutside">
-        <CellComponent
-          v-bind="getComponentProps"
-          :component="getComponent"
-          :style="getWrapperStyle"
-          :popoverVisible="getRuleVisible"
-          :rule="getRule"
-          :ruleMessage="ruleMessage"
-          :class="getWrapperClass"
-          ref="elRef"
-          @change="handleChange"
-          @options-change="handleOptionsChange"
-          @pressEnter="handleEnter"
-        />
-        <div :class="`${prefixCls}__action`" v-if="!getRowEditable">
-          <CheckOutlined :class="[`${prefixCls}__icon`, 'mx-2']" @click="handleSubmitClick" />
-          <CloseOutlined :class="`${prefixCls}__icon `" @click="handleCancel" />
-        </div>
-      </div>
-    </a-spin>
-  </div>
-</template>
-<script lang="ts">
+<script lang="tsx">
   import type { CSSProperties, PropType } from 'vue';
   import { computed, defineComponent, nextTick, ref, toRaw, unref, watchEffect } from 'vue';
   import type { BasicColumn } from '../../types/table';
@@ -56,7 +20,7 @@
 
   export default defineComponent({
     name: 'EditableCell',
-    components: { FormOutlined, CloseOutlined, CheckOutlined, CellComponent, ASpin: Spin },
+    components: { FormOutlined, CloseOutlined, CheckOutlined, CellComponent, Spin },
     directives: {
       clickOutside,
     },
@@ -100,13 +64,6 @@
       });
 
       const getComponentProps = computed(() => {
-        const compProps = props.column?.editComponentProps ?? {};
-        const component = unref(getComponent);
-        const apiSelectProps: Recordable = {};
-        if (component === 'ApiSelect') {
-          apiSelectProps.cache = true;
-        }
-
         const isCheckValue = unref(getIsCheckComp);
 
         const valueField = isCheckValue ? 'checked' : 'value';
@@ -114,19 +71,30 @@
 
         const value = isCheckValue ? (isNumber(val) && isBoolean(val) ? val : !!val) : val;
 
+        let compProps = props.column?.editComponentProps ?? {};
+        const { record, column, index } = props;
+
+        if (isFunction(compProps)) {
+          compProps = compProps({ text: val, record, column, index }) ?? {};
+        }
+        const component = unref(getComponent);
+        const apiSelectProps: Recordable = {};
+        if (component === 'ApiSelect') {
+          apiSelectProps.cache = true;
+        }
+
         return {
           size: 'small',
           getPopupContainer: () => unref(table?.wrapRef.value) ?? document.body,
-          getCalendarContainer: () => unref(table?.wrapRef.value) ?? document.body,
           placeholder: createPlaceholderMessage(unref(getComponent)),
           ...apiSelectProps,
           ...omit(compProps, 'onChange'),
           [valueField]: value,
-        };
+        } as any;
       });
 
       const getValues = computed(() => {
-        const { editComponentProps, editValueMap } = props.column;
+        const { editValueMap } = props.column;
 
         const value = unref(currentValueRef);
 
@@ -139,7 +107,8 @@
           return value;
         }
 
-        const options: LabelValueOptions = editComponentProps?.options ?? (unref(optionsRef) || []);
+        const options: LabelValueOptions =
+          unref(getComponentProps)?.options ?? (unref(optionsRef) || []);
         const option = options.find((item) => `${item.value}` === `${value}`);
 
         return option?.label ?? value;
@@ -199,7 +168,7 @@
         } else if (isString(e) || isBoolean(e) || isNumber(e)) {
           currentValueRef.value = e;
         }
-        const onChange = props.column?.editComponentProps?.onChange;
+        const onChange = unref(getComponentProps)?.onChange;
         if (onChange && isFunction(onChange)) onChange(...arguments);
 
         table.emit?.('edit-change', {
@@ -267,7 +236,7 @@
               result = await beforeEditSubmit({
                 record: pick(record, keys),
                 index,
-                key: key as string,
+                key: dataKey as string,
                 value,
               });
             } catch (e) {
@@ -283,7 +252,7 @@
 
         set(record, dataKey, value);
         //const record = await table.updateTableData(index, dataKey, value);
-        needEmit && table.emit?.('edit-end', { record, index, key, value });
+        needEmit && table.emit?.('edit-end', { record, index, key: dataKey, value });
         isEdit.value = false;
       }
 
@@ -324,7 +293,7 @@
 
       // only ApiSelect or TreeSelect
       function handleOptionsChange(options: LabelValueOptions) {
-        const { replaceFields } = props.column?.editComponentProps ?? {};
+        const { replaceFields } = unref(getComponentProps);
         const component = unref(getComponent);
         if (component === 'ApiTreeSelect') {
           const { title = 'title', value = 'value', children = 'children' } = replaceFields || {};
@@ -357,7 +326,7 @@
 
         if (props.column.dataIndex) {
           if (!props.record.editValueRefs) props.record.editValueRefs = {};
-          props.record.editValueRefs[props.column.dataIndex] = currentValueRef;
+          props.record.editValueRefs[props.column.dataIndex as any] = currentValueRef;
         }
         /* eslint-disable  */
         props.record.onCancelEdit = () => {
@@ -400,6 +369,59 @@
         spinning,
       };
     },
+    render() {
+      return (
+        <div class={this.prefixCls}>
+          <div
+            v-show={!this.isEdit}
+            class={{ [`${this.prefixCls}__normal`]: true, 'ellipsis-cell': this.column.ellipsis }}
+            onClick={this.handleEdit}
+          >
+            <div class="cell-content" title={this.column.ellipsis ? this.getValues ?? '' : ''}>
+              {this.column.editRender
+                ? this.column.editRender({
+                    text: this.value,
+                    record: this.record as Recordable,
+                    column: this.column,
+                    index: this.index,
+                  })
+                : this.getValues
+                ? this.getValues
+                : '\u00A0'}
+            </div>
+            {!this.column.editRow && <FormOutlined class={`${this.prefixCls}__normal-icon`} />}
+          </div>
+          {this.isEdit && (
+            <Spin spinning={this.spinning}>
+              <div class={`${this.prefixCls}__wrapper`} v-click-outside={this.onClickOutside}>
+                <CellComponent
+                  {...this.getComponentProps}
+                  component={this.getComponent}
+                  style={this.getWrapperStyle}
+                  popoverVisible={this.getRuleVisible}
+                  rule={this.getRule}
+                  ruleMessage={this.ruleMessage}
+                  class={this.getWrapperClass}
+                  ref="elRef"
+                  onChange={this.handleChange}
+                  onOptionsChange={this.handleOptionsChange}
+                  onPressEnter={this.handleEnter}
+                />
+                {!this.getRowEditable && (
+                  <div class={`${this.prefixCls}__action`}>
+                    <CheckOutlined
+                      class={[`${this.prefixCls}__icon`, 'mx-2']}
+                      onClick={this.handleSubmitClick}
+                    />
+                    <CloseOutlined class={`${this.prefixCls}__icon `} onClick={this.handleCancel} />
+                  </div>
+                )}
+              </div>
+            </Spin>
+          )}
+        </div>
+      );
+    },
   });
 </script>
 <style lang="less">

+ 1 - 1
src/components/Table/src/components/settings/ColumnSetting.vue

@@ -6,7 +6,7 @@
     <Popover
       placement="bottomLeft"
       trigger="click"
-      @visibleChange="handleVisibleChange"
+      @visible-change="handleVisibleChange"
       :overlayClassName="`${prefixCls}__cloumn-list`"
       :getPopupContainer="getPopupContainer"
     >

+ 1 - 1
src/components/Table/src/components/settings/SizeSetting.vue

@@ -4,7 +4,7 @@
       <span>{{ t('component.table.settingDens') }}</span>
     </template>
 
-    <Dropdown placement="bottomCenter" :trigger="['click']" :getPopupContainer="getPopupContainer">
+    <Dropdown placement="bottom" :trigger="['click']" :getPopupContainer="getPopupContainer">
       <ColumnHeightOutlined />
       <template #overlay>
         <Menu @click="handleTitleClick" selectable v-model:selectedKeys="selectedKeysRef">

+ 8 - 8
src/components/Table/src/hooks/useColumns.ts

@@ -152,10 +152,10 @@ export function useColumns(
         return hasPermission(column.auth) && isIfShow(column);
       })
       .map((column) => {
-        const { slots, dataIndex, customRender, format, edit, editRow, flag } = column;
+        const { slots, customRender, format, edit, editRow, flag } = column;
 
         if (!slots || !slots?.title) {
-          column.slots = { title: `header-${dataIndex}`, ...(slots || {}) };
+          // column.slots = { title: `header-${dataIndex}`, ...(slots || {}) };
           column.customTitle = column.title;
           Reflect.deleteProperty(column, 'title');
         }
@@ -197,7 +197,7 @@ export function useColumns(
    * set columns
    * @param columnList key|column
    */
-  function setColumns(columnList: Partial<BasicColumn>[] | string[]) {
+  function setColumns(columnList: Partial<BasicColumn>[] | (string | string[])[]) {
     const columns = cloneDeep(columnList);
     if (!isArray(columns)) return;
 
@@ -210,23 +210,23 @@ export function useColumns(
 
     const cacheKeys = cacheColumns.map((item) => item.dataIndex);
 
-    if (!isString(firstColumn)) {
+    if (!isString(firstColumn) && !isArray(firstColumn)) {
       columnsRef.value = columns as BasicColumn[];
     } else {
-      const columnKeys = columns as string[];
+      const columnKeys = (columns as (string | string[])[]).map(m => m.toString());
       const newColumns: BasicColumn[] = [];
       cacheColumns.forEach((item) => {
         newColumns.push({
           ...item,
-          defaultHidden: !columnKeys.includes(item.dataIndex! || (item.key as string)),
+          defaultHidden: !columnKeys.includes(item.dataIndex?.toString() || (item.key as string))
         });
       });
       // Sort according to another array
       if (!isEqual(cacheKeys, columns)) {
         newColumns.sort((prev, next) => {
           return (
-            columnKeys.indexOf(prev.dataIndex as string) -
-            columnKeys.indexOf(next.dataIndex as string)
+            columnKeys.indexOf(prev.dataIndex?.toString() as string) -
+            columnKeys.indexOf(next.dataIndex?.toString() as string)
           );
         });
       }

+ 1 - 1
src/components/Table/src/hooks/useDataSource.ts

@@ -292,7 +292,7 @@ export function useDataSource(
       const isArrayResult = Array.isArray(res);
 
       let resultItems: Recordable[] = isArrayResult ? res : get(res, listField);
-      const resultTotal: number = isArrayResult ? 0 : get(res, totalField);
+      const resultTotal: number = isArrayResult ? res.length : get(res, totalField);
 
       // 假如数据变少,导致总页数变少并小于当前选中页码,通过getPaginationRef获取到的页码是不正确的,需获取正确的页码再次执行
       if (resultTotal) {

+ 16 - 2
src/components/Table/src/types/table.ts

@@ -412,7 +412,7 @@ export type CellFormat =
   | Map<string | number, any>;
 
 // @ts-ignore
-export interface BasicColumn extends ColumnProps {
+export interface BasicColumn extends ColumnProps<Recordable> {
   children?: BasicColumn[];
   filters?: {
     text: string;
@@ -441,7 +441,14 @@ export interface BasicColumn extends ColumnProps {
   editRow?: boolean;
   editable?: boolean;
   editComponent?: ComponentType;
-  editComponentProps?: Recordable;
+  editComponentProps?:
+    | ((opt: {
+        text: string | number | boolean | Recordable;
+        record: Recordable;
+        column: BasicColumn;
+        index: number;
+      }) => Recordable)
+    | Recordable;
   editRule?: boolean | ((text: string, record: Recordable) => Promise<string>);
   editValueMap?: (value: any) => string;
   onEditRow?: () => void;
@@ -449,6 +456,13 @@ export interface BasicColumn extends ColumnProps {
   auth?: RoleEnum | RoleEnum[] | string | string[];
   // 业务控制是否显示
   ifShow?: boolean | ((column: BasicColumn) => boolean);
+  // 自定义修改后显示的内容
+  editRender?: (opt: {
+    text: string | number | boolean | Recordable;
+    record: Recordable;
+    column: BasicColumn;
+    index: number;
+  }) => VNodeChild | JSX.Element;
 }
 
 export type ColumnChangeParam = {

+ 0 - 5
src/design/ant/index.less

@@ -2,11 +2,6 @@
 @import './input.less';
 @import './btn.less';
 
-// TODO beta.11 fix
-.ant-col {
-  width: 100%;
-}
-
 .ant-image-preview-root {
   img {
     display: unset;

+ 3 - 0
src/layouts/default/header/components/lock/LockModal.vue

@@ -53,6 +53,9 @@
           {
             field: 'password',
             label: t('layout.header.lockScreenPassword'),
+            colProps: {
+              span: 24,
+            },
             component: 'InputPassword',
             required: true,
           },

+ 1 - 0
src/layouts/default/header/components/user-dropdown/DropMenuItem.vue

@@ -18,6 +18,7 @@
     name: 'DropdownMenuItem',
     components: { MenuItem: Menu.Item, Icon },
     props: {
+      // eslint-disable-next-line
       key: propTypes.string,
       text: propTypes.string,
       icon: propTypes.string,

+ 1 - 1
src/layouts/default/sider/MixSider.vue

@@ -66,7 +66,7 @@
           :items="childrenMenus"
           :theme="getMenuTheme"
           mixSider
-          @menuClick="handleMenuClick"
+          @menu-click="handleMenuClick"
         />
       </ScrollContainer>
       <div

+ 7 - 1
src/layouts/default/tabs/components/TabContent.vue

@@ -1,5 +1,11 @@
 <template>
-  <Dropdown :dropMenuList="getDropMenuList" :trigger="getTrigger" @menuEvent="handleMenuEvent">
+  <Dropdown
+    :dropMenuList="getDropMenuList"
+    :trigger="getTrigger"
+    placement="bottom"
+    overlayClassName="multiple-tabs__dropdown"
+    @menu-event="handleMenuEvent"
+  >
     <div :class="`${prefixCls}__info`" @contextmenu="handleContext" v-if="getIsTabs">
       <span class="ml-1">{{ getTitle }}</span>
     </div>

+ 25 - 0
src/layouts/default/tabs/index.less

@@ -180,3 +180,28 @@ html[data-theme='light'] {
     }
   }
 }
+
+.ant-tabs-dropdown-menu {
+  &-title-content {
+    display: flex;
+    align-items: center;
+
+    .@{prefix-cls} {
+      &-content__info {
+        width: auto;
+        margin-left: 0;
+        line-height: 28px;
+      }
+    }
+  }
+
+  &-item-remove {
+    margin-left: auto;
+  }
+}
+
+.multiple-tabs__dropdown {
+  .ant-dropdown-content {
+    width: 172px;
+  }
+}

+ 1 - 0
src/locales/lang/en/routes/demo.ts

@@ -92,6 +92,7 @@ export default {
     breadcrumb: 'Breadcrumbs',
     breadcrumbFlat: 'Flat Mode',
     breadcrumbFlatDetail: 'Flat mode details',
+    requestDemo: 'Retry request demo',
 
     breadcrumbChildren: 'Level mode',
     breadcrumbChildrenDetail: 'Level mode detail',

+ 1 - 0
src/locales/lang/zh-CN/routes/demo.ts

@@ -88,6 +88,7 @@ export default {
     ws: 'websocket测试',
     breadcrumb: '面包屑导航',
     breadcrumbFlat: '平级模式',
+    requestDemo: '测试请求重试',
     breadcrumbFlatDetail: '平级详情',
     breadcrumbChildren: '层级模式',
     breadcrumbChildrenDetail: '层级详情',

+ 9 - 0
src/router/routes/modules/demo/feat.ts

@@ -32,6 +32,15 @@ const feat: AppRouteModule = {
       },
     },
     {
+      path: 'request',
+      name: 'RequestDemo',
+      // @ts-ignore
+      component: () => import('/@/views/demo/feat/request-demo/index.vue'),
+      meta: {
+        title: t('routes.demo.feat.requestDemo'),
+      },
+    },
+    {
       path: 'session-timeout',
       name: 'SessionTimeout',
       component: () => import('/@/views/demo/feat/session-timeout/index.vue'),

+ 6 - 1
src/utils/cache/memory.ts

@@ -58,7 +58,12 @@ export class Memory<T = any, V = any> {
       return value;
     }
     const now = new Date().getTime();
-    item.time = now + this.alive;
+    /**
+     * Prevent overflow of the setTimeout Maximum delay value
+     * Maximum delay value 2,147,483,647 ms
+     * https://developer.mozilla.org/en-US/docs/Web/API/setTimeout#maximum_delay_value
+     */
+    item.time = expires > now ? expires : now + expires;
     item.timeoutId = setTimeout(
       () => {
         this.remove(key);

+ 4 - 1
src/utils/http/axios/Axios.ts

@@ -111,7 +111,10 @@ export class VAxios {
     // Response result interceptor error capture
     responseInterceptorsCatch &&
       isFunction(responseInterceptorsCatch) &&
-      this.axiosInstance.interceptors.response.use(undefined, responseInterceptorsCatch);
+      this.axiosInstance.interceptors.response.use(undefined, (error) => {
+        // @ts-ignore
+        responseInterceptorsCatch(this.axiosInstance, error);
+      });
   }
 
   /**

+ 28 - 0
src/utils/http/axios/axiosRetry.ts

@@ -0,0 +1,28 @@
+import { AxiosError, AxiosInstance } from 'axios';
+/**
+ *  请求重试机制
+ */
+
+export class AxiosRetry {
+  /**
+   * 重试
+   */
+  retry(AxiosInstance: AxiosInstance, error: AxiosError) {
+    // @ts-ignore
+    const { config } = error.response;
+    const { waitTime, count } = config?.requestOptions?.retryRequest;
+    config.__retryCount = config.__retryCount || 0;
+    if (config.__retryCount >= count) {
+      return Promise.reject(error);
+    }
+    config.__retryCount += 1;
+    return this.delay(waitTime).then(() => AxiosInstance(config));
+  }
+
+  /**
+   * 延迟
+   */
+  private delay(waitTime: number) {
+    return new Promise((resolve) => setTimeout(resolve, waitTime));
+  }
+}

+ 1 - 1
src/utils/http/axios/axiosTransform.ts

@@ -48,5 +48,5 @@ export abstract class AxiosTransform {
   /**
    * @description: 请求之后的拦截器错误处理
    */
-  responseInterceptorsCatch?: (error: Error) => void;
+  responseInterceptorsCatch?: (axiosInstance: AxiosResponse, error: Error) => void;
 }

+ 15 - 1
src/utils/http/axios/index.ts

@@ -17,6 +17,7 @@ import { useErrorLogStoreWithOut } from '/@/store/modules/errorLog';
 import { useI18n } from '/@/hooks/web/useI18n';
 import { joinTimestamp, formatRequestDate } from './helper';
 import { useUserStoreWithOut } from '/@/store/modules/user';
+import { AxiosRetry } from '/@/utils/http/axios/axiosRetry';
 
 const globSetting = useGlobSetting();
 const urlPrefix = globSetting.urlPrefix;
@@ -158,7 +159,7 @@ const transform: AxiosTransform = {
   /**
    * @description: 响应错误处理
    */
-  responseInterceptorsCatch: (error: any) => {
+  responseInterceptorsCatch: (axiosInstance: AxiosResponse, error: any) => {
     const { t } = useI18n();
     const errorLogStore = useErrorLogStoreWithOut();
     errorLogStore.addAjaxErrorInfo(error);
@@ -189,6 +190,14 @@ const transform: AxiosTransform = {
     }
 
     checkStatus(error?.response?.status, msg, errorMessageMode);
+
+    // 添加自动重试机制 保险起见 只针对GET请求
+    const retryRequest = new AxiosRetry();
+    const { isOpenRetry } = config.requestOptions.retryRequest;
+    config.method?.toUpperCase() === RequestEnum.GET &&
+      isOpenRetry &&
+      // @ts-ignore
+      retryRequest.retry(axiosInstance, error);
     return Promise.reject(error);
   },
 };
@@ -234,6 +243,11 @@ function createAxios(opt?: Partial<CreateAxiosOptions>) {
           ignoreCancelToken: true,
           // 是否携带token
           withToken: true,
+          retryRequest: {
+            isOpenRetry: true,
+            count: 5,
+            waitTime: 100,
+          },
         },
       },
       opt || {},

+ 1 - 1
src/views/dashboard/analysis/components/SiteAnalysis.vue

@@ -3,7 +3,7 @@
     :tab-list="tabListTitle"
     v-bind="$attrs"
     :active-tab-key="activeKey"
-    @tabChange="onTabChange"
+    @tab-change="onTabChange"
   >
     <p v-if="activeKey === 'tab1'">
       <VisitAnalysis />

+ 6 - 12
src/views/dashboard/analysis/components/VisitRadar.vue

@@ -39,28 +39,22 @@
           splitNumber: 8,
           indicator: [
             {
-              text: '电脑',
-              max: 100,
+              name: '电脑',
             },
             {
-              text: '充电器',
-              max: 100,
+              name: '充电器',
             },
             {
-              text: '耳机',
-              max: 100,
+              name: '耳机',
             },
             {
-              text: '手机',
-              max: 100,
+              name: '手机',
             },
             {
-              text: 'Ipad',
-              max: 100,
+              name: 'Ipad',
             },
             {
-              text: '耳机',
-              max: 100,
+              name: '耳机',
             },
           ],
         },

+ 6 - 12
src/views/dashboard/workbench/components/SaleRadar.vue

@@ -39,28 +39,22 @@
           splitNumber: 8,
           indicator: [
             {
-              text: '2017',
-              max: 100,
+              name: '2017',
             },
             {
-              text: '2017',
-              max: 100,
+              name: '2017',
             },
             {
-              text: '2018',
-              max: 100,
+              name: '2018',
             },
             {
-              text: '2019',
-              max: 100,
+              name: '2019',
             },
             {
-              text: '2020',
-              max: 100,
+              name: '2020',
             },
             {
-              text: '2021',
-              max: 100,
+              name: '2021',
             },
           ],
         },

+ 6 - 12
src/views/demo/charts/SaleRadar.vue

@@ -43,28 +43,22 @@
               splitNumber: 8,
               indicator: [
                 {
-                  text: '2017',
-                  max: 100,
+                  name: '2017',
                 },
                 {
-                  text: '2017',
-                  max: 100,
+                  name: '2017',
                 },
                 {
-                  text: '2018',
-                  max: 100,
+                  name: '2018',
                 },
                 {
-                  text: '2019',
-                  max: 100,
+                  name: '2019',
                 },
                 {
-                  text: '2020',
-                  max: 100,
+                  name: '2020',
                 },
                 {
-                  text: '2021',
-                  max: 100,
+                  name: '2021',
                 },
               ],
             },

+ 1 - 1
src/views/demo/comp/card-list/index.vue

@@ -1,6 +1,6 @@
 <template>
   <PageWrapper title="卡片列表示例" content="基础封装">
-    <CardList :params="params" :api="demoListApi" @getMethod="getMethod" @delete="handleDel">
+    <CardList :params="params" :api="demoListApi" @get-method="getMethod" @delete="handleDel">
       <template #header>
         <Button type="primary" color="error"> 按钮1 </Button>
         <Button type="primary" color="success"> 按钮2 </Button>

+ 1 - 1
src/views/demo/feat/click-out-side/index.vue

@@ -1,6 +1,6 @@
 <template>
   <PageWrapper title="点内外部触发事件">
-    <ClickOutSide @clickOutside="handleClickOutside" class="flex justify-center">
+    <ClickOutSide @click-outside="handleClickOutside" class="flex justify-center">
       <div @click="innerClick" class="demo-box">
         {{ text }}
       </div>

+ 23 - 0
src/views/demo/feat/request-demo/index.vue

@@ -0,0 +1,23 @@
+<template>
+  <div class="request-box">
+    <a-button @click="handleClick" type="primary"> 点击会重新发起请求5次 </a-button>
+    <p>打开浏览器的network面板,可以看到发出了六次请求</p>
+  </div>
+</template>
+<script lang="ts" setup>
+  import { testRetry } from '/@/api/sys/user';
+  // @ts-ignore
+  const handleClick = async () => {
+    await testRetry();
+  };
+</script>
+
+<style lang="less">
+  .request-box {
+    margin: 50px;
+  }
+
+  p {
+    margin-top: 10px;
+  }
+</style>

+ 35 - 0
src/views/demo/form/index.vue

@@ -142,6 +142,9 @@
       field: 'divider-basic',
       component: 'Divider',
       label: '基础字段',
+      colProps: {
+        span: 24,
+      },
     },
     {
       field: 'field1',
@@ -340,6 +343,9 @@
       field: 'divider-api-select',
       component: 'Divider',
       label: '远程下拉演示',
+      colProps: {
+        span: 24,
+      },
     },
     {
       field: 'field30',
@@ -458,6 +464,9 @@
       field: 'divider-linked',
       component: 'Divider',
       label: '字段联动',
+      colProps: {
+        span: 24,
+      },
     },
     {
       field: 'province',
@@ -509,6 +518,9 @@
       component: 'Divider',
       label: '互斥多选',
       helpMessage: ['两个Select共用数据源', '但不可选择对方已选中的项目'],
+      colProps: {
+        span: 24,
+      },
     },
     {
       field: 'selectA',
@@ -531,9 +543,31 @@
       },
     },
     {
+      field: 'divider-deconstruct',
+      component: 'Divider',
+      label: '字段解构',
+      helpMessage: ['如果组件的值是 array 或者 object', '可以根据 ES6 的解构语法分别取值'],
+      colProps: {
+        span: 24,
+      },
+    },
+    {
+      field: '[startTime, endTime]',
+      label: '时间范围',
+      component: 'RangePicker',
+      componentProps: {
+        format: 'YYYY-MM-DD HH:mm:ss',
+        placeholder: ['开始时间', '结束时间'],
+        showTime: { format: 'HH:mm:ss' },
+      },
+    },
+    {
       field: 'divider-others',
       component: 'Divider',
       label: '其它',
+      colProps: {
+        span: 24,
+      },
     },
     {
       field: 'field20',
@@ -602,6 +636,7 @@
           keyword.value = '';
         },
         handleSubmit: (values: any) => {
+          console.log('values', values);
           createMessage.success('click search,values:' + JSON.stringify(values));
         },
         check,

+ 41 - 22
src/views/demo/page/form/basic/data.ts

@@ -1,10 +1,14 @@
 import { FormSchema } from '/@/components/Form';
+const colProps = {
+  span: 8,
+};
 
 export const schemas: FormSchema[] = [
   {
     field: 'title',
     component: 'Input',
     label: '标题',
+    colProps,
     componentProps: {
       placeholder: '给目标起个名字',
     },
@@ -14,12 +18,37 @@ export const schemas: FormSchema[] = [
     field: 'time',
     component: 'RangePicker',
     label: '起止日期',
+    colProps,
     required: true,
   },
   {
+    field: 'client',
+    component: 'Input',
+    colProps,
+    label: '客户',
+    helpMessage: '目标的服务对象',
+    subLabel: '( 选填 )',
+    componentProps: {
+      placeholder: '请描述你服务的客户,内部客户直接 @姓名/工号',
+    },
+  },
+  {
+    field: 'weights',
+    component: 'InputNumber',
+    label: '权重',
+    colProps,
+    subLabel: '( 选填 )',
+    componentProps: {
+      formatter: (value: string) => (value ? `${value}%` : ''),
+      parser: (value: string) => value.replace('%', ''),
+      placeholder: '请输入',
+    },
+  },
+  {
     field: 'target',
     component: 'InputTextArea',
     label: '目标描述',
+    colProps,
     componentProps: {
       placeholder: '请输入你的阶段性工作目标',
       rows: 4,
@@ -30,46 +59,33 @@ export const schemas: FormSchema[] = [
     field: 'metrics',
     component: 'InputTextArea',
     label: '衡量标准',
+    colProps,
     componentProps: {
       placeholder: '请输入衡量标准',
       rows: 4,
     },
     required: true,
   },
-  {
-    field: 'client',
-    component: 'Input',
-    label: '客户',
-    helpMessage: '目标的服务对象',
-    subLabel: '( 选填 )',
-    componentProps: {
-      placeholder: '请描述你服务的客户,内部客户直接 @姓名/工号',
-    },
-  },
+
   {
     field: 'inviteer',
     component: 'Input',
     label: '邀评人',
-    subLabel: '( 选填 )',
-    componentProps: {
-      placeholder: '请直接 @姓名/工号,最多可邀请 5 人',
+    colProps: {
+      span: 8,
     },
-  },
-  {
-    field: 'weights',
-    component: 'InputNumber',
-    label: '权重',
     subLabel: '( 选填 )',
     componentProps: {
-      formatter: (value: string) => (value ? `${value}%` : ''),
-      parser: (value: string) => value.replace('%', ''),
-      placeholder: '请输入',
+      placeholder: '请直接 @姓名/工号,最多可邀请 5 人',
     },
   },
   {
     field: 'disclosure',
     component: 'RadioGroup',
     label: '目标公开',
+    colProps: {
+      span: 16,
+    },
     itemProps: {
       extra: '客户、邀评人默认被分享',
     },
@@ -91,9 +107,12 @@ export const schemas: FormSchema[] = [
     },
   },
   {
-    field: 'disclosurer',
+    field: 'disclosure',
     component: 'Select',
     label: ' ',
+    colProps: {
+      span: 8,
+    },
     show: ({ model }) => {
       return model.disclosure === '2';
     },

+ 2 - 2
src/views/demo/page/form/basic/index.vue

@@ -25,12 +25,12 @@
           span: 8,
         },
         wrapperCol: {
-          span: 10,
+          span: 15,
         },
         schemas: schemas,
         actionColOptions: {
           offset: 8,
-          span: 12,
+          span: 23,
         },
         submitButtonOptions: {
           text: '提交',

+ 2 - 0
src/views/demo/page/form/high/index.vue

@@ -34,6 +34,7 @@
       const tableRef = ref<{ getDataSource: () => any } | null>(null);
 
       const [register, { validate }] = useForm({
+        layout: 'vertical',
         baseColProps: {
           span: 6,
         },
@@ -42,6 +43,7 @@
       });
 
       const [registerTask, { validate: validateTaskForm }] = useForm({
+        layout: 'vertical',
         baseColProps: {
           span: 6,
         },

+ 15 - 0
src/views/demo/page/form/step/data.tsx

@@ -15,6 +15,9 @@ export const step1Schemas: FormSchema[] = [
         },
       ],
     },
+    colProps: {
+      span: 24,
+    },
   },
   {
     field: 'fac',
@@ -23,6 +26,9 @@ export const step1Schemas: FormSchema[] = [
     required: true,
     defaultValue: 'test@example.com',
     slot: 'fac',
+    colProps: {
+      span: 24,
+    },
   },
   {
     field: 'pay',
@@ -37,6 +43,9 @@ export const step1Schemas: FormSchema[] = [
     label: '收款人姓名',
     defaultValue: 'Vben',
     required: true,
+    colProps: {
+      span: 24,
+    },
   },
   {
     field: 'money',
@@ -49,6 +58,9 @@ export const step1Schemas: FormSchema[] = [
         prefix: () => '¥',
       };
     },
+    colProps: {
+      span: 24,
+    },
   },
 ];
 
@@ -59,5 +71,8 @@ export const step2Schemas: FormSchema[] = [
     label: '支付密码',
     required: true,
     defaultValue: '123456',
+    colProps: {
+      span: 24,
+    },
   },
 ];

+ 6 - 6
src/views/demo/table/CustomerCell.vue

@@ -32,13 +32,13 @@
     {
       title: 'ID',
       dataIndex: 'id',
-      slots: { customRender: 'id' },
+      // slots: { customRender: 'id' },
     },
     {
       title: '头像',
       dataIndex: 'avatar',
       width: 100,
-      slots: { customRender: 'avatar' },
+      // slots: { customRender: 'avatar' },
     },
     {
       title: '分类',
@@ -46,7 +46,7 @@
       width: 80,
       align: 'center',
       defaultHidden: true,
-      slots: { customRender: 'category' },
+      // slots: { customRender: 'category' },
     },
     {
       title: '姓名',
@@ -58,13 +58,13 @@
       dataIndex: 'imgArr',
       helpMessage: ['这是简单模式的图片列表', '只会显示一张在表格中', '但点击可预览多张图片'],
       width: 140,
-      slots: { customRender: 'img' },
+      // slots: { customRender: 'img' },
     },
     {
       title: '照片列表2',
       dataIndex: 'imgs',
       width: 160,
-      slots: { customRender: 'imgs' },
+      // slots: { customRender: 'imgs' },
     },
     {
       title: '地址',
@@ -73,7 +73,7 @@
     {
       title: '编号',
       dataIndex: 'no',
-      slots: { customRender: 'no' },
+      // slots: { customRender: 'no' },
     },
     {
       title: '开始时间',

+ 11 - 1
src/views/demo/table/EditCellTable.vue

@@ -9,13 +9,14 @@
   </div>
 </template>
 <script lang="ts">
-  import { defineComponent } from 'vue';
+  import { defineComponent, h } from 'vue';
   import { BasicTable, useTable, BasicColumn } from '/@/components/Table';
   import { optionsListApi } from '/@/api/demo/select';
 
   import { demoListApi } from '/@/api/demo/table';
   import { treeOptionsListApi } from '/@/api/demo/tree';
   import { useMessage } from '/@/hooks/web/useMessage';
+  import { Progress } from 'ant-design-vue';
   const columns: BasicColumn[] = [
     {
       title: '输入框',
@@ -60,6 +61,15 @@
       editRule: true,
       editComponent: 'InputNumber',
       width: 200,
+      editComponentProps: () => {
+        return {
+          max: 100,
+          min: 0,
+        };
+      },
+      editRender: ({ text }) => {
+        return h(Progress, { percent: Number(text) });
+      },
     },
     {
       title: '下拉框',

+ 1 - 1
src/views/demo/table/ExpandTable.vue

@@ -53,7 +53,7 @@
         actionColumn: {
           width: 160,
           title: 'Action',
-          slots: { customRender: 'action' },
+          // slots: { customRender: 'action' },
         },
       });
       function handleDelete(record: Recordable) {

+ 1 - 1
src/views/demo/table/FixedColumn.vue

@@ -74,7 +74,7 @@
           width: 160,
           title: 'Action',
           dataIndex: 'action',
-          slots: { customRender: 'action' },
+          // slots: { customRender: 'action' },
         },
       });
       function handleDelete(record: Recordable) {

+ 2 - 2
src/views/demo/table/tableData.tsx

@@ -123,13 +123,13 @@ export function getCustomHeaderColumns(): BasicColumn[] {
       // title: '姓名',
       dataIndex: 'name',
       width: 120,
-      slots: { title: 'customTitle' },
+      // slots: { title: 'customTitle' },
     },
     {
       // title: '地址',
       dataIndex: 'address',
       width: 120,
-      slots: { title: 'customAddress' },
+      // slots: { title: 'customAddress' },
       sorter: true,
     },
 

+ 1 - 17
src/views/sys/lock/LockPage.vue

@@ -5,23 +5,7 @@
   >
     <div
       :class="`${prefixCls}__unlock`"
-      class="
-        absolute
-        top-0
-        left-1/2
-        flex
-        pt-5
-        h-16
-        items-center
-        justify-center
-        sm:text-md
-        xl:text-xl
-        text-white
-        flex-col
-        cursor-pointer
-        transform
-        translate-x-1/2
-      "
+      class="absolute top-0 left-1/2 flex pt-5 h-16 items-center justify-center sm:text-md xl:text-xl text-white flex-col cursor-pointer transform translate-x-1/2"
       @click="handleShowForm(false)"
       v-show="showDate"
     >

+ 1 - 17
src/views/sys/login/Login.vue

@@ -32,23 +32,7 @@
         <div class="flex w-full h-full py-5 xl:h-auto xl:py-0 xl:my-0 xl:w-6/12">
           <div
             :class="`${prefixCls}-form`"
-            class="
-              relative
-              w-full
-              px-5
-              py-8
-              mx-auto
-              my-auto
-              rounded-md
-              shadow-md
-              xl:ml-16 xl:bg-transparent
-              sm:px-8
-              xl:p-4 xl:shadow-none
-              sm:w-3/4
-              lg:w-2/4
-              xl:w-auto
-              enter-x
-            "
+            class="relative w-full px-5 py-8 mx-auto my-auto rounded-md shadow-md xl:ml-16 xl:bg-transparent sm:px-8 xl:p-4 xl:shadow-none sm:w-3/4 lg:w-2/4 xl:w-auto enter-x"
           >
             <LoginForm />
             <ForgetPasswordForm />

+ 9 - 2
stylelint.config.js

@@ -1,9 +1,10 @@
 module.exports = {
   root: true,
   plugins: ['stylelint-order'],
-  customSyntax: 'postcss-html',
   extends: ['stylelint-config-standard', 'stylelint-config-prettier'],
+  customSyntax: 'postcss-html',
   rules: {
+    'function-no-unknown': null,
     'selector-class-pattern': null,
     'selector-pseudo-class-no-unknown': [
       true,
@@ -35,6 +36,7 @@ module.exports = {
       },
     ],
     'no-empty-source': null,
+    'string-quotes': null,
     'named-grid-areas-no-invalid': null,
     'unicode-bom': 'never',
     'no-descending-specificity': null,
@@ -72,7 +74,7 @@ module.exports = {
   overrides: [
     {
       files: ['*.vue', '**/*.vue', '*.html', '**/*.html'],
-      extends: ['stylelint-config-recommended', 'stylelint-config-html'],
+      extends: ['stylelint-config-recommended'],
       rules: {
         'keyframes-name-pattern': null,
         'selector-pseudo-class-no-unknown': [
@@ -89,5 +91,10 @@ module.exports = {
         ],
       },
     },
+    {
+      files: ['*.less', '**/*.less'],
+      customSyntax: 'postcss-less',
+      extends: ['stylelint-config-standard', 'stylelint-config-recommended-vue'],
+    },
   ],
 };

+ 0 - 1
tests/__mocks__/fileMock.ts

@@ -1 +0,0 @@
-export default '';

+ 0 - 1
tests/__mocks__/styleMock.ts

@@ -1 +0,0 @@
-export default {};

+ 0 - 5
tests/__mocks__/workerMock.ts

@@ -1,5 +0,0 @@
-export default jest.fn().mockImplementation(() => ({
-  postMessage: jest.fn(),
-  onmessage: jest.fn(),
-  onerror: jest.fn(),
-}));

+ 7 - 7
tests/server/package.json

@@ -10,7 +10,7 @@
     "stop": "npx pm2 stop ecosystem.config.js"
   },
   "dependencies": {
-    "fs-extra": "^10.0.0",
+    "fs-extra": "^10.0.1",
     "koa": "^2.13.4",
     "koa-body": "^4.2.0",
     "koa-bodyparser": "^4.3.0",
@@ -24,13 +24,13 @@
     "@types/koa": "^2.13.4",
     "@types/koa-bodyparser": "^5.0.2",
     "@types/koa-router": "^7.4.4",
-    "@types/node": "^17.0.5",
+    "@types/node": "^17.0.21",
     "nodemon": "^2.0.15",
-    "pm2": "^5.1.2",
+    "pm2": "^5.2.0",
     "rimraf": "^3.0.2",
-    "ts-node": "^10.4.0",
-    "tsconfig-paths": "^3.12.0",
-    "tsup": "^5.11.9",
-    "typescript": "^4.5.4"
+    "ts-node": "^10.7.0",
+    "tsconfig-paths": "^3.14.0",
+    "tsup": "^5.12.1",
+    "typescript": "^4.6.2"
   }
 }

+ 0 - 16
tests/test.spec.ts

@@ -1,16 +0,0 @@
-// import { mount } from '@vue/test-utils';
-// import { Button } from '/@/components/Button';
-
-test('if jest is normal.', async () => {
-  expect('jest').toEqual('jest');
-});
-
-// TODO Vue component testing is not supported temporarily
-// test('is a Vue instance.', async () => {
-//   const wrapper = mount(Button, {
-//     slots: {
-//       default: 'Button text',
-//     },
-//   });
-//   expect(wrapper.html()).toContain('Button text');
-// });

+ 7 - 0
types/axios.d.ts

@@ -23,8 +23,15 @@ export interface RequestOptions {
   ignoreCancelToken?: boolean;
   // Whether to send token in header
   withToken?: boolean;
+  // 请求重试机制
+  retryRequest?: RetryRequest;
 }
 
+export interface RetryRequest {
+  isOpenRetry: boolean;
+  count: number;
+  waitTime: number;
+}
 export interface Result<T = any> {
   code: number;
   type: 'success' | 'error' | 'warning';

+ 17 - 10
vite.config.ts

@@ -53,24 +53,31 @@ export default ({ command, mode }: ConfigEnv): UserConfig => {
       ],
     },
     server: {
+      https: true,
       // Listening on all local IPs
       host: true,
       port: VITE_PORT,
       // Load proxy configuration from .env
       proxy: createProxy(VITE_PROXY),
     },
+    esbuild: {
+      pure: VITE_DROP_CONSOLE ? ['console.log', 'debugger'] : [],
+    },
     build: {
-      minify: false,
-      // target: 'es2015',
-      // cssTarget: 'chrome86',
+      target: 'es2015',
+      cssTarget: 'chrome80',
       outDir: OUTPUT_DIR,
-      terserOptions: {
-        compress: {
-          keep_infinity: true,
-          // Used to delete console in production environment
-          drop_console: VITE_DROP_CONSOLE,
-        },
-      },
+      // minify: 'terser',
+      /**
+       * 当 minify=“minify:'terser'” 解开注释
+       * Uncomment when minify="minify:'terser'"
+       */
+      // terserOptions: {
+      //   compress: {
+      //     keep_infinity: true,
+      //     drop_console: VITE_DROP_CONSOLE,
+      //   },
+      // },
       // Turning off brotliSize display can slightly reduce packaging time
       brotliSize: false,
       chunkSizeWarningLimit: 2000,

Algúns arquivos non se mostraron porque demasiados arquivos cambiaron neste cambio