Browse Source

refactor(全局): 重构升级

升级依赖
Ant-Design-Vue3.x
Vue3.2.x
vite2.9.15
BREAKING CHANGE: 重构到vben最新分支
master
wangxiang 2 years ago
parent
commit
ef868d4f58
  1. 2
      .env
  2. 7
      .eslintrc.js
  3. 52
      .yarnclean
  4. 2
      build/config/themeConfig.ts
  5. 4
      build/generate/icon/index.ts
  6. 8
      build/script/buildConf.ts
  7. 6
      build/script/postBuild.ts
  8. 6
      build/vite/plugin/compress.ts
  9. 27
      build/vite/plugin/hmr.ts
  10. 8
      build/vite/plugin/html.ts
  11. 16
      build/vite/plugin/index.ts
  12. 64
      build/vite/plugin/styleImport.ts
  13. 4
      build/vite/plugin/svgSprite.ts
  14. 155
      build/vite/plugin/theme.ts
  15. 3
      build/vite/plugin/visualizer.ts
  16. 2
      index.html
  17. 239
      package.json
  18. 9674
      pnpm-lock.yaml
  19. BIN
      public/favicon.ico
  20. BIN
      public/resource/img/logo.png
  21. 2
      public/resource/img/logo.svg
  22. BIN
      public/resource/img/pwa-192x192.png
  23. BIN
      public/resource/img/pwa-512x512.png
  24. 1
      src/App.vue
  25. 2
      src/api/platform/core/controller/user.ts
  26. BIN
      src/assets/images/login-ad-mini.jpg
  27. BIN
      src/assets/images/logo.png
  28. 2
      src/assets/images/logo.svg
  29. 150
      src/assets/styles/ant-design/btn.less
  30. 8
      src/assets/styles/ant-design/index.less
  31. 3
      src/assets/styles/ant-design/input.less
  32. 5
      src/assets/styles/color.less
  33. 188
      src/assets/styles/common.less
  34. 6
      src/assets/styles/index.less
  35. 268
      src/assets/styles/kicc.less
  36. 12
      src/assets/styles/transition/fade.less
  37. 7
      src/assets/styles/var/index.less
  38. 13
      src/assets/styles/var/kicc.less
  39. 1
      src/assets/styles/vxe-table/index.less
  40. 110
      src/assets/styles/vxe-table/vxe.dark.less
  41. 10
      src/components/AMap/src/AMapDesigner/index.vue
  42. 20
      src/components/AMap/src/amap.data.tsx
  43. 14
      src/components/AMap/src/components/MapPointModal.vue
  44. 2
      src/components/Application/src/AppLogo.vue
  45. 4
      src/components/Button/src/BasicButton.vue
  46. 9
      src/components/Button/src/props.ts
  47. 27
      src/components/ClickOutSide/src/ClickOutSide.vue
  48. 99
      src/components/Container/src/collapse/CollapseContainer.vue
  49. 60
      src/components/Container/src/collapse/CollapseHeader.vue
  50. 19
      src/components/ContextMenu/src/ContextMenu.vue
  51. 1
      src/components/ContextMenu/src/typing.ts
  52. 3
      src/components/Cropper/src/CopperModal.vue
  53. 82
      src/components/Description/src/Description.vue
  54. 2
      src/components/Description/src/typing.ts
  55. 48
      src/components/Excel/src/Export2Excel.ts
  56. 118
      src/components/Excel/src/ImportExcel.vue
  57. 1
      src/components/Form/index.ts
  58. 97
      src/components/Form/src/BasicForm.vue
  59. 2
      src/components/Form/src/componentMap.ts
  60. 31
      src/components/Form/src/components/ApiCascader.vue
  61. 29
      src/components/Form/src/components/ApiRadioGroup.vue
  62. 26
      src/components/Form/src/components/ApiSelect.vue
  63. 138
      src/components/Form/src/components/ApiTransfer.vue
  64. 31
      src/components/Form/src/components/FormItem.vue
  65. 19
      src/components/Form/src/components/RadioButtonGroup.vue
  66. 4
      src/components/Form/src/helper.ts
  67. 8
      src/components/Form/src/hooks/useAdvanced.ts
  68. 10
      src/components/Form/src/hooks/useForm.ts
  69. 80
      src/components/Form/src/hooks/useFormEvents.ts
  70. 20
      src/components/Form/src/hooks/useFormValues.ts
  71. 10
      src/components/Form/src/props.ts
  72. 12
      src/components/Form/src/types/form.ts
  73. 3
      src/components/Form/src/types/index.ts
  74. 2
      src/components/Icon/src/Icon.vue
  75. 178
      src/components/Icon/src/IconPicker.vue
  76. 27
      src/components/Loading/src/Loading.vue
  77. 4
      src/components/Loading/src/useLoading.ts
  78. 2
      src/components/Markdown/index.ts
  79. 236
      src/components/Markdown/src/Markdown.vue
  80. 62
      src/components/Markdown/src/MarkdownViewer.vue
  81. 19
      src/components/Markdown/src/getTheme.ts
  82. 5
      src/components/Menu/src/BasicMenu.vue
  83. 11
      src/components/Menu/src/components/BasicMenuItem.vue
  84. 8
      src/components/Menu/src/components/BasicSubMenuItem.vue
  85. 11
      src/components/Menu/src/components/MenuItemContent.vue
  86. 9
      src/components/Menu/src/props.ts
  87. 4
      src/components/Menu/src/useOpenKeys.ts
  88. 44
      src/components/Modal/src/BasicModal.vue
  89. 9
      src/components/Modal/src/components/Modal.tsx
  90. 2
      src/components/Modal/src/components/ModalClose.vue
  91. 1
      src/components/Modal/src/components/ModalFooter.vue
  92. 1
      src/components/Modal/src/components/ModalHeader.vue
  93. 65
      src/components/Modal/src/components/ModalWrapper.vue
  94. 16
      src/components/Modal/src/hooks/useModal.ts
  95. 5
      src/components/Modal/src/index.less
  96. 2
      src/components/Page/index.ts
  97. 4
      src/components/Page/src/PageFooter.vue
  98. 51
      src/components/Page/src/PageWrapper.vue
  99. 4
      src/components/Qrcode/src/Qrcode.vue
  100. 11
      src/components/Scrollbar/src/Scrollbar.vue
  101. Some files were not shown because too many files have changed in this diff Show More

2
.env

@ -2,7 +2,7 @@
VITE_PORT = 3100 VITE_PORT = 3100
# 网站标题 # 网站标题
VITE_GLOB_APP_TITLE = Kicc Admin VITE_GLOB_APP_TITLE = 康来智慧冷链平台
# 简称,用于配置文件名字 不要出现空格、数字开头等特殊字符 # 简称,用于配置文件名字 不要出现空格、数字开头等特殊字符
VITE_GLOB_APP_SHORT_NAME = kicc_admin VITE_GLOB_APP_SHORT_NAME = kicc_admin

7
.eslintrc.js

@ -56,7 +56,6 @@ module.exports = defineConfig({
/** ----------vue配置----------- */ /** ----------vue配置----------- */
'vue/script-setup-uses-vars': 'error', 'vue/script-setup-uses-vars': 'error',
'vue/custom-event-name-casing': 'off', 'vue/custom-event-name-casing': 'off',
'vue/attributes-order': 'off',
'vue/one-component-per-file': 'off', 'vue/one-component-per-file': 'off',
'vue/multiline-html-element-content-newline': 'off', 'vue/multiline-html-element-content-newline': 'off',
'vue/singleline-html-element-content-newline': 'off', 'vue/singleline-html-element-content-newline': 'off',
@ -87,10 +86,7 @@ module.exports = defineConfig({
}], }],
'vue/max-attributes-per-line': ['error', { 'vue/max-attributes-per-line': ['error', {
'singleline': 3, 'singleline': 3,
'multiline': { 'multiline': 1
'max': 1,
'allowFirstLine': true
}
}], }],
'vue/html-closing-bracket-spacing': ['error', { 'vue/html-closing-bracket-spacing': ['error', {
'startTag': 'never', 'startTag': 'never',
@ -118,5 +114,6 @@ module.exports = defineConfig({
'alphabetical': false 'alphabetical': false
}], }],
'vue/no-v-html': 'off', 'vue/no-v-html': 'off',
'vue/multi-word-component-names': 'off',
} }
}); });

52
.yarnclean

@ -1,52 +0,0 @@
# PROJECT: kicc-ui
# DESCRIPTION: 配置yarn install后清除并从软件包依赖关系中删除不必要的文件
# VERSION: 1.0.0
# Author: 康来生物科技有限公司-王翔
# test directories
test
tests
powered-test
# asset directories
docs
doc
website
images
assets
# examples
example
examples
# code coverage directories
coverage
.nyc_output
# build scripts
Makefile
Gulpfile.js
Gruntfile.js
# configs
appveyor.yml
circle.yml
codeship-services.yml
codeship-steps.yml
wercker.yml
.tern-project
.gitattributes
.editorconfig
.*ignore
.eslintrc
.jshintrc
.flowconfig
.documentup.json
.yarn-metadata.json
.travis.yml
# misc
*.md
!istanbul-reports/lib/html/assets
!istanbul-api/node_modules/istanbul-reports/lib/html/assets

2
build/config/themeConfig.ts

@ -7,7 +7,7 @@
import { generate } from '@ant-design/colors'; import { generate } from '@ant-design/colors';
export const primaryColor = '#0960bd'; export const primaryColor = '#0084f4';
type Fn = (...arg: any) => any; type Fn = (...arg: any) => any;
type GenerateTheme = 'default' | 'dark'; type GenerateTheme = 'default' | 'dark';

4
build/generate/icon/index.ts

@ -14,7 +14,7 @@
import path from 'path'; import path from 'path';
import fs from 'fs-extra'; import fs from 'fs-extra';
import inquirer from 'inquirer'; import inquirer from 'inquirer';
import chalk from 'chalk'; import colors from 'picocolors';
import pkg from '../../../package.json'; import pkg from '../../../package.json';
/** 生成图标集 */ /** 生成图标集 */
@ -80,7 +80,7 @@ import pkg from '../../../package.json';
} }
// 清除vite预构建缓存 // 清除vite预构建缓存
fs.emptyDir(path.join(process.cwd(), 'node_modules/.vite')); fs.emptyDir(path.join(process.cwd(), 'node_modules/.vite'));
console.log(`${chalk.cyan(`[${pkg.name}]`)}` + ' - Icon generated successfully:' + `[${prefixSet}]`); console.log(`${colors.cyan(`[${pkg.name}]`)}` + ' - Icon generated successfully:' + `[${prefixSet}]`);
}); });
})(); })();

8
build/script/buildConf.ts

@ -7,7 +7,7 @@
import { GLOB_CONFIG_FILE_NAME, OUTPUT_DIR } from '../constant'; import { GLOB_CONFIG_FILE_NAME, OUTPUT_DIR } from '../constant';
import fs, { writeFileSync } from 'fs-extra'; import fs, { writeFileSync } from 'fs-extra';
import chalk from 'chalk'; import colors from 'picocolors';
import { getRootPath, getEnvConfig } from '../utils'; import { getRootPath, getEnvConfig } from '../utils';
import { getConfigFileName } from '../getConfigFileName'; import { getConfigFileName } from '../getConfigFileName';
import pkg from '../../package.json'; import pkg from '../../package.json';
@ -32,10 +32,10 @@ function createConfig({
// 将配置信息追加到_app.config.js文件中 // 将配置信息追加到_app.config.js文件中
writeFileSync(getRootPath(`${OUTPUT_DIR}/${configFileName}`), configStr); writeFileSync(getRootPath(`${OUTPUT_DIR}/${configFileName}`), configStr);
console.log(chalk.cyan(`✨ [${pkg.name}]`) + ` - configuration file is build successfully:`); console.log(colors.cyan(`✨ [${pkg.name}]`) + ` - configuration file is build successfully:`);
console.log(chalk.gray(OUTPUT_DIR + '/' + chalk.green(configFileName)) + '\n'); console.log(colors.gray(OUTPUT_DIR + '/' + colors.green(configFileName)) + '\n');
} catch (error) { } 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));
} }
} }

6
build/script/postBuild.ts

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

6
build/vite/plugin/compress.ts

@ -7,13 +7,13 @@
* @create: 2022/4/6 * @create: 2022/4/6
*/ */
import type { Plugin } from 'vite';
import compressPlugin from 'vite-plugin-compression'; import compressPlugin from 'vite-plugin-compression';
import type { PluginOption } from 'vite';
export function configCompressPlugin(compress: 'gzip' | 'brotli' | 'none', deleteOriginFile = false): Plugin | Plugin[] { export function configCompressPlugin(compress: 'gzip' | 'brotli' | 'none', deleteOriginFile = false): PluginOption | PluginOption[] {
const compressList = compress.split(','); const compressList = compress.split(',');
const plugins: Plugin[] = []; const plugins: PluginOption[] = [];
if (compressList.includes('gzip')) { if (compressList.includes('gzip')) {
plugins.push( plugins.push(

27
build/vite/plugin/hmr.ts

@ -1,27 +0,0 @@
/**
* @program: kicc-ui
* @description: HMR()
* todo: 目前解决方案直接清除已经导入的模块然后直接热部署,Vite循环依赖问题,,
* @author: entfrm开发团队-
* @create: 2022/4/5
*/
import type { Plugin } from 'vite';
export function configHmrPlugin(): Plugin {
return {
name: 'singleHMR',
handleHotUpdate({ modules, file }) {
if (file.match(/xml$/)) return [];
modules.forEach((m) => {
if (!m.url.match(/\.(css|less)/)) {
m.importedModules = new Set();
m.importers = new Set();
}
});
return modules;
},
};
}

8
build/vite/plugin/html.ts

@ -7,8 +7,8 @@
* @create: 2022/4/5 * @create: 2022/4/5
*/ */
import type { Plugin } from 'vite'; import type { PluginOption } from 'vite';
import html from 'vite-plugin-html'; import { createHtmlPlugin } from 'vite-plugin-html';
import pkg from '../../../package.json'; import pkg from '../../../package.json';
import { GLOB_CONFIG_FILE_NAME } from '../../constant'; import { GLOB_CONFIG_FILE_NAME } from '../../constant';
@ -20,11 +20,11 @@ export function configHtmlPlugin(env: ViteEnv, isBuild: boolean) {
return `${path || '/'}${GLOB_CONFIG_FILE_NAME}?v=${pkg.version}-${new Date().getTime()}`; return `${path || '/'}${GLOB_CONFIG_FILE_NAME}?v=${pkg.version}-${new Date().getTime()}`;
}; };
const htmlPlugin: Plugin[] = html({ const htmlPlugin: PluginOption[] = createHtmlPlugin({
minify: isBuild, minify: isBuild,
inject: { inject: {
// 将数据注入ejs模板 // 将数据注入ejs模板
injectData: { data: {
title: VITE_GLOB_APP_TITLE, title: VITE_GLOB_APP_TITLE,
}, },
// 嵌入生成的app.config.js文件 // 嵌入生成的app.config.js文件

16
build/vite/plugin/index.ts

@ -5,7 +5,7 @@
* @create: 2022/4/5 * @create: 2022/4/5
*/ */
import type { Plugin } from 'vite'; import type { PluginOption } from 'vite';
import vue from '@vitejs/plugin-vue'; import vue from '@vitejs/plugin-vue';
import vueJsx from '@vitejs/plugin-vue-jsx'; import vueJsx from '@vitejs/plugin-vue-jsx';
import legacy from '@vitejs/plugin-legacy'; import legacy from '@vitejs/plugin-legacy';
@ -19,7 +19,8 @@ import { configVisualizerConfig } from './visualizer';
import { configThemePlugin } from './theme'; import { configThemePlugin } from './theme';
import { configImageminPlugin } from './imagemin'; import { configImageminPlugin } from './imagemin';
import { configSvgIconsPlugin } from './svgSprite'; import { configSvgIconsPlugin } from './svgSprite';
import { configHmrPlugin } from './hmr'; import OptimizationPersist from 'vite-plugin-optimize-persist';
import PkgConfig from 'vite-plugin-package-config';
export function createVitePlugins(viteEnv: ViteEnv, isBuild: boolean) { export function createVitePlugins(viteEnv: ViteEnv, isBuild: boolean) {
const { const {
@ -29,7 +30,7 @@ export function createVitePlugins(viteEnv: ViteEnv, isBuild: boolean) {
VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE
} = viteEnv; } = viteEnv;
const vitePlugins: (Plugin | Plugin[])[] = [ const vitePlugins: (PluginOption | PluginOption[])[] = [
// 必须,提供对Vue3单文件组件构建 // 必须,提供对Vue3单文件组件构建
vue(), vue(),
// 必须,提供对Vue3 Jsx构建 // 必须,提供对Vue3 Jsx构建
@ -45,9 +46,6 @@ export function createVitePlugins(viteEnv: ViteEnv, isBuild: boolean) {
// vite-plugin-purge-icons,提供对Iconify图标框架中的icon以dom节点的方式生成到html中 // vite-plugin-purge-icons,提供对Iconify图标框架中的icon以dom节点的方式生成到html中
vitePlugins.push(purgeIcons()); vitePlugins.push(purgeIcons());
// todo: 解决HMR(模块热部署)循环依赖问题
!isBuild && vitePlugins.push(configHmrPlugin());
// vite-plugin-html,提供压缩和基于ejs模板功能 // vite-plugin-html,提供压缩和基于ejs模板功能
vitePlugins.push(configHtmlPlugin(viteEnv, isBuild)); vitePlugins.push(configHtmlPlugin(viteEnv, isBuild));
@ -63,6 +61,12 @@ export function createVitePlugins(viteEnv: ViteEnv, isBuild: boolean) {
// vite-plugin-style-import,提供对vite的按需引入组件库样式功能 // vite-plugin-style-import,提供对vite的按需引入组件库样式功能
vitePlugins.push(configStyleImportPlugin()); vitePlugins.push(configStyleImportPlugin());
// vite-plugin-package-config,提供对依赖包的配置文件注入到package.json
vitePlugins.push(PkgConfig());
// vite-plugin-optimize-persist,提供对构建文件的缓存,避免二次预构建
vitePlugins.push(OptimizationPersist());
// 以下插件仅适用于生产环境 // 以下插件仅适用于生产环境
if (isBuild) { if (isBuild) {
// vite-plugin-imagemin,提供压缩图片资源的功能 // vite-plugin-imagemin,提供压缩图片资源的功能

64
build/vite/plugin/styleImport.ts

@ -7,9 +7,9 @@
* @create: 2022/4/5 * @create: 2022/4/5
*/ */
import styleImport from 'vite-plugin-style-import'; import { createStyleImportPlugin } from 'vite-plugin-style-import';
export function configStyleImportPlugin() { export function configStyleImportPlugin() {
const styleImportPlugin = styleImport({ const styleImportPlugin = createStyleImportPlugin({
libs: [ libs: [
{ {
// 需要导入库名 // 需要导入库名
@ -18,13 +18,69 @@ export function configStyleImportPlugin() {
esModule: true, esModule: true,
// 自定义样式文件转换 // 自定义样式文件转换
resolveStyle: (name) => { resolveStyle: (name) => {
return `ant-design-vue/es/${name}/style/index`; // 这里是无需额外引入样式文件的“子组件”列表
const ignoreList = [
'anchor-link',
'sub-menu',
'menu-item',
'menu-divider',
'menu-item-group',
'breadcrumb-item',
'breadcrumb-separator',
'form-item',
'step',
'select-option',
'select-opt-group',
'card-grid',
'card-meta',
'collapse-panel',
'descriptions-item',
'list-item',
'list-item-meta',
'table-column',
'table-column-group',
'tab-pane',
'tab-content',
'timeline-item',
'tree-node',
'skeleton-input',
'skeleton-avatar',
'skeleton-title',
'skeleton-paragraph',
'skeleton-image',
'skeleton-button',
];
// 这里是需要额外引入样式的子组件列表
// 单独引入子组件时需引入组件样式,否则会在打包后导致子组件样式丢失
const replaceList = {
'typography-text': 'typography',
'typography-title': 'typography',
'typography-paragraph': 'typography',
'typography-link': 'typography',
'dropdown-button': 'dropdown',
'input-password': 'input',
'input-search': 'input',
'input-group': 'input',
'radio-group': 'radio',
'checkbox-group': 'checkbox',
'layout-sider': 'layout',
'layout-content': 'layout',
'layout-footer': 'layout',
'layout-header': 'layout',
'month-picker': 'date-picker',
'range-picker': 'date-picker',
'image-preview-group': 'image',
};
return ignoreList.includes(name)
? ''
: replaceList.hasOwnProperty(name)
? `ant-design-vue/es/${replaceList[name]}/style/index`
: `ant-design-vue/es/${name}/style/index`;
} }
}, },
{ // 按需加载vxe-table组件 { // 按需加载vxe-table组件
libraryName: 'vxe-table', libraryName: 'vxe-table',
esModule: true, esModule: true,
resolveComponent: (name) => `vxe-table/es/${name}`,
resolveStyle: (name) => `vxe-table/es/${name}/style.css` resolveStyle: (name) => `vxe-table/es/${name}/style.css`
} }
] ]

4
build/vite/plugin/svgSprite.ts

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

155
build/vite/plugin/theme.ts

@ -7,94 +7,109 @@
* @create: 2022/4/5 * @create: 2022/4/5
*/ */
import type { Plugin } from 'vite'; import type { PluginOption } from 'vite';
import path from 'path'; import path from 'path';
import { import {
viteThemePlugin, viteThemePlugin,
antdDarkThemePlugin, antdDarkThemePlugin,
mixLighten, mixLighten,
mixDarken, mixDarken,
tinycolor tinycolor,
} from 'vite-plugin-theme'; } from 'vite-plugin-theme';
import { getThemeColors, generateColors } from '../../config/themeConfig'; import { getThemeColors, generateColors } from '../../config/themeConfig';
import { generateModifyVars } from '../../generate/generateModifyVars'; import { generateModifyVars } from '../../generate/generateModifyVars';
export function configThemePlugin(isBuild: boolean): Plugin[] { export function configThemePlugin(isBuild: boolean): PluginOption[] {
// 生成主题渐变颜色,进行匹配提取css // 生成主题渐变颜色,进行匹配提取css
const colors = generateColors({ const colors = generateColors({
mixDarken, mixDarken,
mixLighten, mixLighten,
tinycolor tinycolor
}); });
const plugin = [
// 配置vite主题插件,在vite编译时处理 // fix:修复编译后主题色切换不生效黑屏的问题
viteThemePlugin({ // https://github.com/vbenjs/vue-vben-admin/issues/1445
// 自定义选择器转换 const vite_theme_plugin = viteThemePlugin({
resolveSelector: (s) => { // 自定义选择器转换
s = s.trim(); resolveSelector: (s) => {
switch (s) { s = s.trim();
case '.ant-steps-item-process .ant-steps-item-icon > .ant-steps-icon': switch (s) {
return '.ant-steps-item-icon > .ant-steps-icon'; case '.ant-steps-item-process .ant-steps-item-icon > .ant-steps-icon':
case '.ant-radio-button-wrapper-checked:not(.ant-radio-button-wrapper-disabled)': return '.ant-steps-item-icon > .ant-steps-icon';
case '.ant-radio-button-wrapper-checked:not(.ant-radio-button-wrapper-disabled):hover': case '.ant-radio-button-wrapper-checked:not(.ant-radio-button-wrapper-disabled)':
case '.ant-radio-button-wrapper-checked:not(.ant-radio-button-wrapper-disabled):active': case '.ant-radio-button-wrapper-checked:not(.ant-radio-button-wrapper-disabled):hover':
return s; case '.ant-radio-button-wrapper-checked:not(.ant-radio-button-wrapper-disabled):active':
case '.ant-steps-item-icon > .ant-steps-icon': return s;
return s; case '.ant-steps-item-icon > .ant-steps-icon':
case '.ant-select-item-option-selected:not(.ant-select-item-option-disabled)': return s;
case '.ant-select-item-option-selected:not(.ant-select-item-option-disabled)':
return s;
default:
if (s.indexOf('.ant-btn') >= -1) {
// 按钮被重新定制过,需要过滤掉class防止覆盖
return s; return s;
} }
// 对于没有data-theme前缀的css选择器,加个[data-theme]标识前缀,目前没什么用,后期可能会用到获取这部分数据 }
return s.startsWith('[data-theme') ? s : `[data-theme] ${s}`; // 对于没有data-theme前缀的css选择器,加个[data-theme]标识前缀,目前没什么用,后期可能会用到获取这部分数据
}, return s.startsWith('[data-theme') ? s : `[data-theme] ${s}`;
// 如果css包含数组中的颜色值,则会提取css },
colorVariables: [...getThemeColors(), ...colors] // 如果css包含数组中的颜色值,则会提取css
}), colorVariables: [...getThemeColors(), ...colors]
// 配置ant-design暗黑主题插件 });
antdDarkThemePlugin({
// 设置服务器启动预加载antd.less文件
preloadFiles: [
path.resolve(process.cwd(), 'node_modules/ant-design-vue/dist/antd.less'),
// path.resolve(process.cwd(), 'node_modules/ant-design-vue/dist/antd.dark.less'),
path.resolve(process.cwd(), 'src/assets/styles/index.less'),
path.resolve(process.cwd(), 'node_modules/vxe-table/lib/style.css'),
],
// 过滤less(禁止在生产环境客户端控制台调用transform注入的函数处理css)
filter: (id) => (isBuild ? !id.endsWith('antd.less') : true),
// 制定暗黑主题风格颜色
darkModifyVars: {
// ant-design默认颜色
...generateModifyVars(true),
// 替换ant-design默认颜色,制定暗黑系列
'text-color': '#c9d1d9',
'primary-1': 'rgb(255 255 255 / 8%)',
'text-color-base': '#c9d1d9',
'component-background': '#151515',
'heading-color': 'rgb(255 255 255 / 65%)',
// black: '#0e1117',
// #8b949e
'text-color-secondary': '#8b949e',
'border-color-base': '#303030',
// 'border-color-split': '#30363d',
'item-active-bg': '#111b26',
'app-content-background': '#1e1e1e',
'tree-node-selected-bg': '#11263c',
'alert-success-border-color': '#274916', vite_theme_plugin.forEach(function (item) {
'alert-success-bg-color': '#162312', //对vite:theme插件特殊配置
'alert-success-icon-color': '#49aa19', if ('vite:theme' === item.name) {
'alert-info-border-color': '#153450', // 打包时去除enforce: "post",vite 2.6.x适配,否则生成app-theme-style为空,因为async transform(code, id) {的code没有正确获取
'alert-info-bg-color': '#111b26', if (isBuild) {
'alert-info-icon-color': '#177ddc', delete item.enforce;
'alert-warning-border-color': '#594214',
'alert-warning-bg-color': '#2b2111',
'alert-warning-icon-color': '#d89614',
'alert-error-border-color': '#58181c',
'alert-error-bg-color': '#2a1215',
'alert-error-icon-color': '#a61d24'
} }
}) }
]; });
// 配置ant-design暗黑主题插件
const plugin = [vite_theme_plugin, antdDarkThemePlugin({
// 设置服务器启动预加载antd.less文件
preloadFiles: [
path.resolve(process.cwd(), 'node_modules/ant-design-vue/dist/antd.less'),
// path.resolve(process.cwd(), 'node_modules/ant-design-vue/dist/antd.dark.less'),
path.resolve(process.cwd(), 'src/assets/styles/index.less'),
],
// 过滤less(禁止在生产环境客户端控制台调用transform注入的函数处理css)
filter: (id) => (isBuild ? !id.endsWith('antd.less') : true),
// 制定暗黑主题风格颜色
darkModifyVars: {
// ant-design默认颜色
...generateModifyVars(true),
// 替换ant-design默认颜色,制定暗黑系列
'text-color': '#c9d1d9',
'primary-1': 'rgb(255 255 255 / 8%)',
'text-color-base': '#c9d1d9',
'component-background': '#151515',
'heading-color': 'rgb(255 255 255 / 65%)',
// black: '#0e1117',
// #8b949e
'text-color-secondary': '#8b949e',
'border-color-base': '#303030',
// 'border-color-split': '#30363d',
'item-active-bg': '#111b26',
'app-content-background': '#1e1e1e',
'tree-node-selected-bg': '#11263c',
'alert-success-border-color': '#274916',
'alert-success-bg-color': '#162312',
'alert-success-icon-color': '#49aa19',
'alert-info-border-color': '#153450',
'alert-info-bg-color': '#111b26',
'alert-info-icon-color': '#177ddc',
'alert-warning-border-color': '#594214',
'alert-warning-bg-color': '#2b2111',
'alert-warning-icon-color': '#d89614',
'alert-error-border-color': '#58181c',
'alert-error-bg-color': '#2a1215',
'alert-error-icon-color': '#a61d24',
}
})];
return plugin as unknown as Plugin[]; return plugin as unknown as PluginOption[];
} }

3
build/vite/plugin/visualizer.ts

@ -9,6 +9,7 @@
import visualizer from 'rollup-plugin-visualizer'; import visualizer from 'rollup-plugin-visualizer';
import { isReportMode } from '../../utils'; import { isReportMode } from '../../utils';
import type { PluginOption } from 'vite';
export function configVisualizerConfig() { export function configVisualizerConfig() {
if (isReportMode()) { if (isReportMode()) {
@ -21,7 +22,7 @@ export function configVisualizerConfig() {
gzipSize: true, gzipSize: true,
// 从源代码中收集brotli大小并将其显示在图表中 // 从源代码中收集brotli大小并将其显示在图表中
brotliSize: true, brotliSize: true,
}) as Plugin; }) as PluginOption;
} }
return []; return [];
} }

2
index.html

@ -152,7 +152,7 @@
</style> </style>
<div class="app-loading"> <div class="app-loading">
<div class="app-loading-wrap"> <div class="app-loading-wrap">
<img src="/resource/img/logo.svg" alt="Logo"/> <img src="/resource/img/logo.png" class="app-loading-logo" alt="Logo"/>
<div class="app-loading-dots"> <div class="app-loading-dots">
<span class="dot dot-spin"><i></i><i></i><i></i><i></i></span> <span class="dot dot-spin"><i></i><i></i><i></i><i></i></span>
</div> </div>

239
package.json

@ -12,11 +12,11 @@
"lint:check": "eslint --max-warnings 0 \"src/**/*.{vue,ts,tsx}\"", "lint:check": "eslint --max-warnings 0 \"src/**/*.{vue,ts,tsx}\"",
"lint:eslint": "eslint --cache --max-warnings 0 \"src/**/*.{vue,ts,tsx}\" --fix", "lint:eslint": "eslint --cache --max-warnings 0 \"src/**/*.{vue,ts,tsx}\" --fix",
"build": "cross-env NODE_ENV=production vite build && esno ./build/script/postBuild.ts", "build": "cross-env NODE_ENV=production vite build && esno ./build/script/postBuild.ts",
"build:test": "yarn lint:check && vite build --mode test && esno ./build/script/postBuild.ts", "build:test": "pnpm lint:check && vite build --mode test && esno ./build/script/postBuild.ts",
"build:no-cache": "yarn delete:cache && yarn build", "build:no-cache": "pnpm delete:cache && pnpm build",
"preview": "yarn run build && vite preview", "preview": "pnpm run build && vite preview",
"preview:dist": "vite preview", "preview:dist": "vite preview",
"report": "cross-env REPORT=true yarn run build", "report": "cross-env REPORT=true pnpm run build",
"log": "conventional-changelog -p angular -i CHANGELOG.md -s", "log": "conventional-changelog -p angular -i CHANGELOG.md -s",
"delete:lib": "rimraf node_modules", "delete:lib": "rimraf node_modules",
"delete:cache": "rimraf node_modules/.cache/ && rimraf node_modules/.vite", "delete:cache": "rimraf node_modules/.cache/ && rimraf node_modules/.vite",
@ -24,83 +24,96 @@
"type:check": "vue-tsc --noEmit --skipLibCheck" "type:check": "vue-tsc --noEmit --skipLibCheck"
}, },
"dependencies": { "dependencies": {
"@amap/amap-jsapi-loader": "1.0.1", "@amap/amap-jsapi-loader": "^1.0.1",
"@iconify/iconify": "2.2.1", "@iconify/iconify": "^2.1.30",
"@vueuse/core": "9.1.1", "@vueuse/core": "^9.1.1",
"@zxcvbn-ts/core": "2.0.1", "@vueuse/shared": "^9.1.1",
"ant-design-vue": "2.2.8", "@zxcvbn-ts/core": "^2.0.1",
"axios": "0.21.3", "@ant-design/colors": "^6.0.0",
"cropperjs": "1.5.12", "@ant-design/icons-vue": "^6.1.0",
"crypto-js": "4.1.1", "@vue/runtime-core": "^3.2.33",
"echarts": "5.2.0", "@vue/shared": "^3.2.33",
"ant-design-vue": "^3.2.12",
"axios": "^0.26.1",
"cropperjs": "^1.5.12",
"crypto-js": "^4.1.1",
"dayjs": "^1.11.1",
"echarts": "^5.3.2",
"event-source-polyfill": "^1.0.31", "event-source-polyfill": "^1.0.31",
"js-base64": "3.6.1", "js-base64": "^3.6.1",
"lodash-es": "4.17.21", "lodash-es": "^4.17.21",
"nprogress": "0.2.0", "nprogress": "^0.2.0",
"path-to-regexp": "6.2.0", "path-to-regexp": "^6.2.0",
"pinia": "2.0.13", "pinia": "^2.0.13",
"print-js": "1.6.0", "print-js": "^1.6.0",
"qrcode": "1.4.4", "qrcode": "^1.5.0",
"qs": "6.10.3", "qs": "^6.10.3",
"resize-observer-polyfill": "1.5.1", "resize-observer-polyfill": "^1.5.1",
"sortablejs": "^1.15.0", "sortablejs": "^1.15.0",
"tinymce": "5.9.1", "tinymce": "^5.10.3",
"vditor": "3.8.6", "vditor": "^3.8.13",
"vue": "3.2.12", "vue": "^3.2.45",
"vue-i18n": "9.1.7", "vue-i18n": "^9.1.9",
"vue-router": "4.0.11", "vue-router": "^4.0.14",
"vue-types": "4.0.3", "vue-types": "^4.1.1",
"vxe-table": "^4.2.8", "exceljs": "^4.3.0",
"vxe-table": "^4.3.9",
"vxe-table-plugin-antd": "^3.0.5",
"vxe-table-plugin-export-xlsx": "^3.0.4",
"xe-utils": "^3.5.4", "xe-utils": "^3.5.4",
"xlsx": "0.17.2" "xlsx": "^0.17.2"
}, },
"devDependencies": { "devDependencies": {
"@iconify/json": "2.1.30", "@iconify/json": "^2.1.30",
"@types/crypto-js": "4.0.2", "@purge-icons/generated": "^0.8.1",
"@types/fs-extra": "9.0.12", "@types/crypto-js": "^4.1.1",
"@types/inquirer": "7.3.3", "@types/fs-extra": "^9.0.13",
"@types/lodash-es": "4.17.4", "@types/inquirer": "^8.2.1",
"@types/nprogress": "0.2.0", "@types/lodash-es": "^4.17.6",
"@types/qrcode": "1.4.1", "@types/nprogress": "^0.2.0",
"@types/qs": "6.9.7", "@types/qrcode": "^1.5.0",
"@types/sortablejs": "1.10.7", "@types/qs": "^6.9.7",
"@typescript-eslint/eslint-plugin": "4.31.1", "@types/sortablejs": "^1.10.7",
"@typescript-eslint/parser": "4.31.1", "@typescript-eslint/eslint-plugin": "^5.20.0",
"@vitejs/plugin-legacy": "1.5.3", "@typescript-eslint/parser": "^5.20.0",
"@vitejs/plugin-vue": "1.6.2", "@vitejs/plugin-legacy": "^2.0.0",
"@vitejs/plugin-vue-jsx": "1.1.8", "@vitejs/plugin-vue": "^2.3.3",
"@vue/compiler-sfc": "3.2.9", "@vitejs/plugin-vue-jsx": "^1.3.10",
"autoprefixer": "10.3.4", "@vue/compiler-sfc": "^3.2.33",
"conventional-changelog-cli": "2.1.1", "picocolors": "^1.0.0",
"cross-env": "7.0.3", "autoprefixer": "^10.4.4",
"dotenv": "10.0.0", "conventional-changelog-cli": "^2.2.2",
"eslint": "7.32.0", "cross-env": "^7.0.3",
"eslint-define-config": "1.0.9", "dotenv": "^16.0.0",
"eslint-plugin-vue": "7.17.0", "eslint": "^8.13.0",
"esno": "0.9.1", "eslint-define-config": "^1.1.1",
"fs-extra": "10.0.0", "eslint-plugin-vue": "^8.6.0",
"inquirer": "8.1.2", "esno": "^0.14.1",
"less": "4.1.1", "fs-extra": "^10.1.0",
"postcss": "8.3.6", "inquirer": "^8.2.2",
"rimraf": "3.0.2", "less": "^4.1.2",
"rollup-plugin-visualizer": "5.5.2", "postcss": "^8.4.12",
"typescript": "4.4.2", "rimraf": "^3.0.2",
"vite": "2.5.10", "rollup-plugin-visualizer": "^5.6.0",
"vite-plugin-compression": "0.3.5", "typescript": "^4.6.3",
"vite-plugin-html": "2.1.0", "vite": "^2.9.15",
"vite-plugin-imagemin": "0.4.5", "vite-plugin-compression": "^0.5.1",
"vite-plugin-purge-icons": "0.7.0", "vite-plugin-html": "^3.2.0",
"vite-plugin-pwa": "0.11.2", "vite-plugin-imagemin": "^0.6.1",
"vite-plugin-style-import": "1.2.1", "vite-plugin-purge-icons": "^0.8.2",
"vite-plugin-svg-icons": "1.0.4", "vite-plugin-pwa": "^0.12.3",
"vite-plugin-theme": "0.8.1", "vite-plugin-style-import": "^2.0.0",
"vite-plugin-windicss": "1.4.3", "vite-plugin-svg-icons": "^2.0.1",
"vue-eslint-parser": "7.11.0", "vite-plugin-theme": "^0.8.6",
"vue-tsc": "0.3.0" "vite-plugin-windicss": "^1.8.7",
"vue-eslint-parser": "^8.3.0",
"vue-tsc": "^1.0.9",
"vite-plugin-optimize-persist": "^0.1.2",
"vite-plugin-package-config": "^0.1.1"
}, },
"resolutions": { "resolutions": {
"bin-wrapper": "npm:bin-wrapper-china", "bin-wrapper": "npm:bin-wrapper-china",
"rollup": "2.56.2" "rollup": "2.72.0"
}, },
"engines": { "engines": {
"node": "12 || >=14" "node": "12 || >=14"
@ -108,5 +121,83 @@
"browserslist": [ "browserslist": [
"> 1%", "> 1%",
"last 2 versions" "last 2 versions"
] ],
"vite": {
"optimizeDeps": {
"include": [
"@ant-design/colors",
"@ant-design/icons-vue",
"@vueuse/core",
"@vueuse/shared",
"@zxcvbn-ts/core",
"ant-design-vue",
"axios",
"cropperjs",
"crypto-js/aes",
"crypto-js/enc-base64",
"crypto-js/enc-utf8",
"crypto-js/md5",
"crypto-js/mode-ecb",
"crypto-js/pad-pkcs7",
"echarts",
"echarts/charts",
"echarts/components",
"echarts/core",
"echarts/renderers",
"lodash-es",
"nprogress",
"path-to-regexp",
"pinia",
"print-js",
"qrcode",
"qs",
"resize-observer-polyfill",
"sortablejs",
"tinymce/icons/default/icons",
"tinymce/plugins/advlist",
"tinymce/plugins/anchor",
"tinymce/plugins/autolink",
"tinymce/plugins/autosave",
"tinymce/plugins/code",
"tinymce/plugins/codesample",
"tinymce/plugins/contextmenu",
"tinymce/plugins/directionality",
"tinymce/plugins/fullscreen",
"tinymce/plugins/hr",
"tinymce/plugins/image",
"tinymce/plugins/insertdatetime",
"tinymce/plugins/link",
"tinymce/plugins/lists",
"tinymce/plugins/media",
"tinymce/plugins/nonbreaking",
"tinymce/plugins/noneditable",
"tinymce/plugins/pagebreak",
"tinymce/plugins/paste",
"tinymce/plugins/preview",
"tinymce/plugins/print",
"tinymce/plugins/save",
"tinymce/plugins/searchreplace",
"tinymce/plugins/spellchecker",
"tinymce/plugins/tabfocus",
"tinymce/plugins/table",
"tinymce/plugins/template",
"tinymce/plugins/textcolor",
"tinymce/plugins/textpattern",
"tinymce/plugins/visualblocks",
"tinymce/plugins/visualchars",
"tinymce/plugins/wordcount",
"tinymce/themes/silver",
"tinymce/tinymce",
"vditor",
"vue",
"vue-i18n",
"vue-router",
"vue-types",
"vxe-table",
"vxe-table-plugin-antd",
"vxe-table-plugin-export-xlsx",
"xe-utils"
]
}
}
} }

9674
pnpm-lock.yaml

File diff suppressed because it is too large Load Diff

BIN
public/favicon.ico

Binary file not shown.

Before

Width:  |  Height:  |  Size: 272 KiB

After

Width:  |  Height:  |  Size: 254 KiB

BIN
public/resource/img/logo.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 456 KiB

2
public/resource/img/logo.svg

@ -1,2 +0,0 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1649230397851" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2943" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css">@font-face { font-family: feedback-iconfont; src: url("//at.alicdn.com/t/font_1031158_u69w8yhxdu.woff2?t=1630033759944") format("woff2"), url("//at.alicdn.com/t/font_1031158_u69w8yhxdu.woff?t=1630033759944") format("woff"), url("//at.alicdn.com/t/font_1031158_u69w8yhxdu.ttf?t=1630033759944") format("truetype"); }
</style></defs><path d="M687.3088 801.7664a9.6 9.6 0 0 1-9.6-9.6v-138.3168c0-8.448 4.5568-16.3072 11.8528-20.5568l105.984-61.184a4.5824 4.5824 0 0 0 2.2784-3.9424v-118.1952a9.6 9.6 0 0 1 19.2 0v118.1952a23.7824 23.7824 0 0 1-11.8784 20.5568l-105.9584 61.184a4.5824 4.5824 0 0 0-2.2784 3.9424v138.3168a9.6 9.6 0 0 1-9.6 9.6z" fill="#008CFF" p-id="2944"></path><path d="M704.128 778.1632a23.808 23.808 0 1 1-33.6896 33.664 23.808 23.808 0 0 1 33.6896-33.664zM824.2432 419.8144a23.808 23.808 0 1 1-33.6896 33.664 23.808 23.808 0 0 1 33.6896-33.664z" fill="#1AC678" p-id="2945"></path><path d="M191.9744 673.92a9.6 9.6 0 0 1-9.6-9.6v-138.3168c0-8.4736 4.5568-16.3328 11.8784-20.5568l105.9584-61.184a4.5824 4.5824 0 0 0 2.2784-3.9424v-118.1952a9.6 9.6 0 0 1 19.2 0V440.32a23.7824 23.7824 0 0 1-11.8784 20.5568l-105.9584 61.184a4.5824 4.5824 0 0 0-2.2784 3.9424v138.3168a9.6 9.6 0 0 1-9.6 9.6z" fill="#008CFF" p-id="2946"></path><path d="M208.7936 650.3168a23.808 23.808 0 1 1-33.6896 33.664 23.808 23.808 0 0 1 33.6896-33.664zM328.9088 291.9424a23.808 23.808 0 1 1-33.6896 33.664 23.808 23.808 0 0 1 33.6896-33.664z" fill="#1AC678" p-id="2947"></path><path d="M351.1296 763.008a9.6 9.6 0 0 1-9.6-9.5232l-1.7408-226.1248a9.6256 9.6256 0 0 1 9.5232-9.6768c4.736 0.5632 9.6256 4.2496 9.6768 9.5232l1.7408 226.1248a9.6256 9.6256 0 0 1-9.5232 9.6768h-0.0768z" fill="#008CFF" p-id="2948"></path><path d="M366.2336 510.4384a23.808 23.808 0 1 1-33.6896 33.664 23.808 23.808 0 0 1 33.6896-33.664zM367.9744 736.5632a23.808 23.808 0 1 1-33.6896 33.664 23.808 23.808 0 0 1 33.6896-33.664z" fill="#1AC678" p-id="2949"></path><path d="M813.9264 666.2656a23.808 23.808 0 1 1-33.6896 33.664 23.808 23.808 0 0 1 33.6896-33.664z" fill="#1AC678" p-id="2950"></path><path d="M640.1536 144.2048a6.4 6.4 0 0 1-3.2-0.8704L512 71.2448 387.0464 143.36a6.4 6.4 0 1 1-6.4-11.0592l128.1536-73.9584a6.3488 6.3488 0 0 1 6.4 0l128.1536 73.9584c3.072 1.7664 4.1216 5.6832 2.3296 8.7296a6.4 6.4 0 0 1-5.5296 3.1744zM512 966.5536a6.272 6.272 0 0 1-3.2-0.8704l-128.1536-73.9584a6.3488 6.3488 0 0 1-2.3296-8.7296 6.4 6.4 0 0 1 8.7296-2.3296L512 952.7552l124.9536-72.1152a6.4 6.4 0 0 1 6.4 11.0592l-128.1536 73.9584a6.0672 6.0672 0 0 1-3.2 0.896zM123.8784 442.2912a6.4 6.4 0 0 1-6.4-6.4l0.0256-147.968a6.4 6.4 0 0 1 3.2-5.5296L248.8064 208.384a6.4 6.4 0 1 1 6.4 11.0592l-124.9024 72.192-0.0256 144.256a6.4 6.4 0 0 1-6.4 6.4zM771.9936 816.4864a6.4 6.4 0 0 1-3.2-11.9296l124.9024-72.1664 0.0256-144.256a6.4 6.4 0 0 1 12.8 0l-0.0256 147.968a6.4 6.4 0 0 1-3.2 5.5296l-128.1024 74.0096a6.656 6.656 0 0 1-3.2 0.8448zM252.0064 816.4864a6.4 6.4 0 0 1-3.2-0.8704l-128.1024-74.0096a6.4 6.4 0 0 1-3.2-5.5296l-0.0256-147.968a6.4 6.4 0 0 1 12.8 0l0.0256 144.256 124.9024 72.1664c3.072 1.7664 4.1216 5.6832 2.3296 8.7296a6.3232 6.3232 0 0 1-5.5296 3.2256zM900.1216 442.2912a6.4 6.4 0 0 1-6.4-6.4l-0.0256-144.256-124.9024-72.1664a6.4 6.4 0 0 1 6.4-11.0592l128.1024 74.0096a6.4 6.4 0 0 1 3.2 5.5296l0.0256 147.968a6.4 6.4 0 0 1-6.4 6.3744z" fill="#00CFFF" p-id="2951"></path><path d="M628.7872 258.176L516.8128 193.536a9.6 9.6 0 0 0-9.6 0l-111.9744 64.64a9.6256 9.6256 0 0 0-4.8128 8.32v129.3056c0 3.4304 1.8176 6.6048 4.8128 8.32l111.9744 64.64a9.8304 9.8304 0 0 0 9.6256 0l111.9744-64.64a9.6256 9.6256 0 0 0 4.8128-8.32V266.496a9.7536 9.7536 0 0 0-4.8384-8.32z m-14.4128 132.096L512 449.3568l-102.3744-59.1104v-118.2208L512 212.9408l102.3744 59.0848v118.2464z" fill="#008CFF" p-id="2952"></path><path d="M512 470.0416a9.6 9.6 0 0 1-9.6-9.6v-123.7504l-107.1872-61.8752a9.6 9.6 0 1 1 9.6-16.6144l111.9744 64.64a9.6256 9.6256 0 0 1 4.8128 8.32v129.3056a9.6 9.6 0 0 1-9.6 9.5744z" fill="#008CFF" p-id="2953"></path><path d="M640.7936 249.6768a23.808 23.808 0 1 1-33.6896 33.664 23.808 23.808 0 0 1 33.6896-33.664z" fill="#1AC678" p-id="2954"></path><path d="M512 340.7616a9.5744 9.5744 0 0 1-4.8128-17.92l100.352-57.9328a9.5744 9.5744 0 1 1 9.6 16.6144l-100.352 57.9328a9.5488 9.5488 0 0 1-4.7872 1.3056z" fill="#008CFF" p-id="2955"></path><path d="M521.0112 601.6256a9.5744 9.5744 0 0 1-4.8128-17.92l130.048-75.008a9.5744 9.5744 0 1 1 9.6 16.6144l-130.048 75.008a9.2928 9.2928 0 0 1-4.7872 1.3056z" fill="#008CFF" p-id="2956"></path><path d="M523.4688 872.064a9.6 9.6 0 0 1-9.6-9.5232l-2.4576-270.4384a9.6 9.6 0 0 1 9.5232-9.6768c4.8128 0.5888 9.6256 4.224 9.6768 9.5232l2.4576 270.4384a9.6 9.6 0 0 1-9.5232 9.6768h-0.0768z" fill="#008CFF" p-id="2957"></path><path d="M537.8304 575.1808a23.808 23.808 0 1 1-33.6896 33.664 23.808 23.808 0 0 1 33.6896-33.664zM540.1344 845.7984a23.808 23.808 0 1 1-33.6896 33.664 23.808 23.808 0 0 1 33.6896-33.664zM667.8784 500.1728a23.808 23.808 0 1 1-33.6896 33.664 23.808 23.808 0 0 1 33.6896-33.664z" fill="#1AC678" p-id="2958"></path></svg>

Before

Width:  |  Height:  |  Size: 5.3 KiB

BIN
public/resource/img/pwa-192x192.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.3 KiB

After

Width:  |  Height:  |  Size: 38 KiB

BIN
public/resource/img/pwa-512x512.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 155 KiB

1
src/App.vue

@ -11,6 +11,7 @@
import { AppProvider } from '/@/components/Application'; import { AppProvider } from '/@/components/Application';
import { useTitle } from '/@/hooks/web/useTitle'; import { useTitle } from '/@/hooks/web/useTitle';
import { useLocale } from '/@/locales/useLocale'; import { useLocale } from '/@/locales/useLocale';
import 'dayjs/locale/zh-cn';
// //
const { getAntdLocale } = useLocale(); const { getAntdLocale } = useLocale();

2
src/api/platform/core/controller/user.ts

@ -50,7 +50,7 @@ export const login = (params: LoginParams, options?: boolean | RequestOptions) =
export const getUserInfo = () => defHttp.get<User>({ url: Api.getUserInfo }); export const getUserInfo = () => defHttp.get<User>({ url: Api.getUserInfo });
/** 登出 */ /** 登出 */
export const logout = () => defHttp.delete({ url: Api.logout }); export const logout = () => defHttp.delete({ url: Api.logout }, { errorMessageMode: 'none'});
/** 获取验证码 */ /** 获取验证码 */
export const getCaptcha = () => defHttp.get<Captcha>({ url: `${Api.getCaptcha}?key=${Date.now()}` }); export const getCaptcha = () => defHttp.get<Captcha>({ url: `${Api.getCaptcha}?key=${Date.now()}` });

BIN
src/assets/images/login-ad-mini.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 525 KiB

BIN
src/assets/images/logo.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 456 KiB

2
src/assets/images/logo.svg

@ -1,2 +0,0 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1649230397851" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2943" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css">@font-face { font-family: feedback-iconfont; src: url("//at.alicdn.com/t/font_1031158_u69w8yhxdu.woff2?t=1630033759944") format("woff2"), url("//at.alicdn.com/t/font_1031158_u69w8yhxdu.woff?t=1630033759944") format("woff"), url("//at.alicdn.com/t/font_1031158_u69w8yhxdu.ttf?t=1630033759944") format("truetype"); }
</style></defs><path d="M687.3088 801.7664a9.6 9.6 0 0 1-9.6-9.6v-138.3168c0-8.448 4.5568-16.3072 11.8528-20.5568l105.984-61.184a4.5824 4.5824 0 0 0 2.2784-3.9424v-118.1952a9.6 9.6 0 0 1 19.2 0v118.1952a23.7824 23.7824 0 0 1-11.8784 20.5568l-105.9584 61.184a4.5824 4.5824 0 0 0-2.2784 3.9424v138.3168a9.6 9.6 0 0 1-9.6 9.6z" fill="#008CFF" p-id="2944"></path><path d="M704.128 778.1632a23.808 23.808 0 1 1-33.6896 33.664 23.808 23.808 0 0 1 33.6896-33.664zM824.2432 419.8144a23.808 23.808 0 1 1-33.6896 33.664 23.808 23.808 0 0 1 33.6896-33.664z" fill="#1AC678" p-id="2945"></path><path d="M191.9744 673.92a9.6 9.6 0 0 1-9.6-9.6v-138.3168c0-8.4736 4.5568-16.3328 11.8784-20.5568l105.9584-61.184a4.5824 4.5824 0 0 0 2.2784-3.9424v-118.1952a9.6 9.6 0 0 1 19.2 0V440.32a23.7824 23.7824 0 0 1-11.8784 20.5568l-105.9584 61.184a4.5824 4.5824 0 0 0-2.2784 3.9424v138.3168a9.6 9.6 0 0 1-9.6 9.6z" fill="#008CFF" p-id="2946"></path><path d="M208.7936 650.3168a23.808 23.808 0 1 1-33.6896 33.664 23.808 23.808 0 0 1 33.6896-33.664zM328.9088 291.9424a23.808 23.808 0 1 1-33.6896 33.664 23.808 23.808 0 0 1 33.6896-33.664z" fill="#1AC678" p-id="2947"></path><path d="M351.1296 763.008a9.6 9.6 0 0 1-9.6-9.5232l-1.7408-226.1248a9.6256 9.6256 0 0 1 9.5232-9.6768c4.736 0.5632 9.6256 4.2496 9.6768 9.5232l1.7408 226.1248a9.6256 9.6256 0 0 1-9.5232 9.6768h-0.0768z" fill="#008CFF" p-id="2948"></path><path d="M366.2336 510.4384a23.808 23.808 0 1 1-33.6896 33.664 23.808 23.808 0 0 1 33.6896-33.664zM367.9744 736.5632a23.808 23.808 0 1 1-33.6896 33.664 23.808 23.808 0 0 1 33.6896-33.664z" fill="#1AC678" p-id="2949"></path><path d="M813.9264 666.2656a23.808 23.808 0 1 1-33.6896 33.664 23.808 23.808 0 0 1 33.6896-33.664z" fill="#1AC678" p-id="2950"></path><path d="M640.1536 144.2048a6.4 6.4 0 0 1-3.2-0.8704L512 71.2448 387.0464 143.36a6.4 6.4 0 1 1-6.4-11.0592l128.1536-73.9584a6.3488 6.3488 0 0 1 6.4 0l128.1536 73.9584c3.072 1.7664 4.1216 5.6832 2.3296 8.7296a6.4 6.4 0 0 1-5.5296 3.1744zM512 966.5536a6.272 6.272 0 0 1-3.2-0.8704l-128.1536-73.9584a6.3488 6.3488 0 0 1-2.3296-8.7296 6.4 6.4 0 0 1 8.7296-2.3296L512 952.7552l124.9536-72.1152a6.4 6.4 0 0 1 6.4 11.0592l-128.1536 73.9584a6.0672 6.0672 0 0 1-3.2 0.896zM123.8784 442.2912a6.4 6.4 0 0 1-6.4-6.4l0.0256-147.968a6.4 6.4 0 0 1 3.2-5.5296L248.8064 208.384a6.4 6.4 0 1 1 6.4 11.0592l-124.9024 72.192-0.0256 144.256a6.4 6.4 0 0 1-6.4 6.4zM771.9936 816.4864a6.4 6.4 0 0 1-3.2-11.9296l124.9024-72.1664 0.0256-144.256a6.4 6.4 0 0 1 12.8 0l-0.0256 147.968a6.4 6.4 0 0 1-3.2 5.5296l-128.1024 74.0096a6.656 6.656 0 0 1-3.2 0.8448zM252.0064 816.4864a6.4 6.4 0 0 1-3.2-0.8704l-128.1024-74.0096a6.4 6.4 0 0 1-3.2-5.5296l-0.0256-147.968a6.4 6.4 0 0 1 12.8 0l0.0256 144.256 124.9024 72.1664c3.072 1.7664 4.1216 5.6832 2.3296 8.7296a6.3232 6.3232 0 0 1-5.5296 3.2256zM900.1216 442.2912a6.4 6.4 0 0 1-6.4-6.4l-0.0256-144.256-124.9024-72.1664a6.4 6.4 0 0 1 6.4-11.0592l128.1024 74.0096a6.4 6.4 0 0 1 3.2 5.5296l0.0256 147.968a6.4 6.4 0 0 1-6.4 6.3744z" fill="#00CFFF" p-id="2951"></path><path d="M628.7872 258.176L516.8128 193.536a9.6 9.6 0 0 0-9.6 0l-111.9744 64.64a9.6256 9.6256 0 0 0-4.8128 8.32v129.3056c0 3.4304 1.8176 6.6048 4.8128 8.32l111.9744 64.64a9.8304 9.8304 0 0 0 9.6256 0l111.9744-64.64a9.6256 9.6256 0 0 0 4.8128-8.32V266.496a9.7536 9.7536 0 0 0-4.8384-8.32z m-14.4128 132.096L512 449.3568l-102.3744-59.1104v-118.2208L512 212.9408l102.3744 59.0848v118.2464z" fill="#008CFF" p-id="2952"></path><path d="M512 470.0416a9.6 9.6 0 0 1-9.6-9.6v-123.7504l-107.1872-61.8752a9.6 9.6 0 1 1 9.6-16.6144l111.9744 64.64a9.6256 9.6256 0 0 1 4.8128 8.32v129.3056a9.6 9.6 0 0 1-9.6 9.5744z" fill="#008CFF" p-id="2953"></path><path d="M640.7936 249.6768a23.808 23.808 0 1 1-33.6896 33.664 23.808 23.808 0 0 1 33.6896-33.664z" fill="#1AC678" p-id="2954"></path><path d="M512 340.7616a9.5744 9.5744 0 0 1-4.8128-17.92l100.352-57.9328a9.5744 9.5744 0 1 1 9.6 16.6144l-100.352 57.9328a9.5488 9.5488 0 0 1-4.7872 1.3056z" fill="#008CFF" p-id="2955"></path><path d="M521.0112 601.6256a9.5744 9.5744 0 0 1-4.8128-17.92l130.048-75.008a9.5744 9.5744 0 1 1 9.6 16.6144l-130.048 75.008a9.2928 9.2928 0 0 1-4.7872 1.3056z" fill="#008CFF" p-id="2956"></path><path d="M523.4688 872.064a9.6 9.6 0 0 1-9.6-9.5232l-2.4576-270.4384a9.6 9.6 0 0 1 9.5232-9.6768c4.8128 0.5888 9.6256 4.224 9.6768 9.5232l2.4576 270.4384a9.6 9.6 0 0 1-9.5232 9.6768h-0.0768z" fill="#008CFF" p-id="2957"></path><path d="M537.8304 575.1808a23.808 23.808 0 1 1-33.6896 33.664 23.808 23.808 0 0 1 33.6896-33.664zM540.1344 845.7984a23.808 23.808 0 1 1-33.6896 33.664 23.808 23.808 0 0 1 33.6896-33.664zM667.8784 500.1728a23.808 23.808 0 1 1-33.6896 33.664 23.808 23.808 0 0 1 33.6896-33.664z" fill="#1AC678" p-id="2958"></path></svg>

Before

Width:  |  Height:  |  Size: 5.3 KiB

150
src/assets/styles/ant-design/btn.less

@ -8,19 +8,16 @@
&-primary { &-primary {
color: @white; color: @white;
background-color: @button-primary-color; background-color: @button-primary-color;
border-width: 0;
&:hover, &:hover,
&:focus { &:focus {
color: @white !important; color: @white;
background-color: @button-primary-hover-color; background-color: @button-primary-hover-color;
} }
}
&[disabled], &-primary:not(&-background-ghost):not([disabled]) {
&[disabled]:hover { color: @white;
color: @white;
background-color: fade(@button-primary-color, 40%);
}
} }
&-default { &-default {
@ -37,7 +34,7 @@
} }
[data-theme='light'] &.ant-btn-link.is-disabled { [data-theme='light'] &.ant-btn-link.is-disabled {
color: rgba(0, 0, 0, 0.25); color: rgb(0 0 0 / 25%);
text-shadow: none; text-shadow: none;
cursor: not-allowed !important; cursor: not-allowed !important;
background-color: transparent !important; background-color: transparent !important;
@ -46,7 +43,7 @@
} }
[data-theme='dark'] &.ant-btn-link.is-disabled { [data-theme='dark'] &.ant-btn-link.is-disabled {
color: rgba(255, 255, 255, 0.25) !important; color: rgb(255 255 255 / 25%) !important;
text-shadow: none; text-shadow: none;
cursor: not-allowed !important; cursor: not-allowed !important;
background-color: transparent !important; background-color: transparent !important;
@ -54,6 +51,8 @@
box-shadow: none; box-shadow: none;
} }
// color: @white;
&-success.ant-btn-link:not([disabled='disabled']) { &-success.ant-btn-link:not([disabled='disabled']) {
color: @button-success-color; color: @button-success-color;
@ -62,6 +61,10 @@
color: @button-success-hover-color; color: @button-success-hover-color;
border-color: transparent; border-color: transparent;
} }
&:active {
color: @button-success-active-color;
}
} }
&-success.ant-btn-link.ant-btn-loading, &-success.ant-btn-link.ant-btn-loading,
@ -74,11 +77,11 @@
} }
} }
&-success:not(.ant-btn-link) { &-success:not(.ant-btn-link, .is-disabled) {
color: @white; color: @white;
background-color: @button-success-color; background-color: @button-success-color;
border-color: @button-success-color; border-color: @button-success-color;
border-width: 0; //border-width: 0;
&:hover, &:hover,
&:focus { &:focus {
@ -87,11 +90,9 @@
border-color: @button-success-hover-color; border-color: @button-success-hover-color;
} }
&[disabled], &:active {
&[disabled]:hover { background-color: @button-success-active-color;
color: @white; border-color: @button-success-active-color;
background-color: fade(@button-success-color, 40%);
border-color: fade(@button-success-color, 40%);
} }
} }
@ -103,13 +104,17 @@
color: @button-warn-hover-color; color: @button-warn-hover-color;
border-color: transparent; border-color: transparent;
} }
&:active {
color: @button-warn-active-color;
}
} }
&-warning:not(.ant-btn-link) { &-warning:not(.ant-btn-link, .is-disabled) {
color: @white; color: @white;
background-color: @button-warn-color; background-color: @button-warn-color;
border-color: @button-warn-color; border-color: @button-warn-color;
border-width: 0; //border-width: 0;
&:hover, &:hover,
&:focus { &:focus {
@ -118,11 +123,9 @@
border-color: @button-warn-hover-color; border-color: @button-warn-hover-color;
} }
&[disabled], &:active {
&[disabled]:hover { background-color: @button-warn-active-color;
color: @white; border-color: @button-warn-active-color;
background-color: fade(@button-warn-color, 40%);
border-color: fade(@button-warn-color, 40%);
} }
} }
@ -134,13 +137,17 @@
color: @button-error-hover-color; color: @button-error-hover-color;
border-color: transparent; border-color: transparent;
} }
&:active {
color: @button-error-active-color;
}
} }
&-error:not(.ant-btn-link) { &-error:not(.ant-btn-link, .is-disabled) {
color: @white; color: @white;
background-color: @button-error-color; background-color: @button-error-color;
border-color: @button-error-color; border-color: @button-error-color;
border-width: 0; //border-width: 0;
&:hover, &:hover,
&:focus { &:focus {
@ -149,41 +156,98 @@
border-color: @button-error-hover-color; border-color: @button-error-hover-color;
} }
&[disabled], &:active {
&[disabled]:hover { background-color: @button-error-active-color;
color: @white; border-color: @button-error-active-color;
background-color: fade(@button-error-color, 40%);
border-color: fade(@button-error-color, 40%);
} }
} }
&-background-ghost.ant-btn-link, &-background-ghost {
&.ant-btn-dashed:not([disabled='disabled']) { border-width: 1px;
color: @text-color-call-out; background-color: transparent !important;
&:hover { &[disabled],
color: @button-primary-color; &[disabled]:hover {
color: fade(@white, 40%) !important;
background-color: transparent !important;
border-color: fade(@white, 40%) !important;
} }
} }
&-background-ghost { &-dashed&-background-ghost,
&-default&-background-ghost {
color: @button-ghost-color; color: @button-ghost-color;
background-color: transparent;
border-color: @button-ghost-color; border-color: @button-ghost-color;
border-width: 1px;
&:hover, &:hover,
&:focus { &:focus {
color: @button-ghost-hover-color !important; color: @button-ghost-hover-color;
background-color: @button-ghost-hover-bg-color;
border-color: @button-ghost-hover-color; border-color: @button-ghost-hover-color;
} }
&:active {
color: @button-ghost-active-color;
border-color: @button-ghost-active-color;
}
&[disabled], &[disabled],
&[disabled]:hover { &[disabled]:hover {
color: @button-ghost-color; color: fade(@white, 40%) !important;
background-color: fade(@white, 40%); border-color: fade(@white, 40%) !important;
border-color: fade(@button-ghost-color, 40%); }
}
&-background-ghost&-success:not(.ant-btn-link) {
color: @button-success-color;
background-color: transparent;
border-color: @button-success-color;
border-width: 1px;
&:hover,
&:focus {
color: @button-success-hover-color !important;
border-color: @button-success-hover-color;
}
&:active {
color: @button-success-active-color;
border-color: @button-success-active-color;
}
}
&-background-ghost&-warning:not(.ant-btn-link) {
color: @button-warn-color;
background-color: transparent;
border-color: @button-warn-color;
border-width: 1px;
&:hover,
&:focus {
color: @button-warn-hover-color !important;
border-color: @button-warn-hover-color;
}
&:active {
color: @button-warn-active-color;
border-color: @button-warn-active-color;
}
}
&-background-ghost&-error:not(.ant-btn-link) {
color: @button-error-color;
background-color: transparent;
border-color: @button-error-color;
border-width: 1px;
&:hover,
&:focus {
color: @button-error-hover-color !important;
border-color: @button-error-hover-color;
}
&:active {
color: @button-error-active-color;
border-color: @button-error-active-color;
} }
} }

8
src/assets/styles/ant-design/index.less

@ -61,3 +61,11 @@ span.anticon:not(.app-iconify) {
border-top: 0 !important; border-top: 0 !important;
border-left: 0 !important; border-left: 0 !important;
} }
.ant-form-item-control-input-content {
> div {
> div {
max-width: 100%;
}
}
}

3
src/assets/styles/ant-design/input.less

@ -1,7 +1,8 @@
@import (reference) '../color.less'; @import (reference) '../color.less';
.ant-input { .ant-input {
&-number { &-number,
&-number-group-wrapper {
min-width: 110px; min-width: 110px;
} }
} }

5
src/assets/styles/color.less

@ -113,19 +113,24 @@ html {
@button-primary-color: @primary-color; @button-primary-color: @primary-color;
@button-primary-hover-color: darken(@primary-color, 5%); @button-primary-hover-color: darken(@primary-color, 5%);
@button-primary-active-color: darken(@primary-color, 5%);
@button-ghost-color: @primary-color; @button-ghost-color: @primary-color;
@button-ghost-hover-color: lighten(@primary-color, 10%); @button-ghost-hover-color: lighten(@primary-color, 10%);
@button-ghost-hover-bg-color: #e1ebf6; @button-ghost-hover-bg-color: #e1ebf6;
@button-ghost-active-color: darken(@white, 10%);
@button-success-color: @success-color; @button-success-color: @success-color;
@button-success-hover-color: darken(@success-color, 10%); @button-success-hover-color: darken(@success-color, 10%);
@button-success-active-color: darken(@success-color, 10%);
@button-warn-color: @warning-color; @button-warn-color: @warning-color;
@button-warn-hover-color: darken(@warning-color, 10%); @button-warn-hover-color: darken(@warning-color, 10%);
@button-warn-active-color: darken(@warning-color, 10%);
@button-error-color: @error-color; @button-error-color: @error-color;
@button-error-hover-color: darken(@error-color, 10%); @button-error-hover-color: darken(@error-color, 10%);
@button-error-active-color: darken(@error-color, 10%);
@button-cancel-color: @text-color-call-out; @button-cancel-color: @text-color-call-out;
@button-cancel-bg-color: @white; @button-cancel-bg-color: @white;

188
src/assets/styles/common.less

@ -50,3 +50,191 @@
opacity: 0.75; opacity: 0.75;
} }
} }
// =================================
// Custom Basic Table
// =================================
html[data-theme='dark'] {
.@{custom-basic-table-prefix-cls} {
// [黑暗模式]table顶部插槽多选栏样式
.alert {
background-color: #323232;
border-color: #424242;
}
}
}
.@{custom-basic-table-prefix-cls} {
max-width: 100%;
height: 100%;
&-row__striped {
td {
background-color: @app-content-background;
}
}
&-form-container {
padding: 16px;
.ant-form {
width: 100%;
padding: 12px 10px 6px 10px;
margin-bottom: 8px;
background-color: @component-background;
border-radius: 2px;
}
}
.ant-tag {
margin-right: 0;
}
.ant-table-wrapper {
padding: 6px;
background-color: @component-background;
border-radius: 2px;
.ant-table-title {
min-height: 40px;
padding: 0 0 8px !important;
}
.ant-table.ant-table-bordered .ant-table-title {
border: none !important;
}
}
.ant-table {
width: 100%;
overflow-x: hidden;
&-title {
display: flex;
padding: 8px 6px;
border-bottom: none;
justify-content: space-between;
align-items: center;
}
.ant-table-tbody > tr.ant-table-row-selected td {
background-color: fade(@primary-color, 8%) !important;
}
}
.ant-pagination {
margin: 10px 0 0;
}
.ant-table-footer {
padding: 0;
.ant-table-wrapper {
padding: 0;
}
table {
border: none !important;
}
.ant-table-body {
overflow-x: hidden !important;
}
td {
padding: 12px 8px;
}
}
// [明亮模式]table顶部插槽多选栏样式
.alert {
height: 38px;
background-color: #f3f3f3;
border-color: #e3e3e3;
}
&--inset {
.ant-table-wrapper {
padding: 0;
}
}
// 表格头部工具栏
&__toolbar {
flex: 1;
display: flex;
align-items: center;
justify-content: flex-end;
> * {
margin-right: 8px;
}
}
// 表格列动作列
&__action {
display: flex;
align-items: center;
height: 22px;
.action-divider {
display: table;
}
&.left {
justify-content: flex-start;
}
&.center {
justify-content: center;
}
&.right {
justify-content: flex-end;
}
button {
display: flex;
align-items: center;
span {
margin-left: 0 !important;
}
}
button.ant-btn-circle {
span {
margin: auto !important;
}
}
.ant-divider,
.ant-divider-vertical {
margin: 0 2px;
}
.icon-more {
transform: rotate(90deg);
svg {
font-size: 1.1em;
font-weight: 700;
}
}
}
// 表格设置
&__settings {
& > * {
margin-right: 12px;
}
svg {
width: 1.3em;
height: 1.3em;
}
}
}

6
src/assets/styles/index.less

@ -3,7 +3,7 @@
@import 'common.less'; @import 'common.less';
@import 'ant-design/index.less'; @import 'ant-design/index.less';
@import 'theme.less'; @import 'theme.less';
@import 'kicc.less'; @import 'vxe-table/index.less';
input:-webkit-autofill { input:-webkit-autofill {
-webkit-box-shadow: 0 0 0 1000px white inset !important; -webkit-box-shadow: 0 0 0 1000px white inset !important;
@ -22,8 +22,8 @@ html,
body { body {
width: 100%; width: 100%;
height: 100%; height: 100%;
overflow: visible !important; overflow: visible;
overflow-x: hidden !important; overflow-x: hidden;
&.color-weak { &.color-weak {
filter: invert(80%); filter: invert(80%);

268
src/assets/styles/kicc.less

@ -1,268 +0,0 @@
/**
* @program: kicc-ui
* @description: kicc系统相关样式
* 在kicc系统中全局调用的样式全部在这里配置
* @author: entfrm开发团队-王翔
* @create: 2022/4/7
*/
.table-settings {
& > * {
margin-right: 12px;
}
svg {
width: 1.3em;
height: 1.3em;
}
}
// 基础表格样式
[data-theme='dark'] {
.ant-table-tbody > tr:hover.ant-table-row-selected > td,
.ant-table-tbody > tr.ant-table-row-selected td {
background-color: #262626;
}
.@{basic-table-prefix-cls} {
// [黑暗模式]table顶部插槽多选栏样式
.alert {
background-color: #323232;
border-color: #424242;
}
}
}
.@{basic-table-prefix-cls} {
max-width: 100%;
&-row__striped {
td {
background-color: @app-content-background;
}
}
&-form-container {
padding: 16px;
.ant-form {
padding: 12px 10px 6px 10px;
margin-bottom: 16px;
background-color: @component-background;
border-radius: 2px;
}
}
.ant-tag {
margin-right: 0;
}
.ant-table-wrapper {
padding: 6px;
background-color: @component-background;
border-radius: 2px;
.ant-table-title {
min-height: 40px;
padding: 0 0 8px 0 !important;
}
.ant-table.ant-table-bordered .ant-table-title {
border: none !important;
}
}
.ant-table {
width: 100%;
overflow-x: hidden;
&-title {
display: flex;
padding: 8px 6px;
border-bottom: none;
justify-content: space-between;
align-items: center;
}
.ant-table-tbody > tr.ant-table-row-selected td {
background-color: fade(@primary-color, 8%) !important;
}
}
.ant-pagination {
margin: 10px 0 0 0;
}
.ant-table-footer {
padding: 0;
.ant-table-wrapper {
padding: 0;
}
table {
border: none !important;
}
.ant-table-body {
overflow-x: hidden !important;
overflow-y: scroll !important;
}
td {
padding: 12px 8px;
}
// [明亮模式]table顶部插槽多选栏样式
.alert {
height: 38px;
background-color: #f3f3f3;
border-color: #e3e3e3;
}
&--inset {
.ant-table-wrapper {
padding: 0;
}
}
}
// 表格头部样式
.@{basic-table-header-prefix-cls} {
&__toolbar {
flex: 1;
display: flex;
align-items: center;
justify-content: flex-end;
> * {
margin-right: 8px;
}
}
}
// 表格动作样式
.@{basic-table-action-prefix-cls} {
display: flex;
align-items: center;
.action-divider {
display: table;
}
&.left {
justify-content: flex-start;
}
&.center {
justify-content: center;
}
&.right {
justify-content: flex-end;
}
button {
display: flex;
align-items: center;
span {
margin-left: 0 !important;
}
}
button.ant-btn-circle {
span {
margin: auto !important;
}
}
.ant-divider,
.ant-divider-vertical {
margin: 0 2px;
}
.icon-more {
transform: rotate(90deg);
svg {
font-size: 1.1em;
font-weight: 700;
}
}
}
// 表格设置样式
.table-coulmn-drag-icon {
margin: 0 5px;
cursor: move;
}
.@{basic-column-setting-prefix-cls} {
&__popover-title {
position: relative;
display: flex;
align-items: center;
justify-content: space-between;
}
&__check-item {
display: flex;
align-items: center;
min-width: 100%;
padding: 4px 16px 8px 0;
.ant-checkbox-wrapper {
width: 100%;
&:hover {
color: @primary-color;
}
}
}
&__fixed-left,
&__fixed-right {
color: rgba(0, 0, 0, 0.45);
cursor: pointer;
&.active,
&:hover {
color: @primary-color;
}
&.disabled {
color: @disabled-color;
cursor: not-allowed;
}
}
&__fixed-right {
transform: rotate(180deg);
}
&__cloumn-list {
svg {
width: 1em !important;
height: 1em !important;
}
.ant-popover-inner-content {
// max-height: 360px;
padding-right: 0;
padding-left: 0;
// overflow: auto;
}
.ant-checkbox-group {
width: 100%;
min-width: 260px;
// flex-wrap: wrap;
}
.scrollbar {
height: 220px;
}
}
}
}

12
src/assets/styles/transition/fade.less

@ -1,3 +1,15 @@
.fade-transition {
&-enter-active,
&-leave-active {
transition: opacity 0.2s ease-in-out;
}
&-enter-from,
&-leave-to {
opacity: 0;
}
}
.fade-enter-active, .fade-enter-active,
.fade-leave-active { .fade-leave-active {
transition: opacity 0.2s ease-in-out; transition: opacity 0.2s ease-in-out;

7
src/assets/styles/var/index.less

@ -8,12 +8,13 @@
@import (reference) '../color.less'; @import (reference) '../color.less';
@import 'easing'; @import 'easing';
@import 'breakpoint'; @import 'breakpoint';
@import 'kicc';
@namespace: kicc; @namespace: kicc;
// tabs // tabs
@multiple-height: 30px; @multiple-height: 30px;
@multiple-card-height: 50px;
@multiple-smooth-height: 50px;
// headers // headers
@header-height: 48px; @header-height: 48px;
@ -44,3 +45,7 @@
@content(); @content();
} }
} }
@custom-basic-table-prefix-cls: ~'@{namespace}-custom-basic-table';
@vxe-table-prefix-cls: ~'@{namespace}-vxe-table';

13
src/assets/styles/var/kicc.less

@ -1,13 +0,0 @@
/**
* @program: kicc-ui
* @description: kicc系统相关变量定义
* 在kicc系统中全局调用的变量全部在这里配置
* @author: entfrm开发团队-王翔
* @create: 2022/4/7
*/
// 基础表格变量
@basic-table-prefix-cls: ~'@{namespace}-basic-table';
@basic-table-header-prefix-cls: ~'@{namespace}-basic-table-header';
@basic-table-action-prefix-cls: ~'@{namespace}-basic-table-action';
@basic-column-setting-prefix-cls: ~'@{namespace}-basic-column-setting';

1
src/assets/styles/vxe-table/index.less

@ -0,0 +1 @@
@import './vxe.dark.less';

110
src/assets/styles/vxe-table/vxe.dark.less

@ -0,0 +1,110 @@
[data-theme='dark'] .@{vxe-table-prefix-cls} {
@fontColor: #c9d1d9;
@bgColor: #151515;
@borderColor: #606060;
.vxe-cell--item,
.vxe-cell--title,
.vxe-cell,
.vxe-body--expanded-cell {
color: @fontColor;
}
.vxe-toolbar {
background-color: @bgColor;
}
.vxe-table--render-default .vxe-table--body-wrapper,
.vxe-table--render-default .vxe-table--footer-wrapper {
background-color: @bgColor;
}
// 外边框
.vxe-table--render-default .vxe-table--border-line {
border-color: @borderColor;
}
// header 下边框
.vxe-table .vxe-table--header-wrapper .vxe-table--header-border-line {
border-bottom-color: @borderColor;
}
// footer 上边框
.vxe-table--render-default .vxe-table--footer-wrapper {
border-top-color: @borderColor;
}
// 展开行 边框
.vxe-table--render-default .vxe-body--expanded-column {
border-bottom-color: @borderColor;
}
// 行斑马纹
.vxe-table--render-default .vxe-body--row.row--stripe {
background-color: #1e1e1e;
}
// 行hover
.vxe-table--render-default .vxe-body--row.row--hover {
background-color: #262626;
}
// 选中行
.vxe-table--render-default .vxe-body--row.row--checked {
background-color: #44403a;
&.row--hover {
background-color: #59524b;
}
}
.vxe-table--render-default.border--default .vxe-table--header-wrapper,
.vxe-table--render-default.border--full .vxe-table--header-wrapper,
.vxe-table--render-default.border--outer .vxe-table--header-wrapper {
background-color: #1d1d1d;
}
.vxe-table--render-default.border--default .vxe-body--column,
.vxe-table--render-default.border--default .vxe-footer--column,
.vxe-table--render-default.border--default .vxe-header--column,
.vxe-table--render-default.border--inner .vxe-body--column,
.vxe-table--render-default.border--inner .vxe-footer--column,
.vxe-table--render-default.border--inner .vxe-header--column {
background-image: linear-gradient(#1d1d1d, #1d1d1d);
}
// 列宽拖动
.vxe-header--column .vxe-resizable.is--line:before {
background-color: #505050;
}
// checkbox
.vxe-custom--option .vxe-checkbox--icon:before,
.vxe-export--panel-column-option .vxe-checkbox--icon:before,
.vxe-table--filter-option .vxe-checkbox--icon:before,
.vxe-table--render-default .vxe-cell--checkbox .vxe-checkbox--icon:before {
background-color: @bgColor;
border-color: @borderColor;
}
.vxe-toolbar .vxe-custom--option-wrapper {
background-color: @bgColor;
}
.vxe-button {
background-color: @bgColor;
border-color: @borderColor;
}
.vxe-button.type--button:not(.is--disabled):active {
background-color: @bgColor;
}
.vxe-toolbar .vxe-custom--wrapper.is--active > .vxe-button {
background-color: @bgColor;
}
.vxe-toolbar .vxe-custom--option-wrapper .vxe-custom--footer button {
color: @fontColor;
}
}

10
src/components/AMap/src/AMapDesigner/index.vue

@ -905,10 +905,10 @@
} }
.operatePanel { .operatePanel {
border: 1px solid @borderColor; background: @component-background;
border: 1px solid @component-background;
width: @siderWidth; width: @siderWidth;
height: 100%; height: 100%;
background-color: white;
position: absolute; position: absolute;
top: 0; top: 0;
right: 0; right: 0;
@ -928,7 +928,7 @@
font-family: Times; font-family: Times;
text-align: center; text-align: center;
border-radius: 4px 0 0 4px; border-radius: 4px 0 0 4px;
color: #fff; color: @component-background;
visibility: visible; visibility: visible;
left: 0; left: 0;
box-shadow: 0 2px 10px rgba(0,0,0,.2); box-shadow: 0 2px 10px rgba(0,0,0,.2);
@ -951,8 +951,8 @@
} }
.headToolbar { .headToolbar {
border: 1px solid @borderColor; background: @component-background;
right: @siderWidth; border: 1px solid @component-background;
} }
} }

20
src/components/AMap/src/amap.data.tsx

@ -1,9 +1,3 @@
import { BasicColumn } from '/@/components/Table';
import { commonUpload } from '/@/api/platform/core/controller/upload';
import { h } from 'vue';
import { Tag } from 'ant-design-vue';
import { VxeTableDefines } from 'vxe-table/types/table';
import Icon from '/@/components/Icon/index';
/** /**
* @program: kicc-ui * @program: kicc-ui
@ -12,6 +6,18 @@ import Icon from '/@/components/Icon/index';
* @create: 2022/5/22 * @create: 2022/5/22
*/ */
import { BasicColumn } from '/@/components/Table';
import { commonUpload } from '/@/api/platform/core/controller/upload';
import { h } from 'vue';
import { Tag } from 'ant-design-vue';
import type { VxeColumnProps, VxeColumnPropTypes } from 'vxe-table/types/column';
import Icon from '/@/components/Icon/index';
export interface ColumnOptions extends VxeColumnProps {
children?: ColumnOptions[]
slots?: VxeColumnPropTypes.Slots
}
export type MapPointType = { export type MapPointType = {
value: string; value: string;
label: string; label: string;
@ -247,7 +253,7 @@ export const taskPresetChildColumns: BasicColumn[] = [
]; ];
/** 地图标记点表格列 */ /** 地图标记点表格列 */
export const mapPointColumns: VxeTableDefines.ColumnOptions[] = [ export const mapPointColumns: ColumnOptions[] = [
{ {
field: 'drag', field: 'drag',
width: '50px', width: '50px',

14
src/components/AMap/src/components/MapPointModal.vue

@ -5,7 +5,7 @@
@register="registerModal" @register="registerModal"
@ok="handleSubmit" @ok="handleSubmit"
> >
<div class="pointBody"> <div :class="[prefixCls ,'pointBody']">
<div class="leftLayout"> <div class="leftLayout">
<AMapDesigner ref="AMapDesignerEl" <AMapDesigner ref="AMapDesignerEl"
:options="state.mapData" :options="state.mapData"
@ -28,12 +28,12 @@
import { AMapDesigner } from '/@/components/AMap'; import { AMapDesigner } from '/@/components/AMap';
import Sortable from 'sortablejs'; import Sortable from 'sortablejs';
import { VxeGridProps } from 'vxe-table'; import { VxeGridProps } from 'vxe-table';
import { mapPointColumns } from '../amap.data'; import { mapPointColumns, ColumnOptions } from '../amap.data';
import { defaultMapData } from '/@/enums/amapEnum'; import { defaultMapData } from '/@/enums/amapEnum';
import { add, cloneDeep, concat } from 'lodash-es'; import { add, cloneDeep } from 'lodash-es';
import { MapLogistic } from '/@/api/platform/common/entity/mapLogistic'; import { MapLogistic } from '/@/api/platform/common/entity/mapLogistic';
import { VxeTableDefines } from 'vxe-table/types/table';
import Icon from '/@/components/Icon/index'; import Icon from '/@/components/Icon/index';
import {useDesign} from '/@/hooks/web/useDesign';
/** 类型规范统一声明定义区域 */ /** 类型规范统一声明定义区域 */
interface WindowState { interface WindowState {
@ -46,6 +46,7 @@
const MapLogisticPointVxeGridEl = ref(); const MapLogisticPointVxeGridEl = ref();
const MapTaskPresetPointVxeGridEl = ref(); const MapTaskPresetPointVxeGridEl = ref();
const AMapDesignerEl = ref(); const AMapDesignerEl = ref();
const { prefixCls } = useDesign('vxe-table');
const state = reactive<WindowState>({ const state = reactive<WindowState>({
sortable: null, sortable: null,
gridOptions: { gridOptions: {
@ -66,7 +67,7 @@
state.mapData = cloneDeep(data.mapData); state.mapData = cloneDeep(data.mapData);
const props: Partial<ModalProps> = { confirmLoading: false }; const props: Partial<ModalProps> = { confirmLoading: false };
props.title = '标记点配置'; props.title = '标记点配置';
const columns: VxeTableDefines.ColumnOptions[] = [ const columns: ColumnOptions[] = [
{ {
field: 'drag', field: 'drag',
width: '50px', width: '50px',
@ -171,8 +172,5 @@
} }
} }
</style> </style>

2
src/components/Application/src/AppLogo.vue

@ -1,6 +1,6 @@
<template> <template>
<div class="anticon" :class="getAppLogoClass" @click="goHome"> <div class="anticon" :class="getAppLogoClass" @click="goHome">
<img src="../../../assets/images/logo.svg"> <img src="../../../assets/images/logo.png">
<div v-show="showTitle" class="ml-2 truncate md:opacity-100" :class="getTitleClass"> <div v-show="showTitle" class="ml-2 truncate md:opacity-100" :class="getTitleClass">
{{ title }} {{ title }}
</div> </div>

4
src/components/Button/src/BasicButton.vue

@ -1,8 +1,8 @@
<template> <template>
<Button v-bind="getBindValue" :class="getButtonClass" @click="onClick"> <Button v-bind="getBindValue" :class="getButtonClass" @click="onClick">
<template #default> <template #default="data">
<Icon v-if="preIcon" :icon="preIcon" :size="iconSize"/> <Icon v-if="preIcon" :icon="preIcon" :size="iconSize"/>
<slot/> <slot v-bind="data || {}"/>
<Icon v-if="postIcon" :icon="postIcon" :size="iconSize"/> <Icon v-if="postIcon" :icon="postIcon" :size="iconSize"/>
</template> </template>
</Button> </Button>

9
src/components/Button/src/props.ts

@ -1,5 +1,12 @@
const validColors = ['error', 'warning', 'success', ''] as const;
type ButtonColorType = typeof validColors[number];
export const buttonProps = { export const buttonProps = {
color: { type: String, validator: (v) => ['error', 'warning', 'success', ''].includes(v) }, color: {
type: String as PropType<ButtonColorType>,
validator: (v) => validColors.includes(v),
default: '',
},
loading: { type: Boolean }, loading: { type: Boolean },
disabled: { type: Boolean }, disabled: { type: Boolean },
/** /**

27
src/components/ClickOutSide/src/ClickOutSide.vue

@ -3,24 +3,17 @@
<slot/> <slot/>
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts" setup>
import { defineComponent, ref, onMounted } from 'vue'; import { ref, onMounted } from 'vue';
import { MaybeElementRef, onClickOutside } from '@vueuse/core'; import { onClickOutside } from '@vueuse/core';
export default defineComponent({ const emit = defineEmits(['mounted', 'clickOutside']);
name: 'ClickOutSide', const wrap = ref<ElRef>(null);
emits: ['mounted', 'clickOutside'],
setup(_, { emit }) {
const wrap = ref<ElRef>(null);
onClickOutside(wrap as MaybeElementRef, () => { onClickOutside(wrap, () => {
emit('clickOutside'); emit('clickOutside');
}); });
onMounted(() => {
emit('mounted');
});
return { wrap }; onMounted(() => {
}, emit('mounted');
}); });
</script> </script>

99
src/components/Container/src/collapse/CollapseContainer.vue

@ -1,48 +1,18 @@
<template> <script lang="tsx">
<div :class="prefixCls"> import { ref, unref, defineComponent, type PropType, type ExtractPropTypes } from 'vue';
<CollapseHeader v-bind="$props" import { isNil } from 'lodash-es';
:prefixCls="prefixCls"
:show="show"
@expand="handleExpand"
>
<template #title>
<slot name="title"/>
</template>
<template #action>
<slot name="action"/>
</template>
</CollapseHeader>
<div class="p-2">
<CollapseTransition :enable="canExpan">
<Skeleton v-if="loading" :active="loading"/>
<div v-else v-show="show" :class="`${prefixCls}__body`">
<slot/>
</div>
</CollapseTransition>
</div>
<div v-if="$slots.footer" :class="`${prefixCls}__footer`">
<slot name="footer"/>
</div>
</div>
</template>
<script lang="ts">
import type { PropType } from 'vue';
import { defineComponent, ref } from 'vue';
// component
import { Skeleton } from 'ant-design-vue'; import { Skeleton } from 'ant-design-vue';
import { CollapseTransition } from '/@/components/Transition'; import { CollapseTransition } from '/@/components/Transition';
import CollapseHeader from './CollapseHeader.vue'; import CollapseHeader from './CollapseHeader.vue';
import { triggerWindowResize } from '/@/utils/event'; import { triggerWindowResize } from '/@/utils/event';
// hook
import { useTimeoutFn } from '/@/hooks/core/useTimeout'; import { useTimeoutFn } from '/@/hooks/core/useTimeout';
import { useDesign } from '/@/hooks/web/useDesign'; import { useDesign } from '/@/hooks/web/useDesign';
const props = { const collapseContainerProps = {
title: { type: String, default: '' }, title: { type: String, default: '' },
loading: { type: Boolean }, loading: { type: Boolean },
/** /**
* 可以扩展吗 * 是否扩展
*/ */
canExpan: { type: Boolean, default: true }, canExpan: { type: Boolean, default: true },
/** /**
@ -63,35 +33,54 @@
lazyTime: { type: Number, default: 0 }, lazyTime: { type: Number, default: 0 },
}; };
export type CollapseContainerProps = ExtractPropTypes<typeof collapseContainerProps>;
export default defineComponent({ export default defineComponent({
name: 'CollapseContainer', name: 'CollapseContainer',
components: {
Skeleton,
CollapseHeader,
CollapseTransition,
},
props,
setup(props) {
const show = ref(true);
props: collapseContainerProps,
setup(props, { expose, slots }) {
const { prefixCls } = useDesign('collapse-container'); const { prefixCls } = useDesign('collapse-container');
/** const show = ref(true);
* 处理开发事件
*/ const handleExpand = (val: boolean) => {
function handleExpand() { show.value = isNil(val) ? !show.value : val;
show.value = !show.value;
if (props.triggerWindowResize) { if (props.triggerWindowResize) {
// 200 // 200 milliseconds here is because the expansion has animation,
useTimeoutFn(triggerWindowResize, 200); useTimeoutFn(triggerWindowResize, 200);
} }
}
return {
show,
handleExpand,
prefixCls,
}; };
expose({ handleExpand });
return () => (
<div class={unref(prefixCls)}>
<CollapseHeader
{...props}
prefixCls={unref(prefixCls)}
onExpand={handleExpand}
show={show.value}
v-slots={{
title: slots.title,
action: slots.action,
}}
/>
<div class="p-2">
<CollapseTransition enable={props.canExpan}>
{props.loading ? (
<Skeleton active={props.loading} />
) : (
<div class={`${prefixCls}__body`} v-show={show.value}>{slots.default?.()}</div>
)}
</CollapseTransition>
</div>
{slots.footer && <div class={`${prefixCls}__footer`}>{slots.footer()}</div>}
</div>
);
}, },
}); });
</script> </script>

60
src/components/Container/src/collapse/CollapseHeader.vue

@ -1,42 +1,44 @@
<template> <script lang="tsx">
<div :class="[`${prefixCls}__header px-2 py-5`, $attrs.class]"> import { defineComponent, computed, unref, type ExtractPropTypes } from 'vue';
<BasicTitle :helpMessage="helpMessage" normal> import { useDesign } from '/@/hooks/web/useDesign';
<template v-if="title">
{{ title }}
</template>
<template v-else>
<slot name="title"/>
</template>
</BasicTitle>
<div :class="`${prefixCls}__action`">
<slot name="action"/>
<BasicArrow v-if="canExpan"
up
:expand="show"
@click="$emit('expand')"
/>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { BasicArrow, BasicTitle } from '/@/components/Basic'; import { BasicArrow, BasicTitle } from '/@/components/Basic';
const props = { const collapseHeaderProps = {
prefixCls: { type: String }, prefixCls: String,
title: String,
show: Boolean,
canExpan: Boolean,
helpMessage: { helpMessage: {
type: [Array, String] as PropType<string[] | string>, type: [Array, String] as PropType<string[] | string>,
default: '', default: '',
}, },
title: { type: String },
show: { type: Boolean },
canExpan: { type: Boolean },
}; };
export type CollapseHeaderProps = ExtractPropTypes<typeof collapseHeaderProps>;
export default defineComponent({ export default defineComponent({
components: { BasicArrow, BasicTitle }, name: 'CollapseHeader',
inheritAttrs: false, inheritAttrs: false,
props, props: collapseHeaderProps,
emits: ['expand'], emits: ['expand'],
setup(props, { slots, attrs, emit }) {
const { prefixCls } = useDesign('collapse-container');
const _prefixCls = computed(() => props.prefixCls || unref(prefixCls));
return () => (
<div class={[`${unref(_prefixCls)}__header px-2 py-5`, attrs.class]}>
<BasicTitle helpMessage={props.helpMessage} normal>
{slots.title?.() || props.title}
</BasicTitle>
<div class={`${unref(_prefixCls)}__action`}>
{slots.action
? slots.action({ expand: props.show, onClick: () => emit('expand') })
: props.canExpan && (
<BasicArrow up expand={props.show} onClick={() => emit('expand')} />
)}
</div>
</div>
);
},
}); });
</script> </script>

19
src/components/ContextMenu/src/ContextMenu.vue

@ -60,9 +60,11 @@
const top = body.clientHeight < y + menuHeight ? y - menuHeight : y; const top = body.clientHeight < y + menuHeight ? y - menuHeight : y;
return { return {
...styles, ...styles,
position: 'absolute',
width: `${width}px`, width: `${width}px`,
left: `${left + 1}px`, left: `${left + 1}px`,
top: `${top + 1}px`, top: `${top + 1}px`,
zIndex: 9999,
}; };
}); });
@ -87,7 +89,8 @@
} }
function renderMenuItem(items: ContextMenuItem[]) { function renderMenuItem(items: ContextMenuItem[]) {
return items.map((item) => { const visibleItems = items.filter((item) => !item.hidden);
return visibleItems.map((item) => {
const { disabled, label, children, divider = false } = item; const { disabled, label, children, divider = false } = item;
const contentProps = { const contentProps = {
@ -124,15 +127,11 @@
} }
const { items } = props; const { items } = props;
return ( return (
<Menu <div class={prefixCls}>
inlineIndent={12} <Menu inlineIndent={12} mode="vertical" ref={wrapRef} style={unref(getStyle)}>
mode="vertical" {renderMenuItem(items)}
class={prefixCls} </Menu>
ref={wrapRef} </div>
style={unref(getStyle)}
>
{renderMenuItem(items)}
</Menu>
); );
}; };
}, },

1
src/components/ContextMenu/src/typing.ts

@ -6,6 +6,7 @@ export interface Axis {
export interface ContextMenuItem { export interface ContextMenuItem {
label: string; label: string;
icon?: string; icon?: string;
hidden?: boolean;
disabled?: boolean; disabled?: boolean;
handler?: Fn; handler?: Fn;
divider?: boolean; divider?: boolean;

3
src/components/Cropper/src/CopperModal.vue

@ -129,6 +129,7 @@
uploadApi: { uploadApi: {
type: Function as PropType<(params: apiFunParams) => Promise<any>>, type: Function as PropType<(params: apiFunParams) => Promise<any>>,
}, },
src: { type: String },
}; };
export default defineComponent({ export default defineComponent({
@ -138,7 +139,7 @@
emits: ['uploadSuccess', 'register'], emits: ['uploadSuccess', 'register'],
setup(props, { emit }) { setup(props, { emit }) {
let filename = ''; let filename = '';
const src = ref(''); const src = ref(props.src || '');
const previewSource = ref(''); const previewSource = ref('');
const cropper = ref<Cropper>(); const cropper = ref<Cropper>();
let scaleX = 1; let scaleX = 1;

82
src/components/Description/src/Description.vue

@ -3,7 +3,7 @@
import type { DescriptionsProps } from 'ant-design-vue/es/descriptions/index'; import type { DescriptionsProps } from 'ant-design-vue/es/descriptions/index';
import type { CSSProperties } from 'vue'; import type { CSSProperties } from 'vue';
import type { CollapseContainerOptions } from '/@/components/Container/index'; import type { CollapseContainerOptions } from '/@/components/Container/index';
import { defineComponent, computed, ref, unref } from 'vue'; import { defineComponent, computed, ref, unref, toRefs } from 'vue';
import { get } from 'lodash-es'; import { get } from 'lodash-es';
import { Descriptions } from 'ant-design-vue'; import { Descriptions } from 'ant-design-vue';
import { CollapseContainer } from '/@/components/Container/index'; import { CollapseContainer } from '/@/components/Container/index';
@ -12,6 +12,7 @@
import { getSlot } from '/@/utils/helper/tsxHelper'; import { getSlot } from '/@/utils/helper/tsxHelper';
import { useAttrs } from '/@/hooks/core/useAttrs'; import { useAttrs } from '/@/hooks/core/useAttrs';
const props = { const props = {
useCollapse: { type: Boolean, default: true }, useCollapse: { type: Boolean, default: true },
title: { type: String, default: '' }, title: { type: String, default: '' },
@ -108,45 +109,48 @@
function renderItem() { function renderItem() {
const { schema, data } = unref(getProps); const { schema, data } = unref(getProps);
return unref(schema) return unref(schema)
.map((item) => { .map((item) => {
const { render, field, span, show, contentMinWidth } = item; const { render, field, span, show, contentMinWidth } = item;
if (show && isFunction(show) && !show(data)) {
return null;
}
const getContent = () => { if (show && isFunction(show) && !show(data)) {
const _data = unref(getProps)?.data;
if (!_data) {
return null; return null;
} }
const getField = get(_data, field);
return isFunction(render) ? render(getField, _data) : getField ?? ''; const getContent = () => {
}; const _data = unref(getProps)?.data;
if (!_data) {
const width = contentMinWidth; return null;
return ( }
<Descriptions.Item label={renderLabel(item)} key={field} span={span}> const getField = get(_data, field);
{() => { if (getField && !toRefs(_data).hasOwnProperty(field)) {
if (!contentMinWidth) { return isFunction(render) ? render('', _data) : '';
return getContent(); }
} return isFunction(render) ? render(getField, _data) : getField ?? '';
const style: CSSProperties = { };
minWidth: `${width}px`,
}; const width = contentMinWidth;
return <div style={style}>{getContent()}</div>; return (
}} <Descriptions.Item label={renderLabel(item)} key={field} span={span}>
</Descriptions.Item> {() => {
); if (!contentMinWidth) {
}) return getContent();
.filter((item) => !!item); }
const style: CSSProperties = {
minWidth: `${width}px`,
};
return <div style={style}>{getContent()}</div>;
}}
</Descriptions.Item>
);
})
.filter((item) => !!item);
} }
const renderDesc = () => { const renderDesc = () => {
return ( return (
<Descriptions class={`${prefixCls}`} {...(unref(getDescriptionsProps) as any)}> <Descriptions class={`${prefixCls}`} {...(unref(getDescriptionsProps) as any)}>
{renderItem()} {renderItem()}
</Descriptions> </Descriptions>
); );
}; };
@ -161,12 +165,12 @@
const { title } = unref(getMergeProps); const { title } = unref(getMergeProps);
return ( return (
<CollapseContainer title={title} canExpan={canExpand} helpMessage={helpMessage}> <CollapseContainer title={title} canExpan={canExpand} helpMessage={helpMessage}>
{{ {{
default: () => content, default: () => content,
action: () => getSlot(slots, 'action'), action: () => getSlot(slots, 'action'),
}} }}
</CollapseContainer> </CollapseContainer>
); );
}; };

2
src/components/Description/src/typing.ts

@ -14,7 +14,7 @@ export interface DescItem {
// render // render
render?: ( render?: (
val: any, val: any,
data: Recordable data: Recordable,
) => VNode | undefined | JSX.Element | Element | string | number; ) => VNode | undefined | JSX.Element | Element | string | number;
} }

48
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 { WorkBook } from 'xlsx';
import type { JsonToSheet, AoAToSheet } from './typing'; import type { JsonToSheet, AoAToSheet } from './typing';
@ -6,13 +6,35 @@ const { utils, writeFile } = xlsx;
const DEF_FILE_NAME = 'excel-list.xlsx'; const DEF_FILE_NAME = 'excel-list.xlsx';
/**
* @param data source data
* @param worksheet worksheet object
* @param min min width
*/
function setColumnWidth(data, worksheet, min = 3) {
const obj = {};
worksheet['!cols'] = [];
data.forEach((item) => {
Object.keys(item).forEach((key) => {
const cur = item[key];
const length = cur?.length ?? min;
obj[key] = Math.max(length, obj[key] ?? min);
});
});
Object.keys(obj).forEach((key) => {
worksheet['!cols'].push({
wch: obj[key],
});
});
}
export function jsonToSheetXlsx<T = any>({ export function jsonToSheetXlsx<T = any>({
data, data,
header, header,
filename = DEF_FILE_NAME, filename = DEF_FILE_NAME,
json2sheetOpts = {}, json2sheetOpts = {},
write2excelOpts = { bookType: 'xlsx' }, write2excelOpts = { bookType: 'xlsx' },
}: JsonToSheet<T>) { }: JsonToSheet<T>) {
const arrData = [...data]; const arrData = [...data];
if (header) { if (header) {
arrData.unshift(header); arrData.unshift(header);
@ -20,7 +42,7 @@ export function jsonToSheetXlsx<T = any>({
} }
const worksheet = utils.json_to_sheet(arrData, json2sheetOpts); const worksheet = utils.json_to_sheet(arrData, json2sheetOpts);
setColumnWidth(arrData, worksheet);
/* add worksheet to workbook */ /* add worksheet to workbook */
const workbook: WorkBook = { const workbook: WorkBook = {
SheetNames: [filename], SheetNames: [filename],
@ -34,11 +56,11 @@ export function jsonToSheetXlsx<T = any>({
} }
export function aoaToSheetXlsx<T = any>({ export function aoaToSheetXlsx<T = any>({
data, data,
header, header,
filename = DEF_FILE_NAME, filename = DEF_FILE_NAME,
write2excelOpts = { bookType: 'xlsx' }, write2excelOpts = { bookType: 'xlsx' },
}: AoAToSheet<T>) { }: AoAToSheet<T>) {
const arrData = [...data]; const arrData = [...data];
if (header) { if (header) {
arrData.unshift(header); arrData.unshift(header);

118
src/components/Excel/src/ImportExcel.vue

@ -14,15 +14,68 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent, ref, unref } from 'vue'; import { defineComponent, ref, unref } from 'vue';
import XLSX from 'xlsx'; import * as XLSX from 'xlsx';
import type { ExcelData } from './typing'; import { dateUtil } from '/@/utils/dateUtil';
import type { ExcelData } from './typing';
export default defineComponent({ export default defineComponent({
name: 'ImportExcel', name: 'ImportExcel',
emits: ['success', 'error'], props: {
setup(_, { emit }) { // Date
dateFormat: {
type: String,
},
// +08:00
// https://github.com/SheetJS/sheetjs/issues/1470#issuecomment-501108554
timeZone: {
type: Number,
default: 8,
},
//
isReturnFile: {
type: Boolean,
default: false,
},
},
emits: ['success', 'error', 'cancel'],
setup(props, { emit }) {
const inputRef = ref<HTMLInputElement | null>(null); const inputRef = ref<HTMLInputElement | null>(null);
const loadingRef = ref<Boolean>(false); const loadingRef = ref<Boolean>(false);
const cancelRef = ref<Boolean>(true);
function shapeWorkSheel(sheet: XLSX.WorkSheet, range: XLSX.Range) {
let str = ' ',
char = 65,
customWorkSheet = {
t: 's',
v: str,
r: '<t> </t><phoneticPr fontId="1" type="noConversion"/>',
h: str,
w: str,
};
if (!sheet || !sheet['!ref']) return [];
let c = 0,
r = 1;
while (c < range.e.c + 1) {
while (r < range.e.r + 1) {
if (!sheet[String.fromCharCode(char) + r]) {
sheet[String.fromCharCode(char) + r] = customWorkSheet;
}
r++;
}
r = 1;
str += ' ';
customWorkSheet = {
t: 's',
v: str,
r: '<t> </t><phoneticPr fontId="1" type="noConversion"/>',
h: str,
w: str,
};
c++;
char++;
}
}
/** /**
* @description: 第一行作为头部 * @description: 第一行作为头部
@ -31,8 +84,8 @@
if (!sheet || !sheet['!ref']) return []; if (!sheet || !sheet['!ref']) return [];
const headers: string[] = []; const headers: string[] = [];
// A3:B7=>{s:{c:0, r:2}, e:{c:1, r:6}} // A3:B7=>{s:{c:0, r:2}, e:{c:1, r:6}}
const range = XLSX.utils.decode_range(sheet['!ref']); const range: XLSX.Range = XLSX.utils.decode_range(sheet['!ref']);
shapeWorkSheel(sheet, range);
const R = range.s.r; const R = range.s.r;
/* start in the first row */ /* start in the first row */
for (let C = range.s.c; C <= range.e.c; ++C) { for (let C = range.s.c; C <= range.e.c; ++C) {
@ -51,10 +104,28 @@
*/ */
function getExcelData(workbook: XLSX.WorkBook) { function getExcelData(workbook: XLSX.WorkBook) {
const excelData: ExcelData[] = []; const excelData: ExcelData[] = [];
const { dateFormat, timeZone } = props;
for (const sheetName of workbook.SheetNames) { for (const sheetName of workbook.SheetNames) {
const worksheet = workbook.Sheets[sheetName]; const worksheet = workbook.Sheets[sheetName];
const header: string[] = getHeaderRow(worksheet); const header: string[] = getHeaderRow(worksheet);
const results = XLSX.utils.sheet_to_json(worksheet); let results = XLSX.utils.sheet_to_json(worksheet, {
raw: true,
dateNF: dateFormat, //Not worked
}) as object[];
results = results.map((row: object) => {
for (let field in row) {
if (row[field] instanceof Date) {
if (timeZone === 8) {
row[field].setSeconds(row[field].getSeconds() + 43);
}
if (dateFormat) {
row[field] = dateUtil(row[field]).format(dateFormat);
}
}
}
return row;
});
excelData.push({ excelData.push({
header, header,
results, results,
@ -76,7 +147,7 @@
reader.onload = async (e) => { reader.onload = async (e) => {
try { try {
const data = e.target && e.target.result; const data = e.target && e.target.result;
const workbook = XLSX.read(data, { type: 'array' }); const workbook = XLSX.read(data, { type: 'array', cellDates: true });
// console.log(workbook); // console.log(workbook);
/* DO SOMETHING WITH workbook HERE */ /* DO SOMETHING WITH workbook HERE */
const excelData = getExcelData(workbook); const excelData = getExcelData(workbook);
@ -106,18 +177,45 @@
* @description: 触发选择文件管理器 * @description: 触发选择文件管理器
*/ */
function handleInputClick(e: Event) { function handleInputClick(e: Event) {
const files = e && (e.target as HTMLInputElement).files; const target = e && (e.target as HTMLInputElement);
const files = target?.files;
const rawFile = files && files[0]; // only setting files[0] const rawFile = files && files[0]; // only setting files[0]
target.value = '';
if (!rawFile) return; if (!rawFile) return;
cancelRef.value = false;
if (props.isReturnFile) {
emit('success', rawFile);
return;
}
upload(rawFile); upload(rawFile);
} }
/**
* @description 文件选择器关闭后,判断取消状态
*/
function handleFocusChange() {
const timeId = setInterval(() => {
if (cancelRef.value === true) {
emit('cancel');
}
clearInterval(timeId);
window.removeEventListener('focus', handleFocusChange);
}, 1000);
}
/** /**
* @description: 点击上传按钮 * @description: 点击上传按钮
*/ */
function handleUpload() { function handleUpload() {
const inputRefDom = unref(inputRef); const inputRefDom = unref(inputRef);
inputRefDom && inputRefDom.click(); if (inputRefDom) {
cancelRef.value = true;
inputRefDom.click();
window.addEventListener('focus', handleFocusChange);
}
} }
return { handleUpload, handleInputClick, inputRef }; return { handleUpload, handleInputClick, inputRef };

1
src/components/Form/index.ts

@ -12,5 +12,6 @@ export { default as ApiTreeSelect } from './src/components/ApiTreeSelect.vue';
export { default as ApiTree } from './src/components/ApiTree.vue'; export { default as ApiTree } from './src/components/ApiTree.vue';
export { default as ApiRadioGroup } from './src/components/ApiRadioGroup.vue'; export { default as ApiRadioGroup } from './src/components/ApiRadioGroup.vue';
export { default as ApiCascader } from './src/components/ApiCascader.vue'; export { default as ApiCascader } from './src/components/ApiCascader.vue';
export { default as ApiTransfer } from './src/components/ApiTransfer.vue';
export { BasicForm }; export { BasicForm };

97
src/components/Form/src/BasicForm.vue

@ -10,6 +10,7 @@
<slot name="formHeader"/> <slot name="formHeader"/>
<template v-for="schema in getSchema" :key="schema.field"> <template v-for="schema in getSchema" :key="schema.field">
<FormItem <FormItem
:isAdvanced="fieldsIsAdvancedMap[schema.field]"
:tableAction="tableAction" :tableAction="tableAction"
:formActionType="formActionType" :formActionType="formActionType"
:schema="schema" :schema="schema"
@ -56,6 +57,8 @@
import { useDebounceFn } from '@vueuse/core'; import { useDebounceFn } from '@vueuse/core';
import { basicProps } from './props'; import { basicProps } from './props';
import { useDesign } from '/@/hooks/web/useDesign'; import { useDesign } from '/@/hooks/web/useDesign';
import { cloneDeep } from 'lodash-es';
import { isFunction, isArray } from '/@/utils/is';
export default defineComponent({ export default defineComponent({
name: 'BasicForm', name: 'BasicForm',
@ -83,7 +86,11 @@
// Get the basic configuration of the form // Get the basic configuration of the form
const getProps = computed((): FormProps => { const getProps = computed((): FormProps => {
return { ...props, ...unref(propsRef) } as FormProps; let mergeProps = deepMerge(props, unref(propsRef)) as FormProps;
if (mergeProps.labelWidth) {
mergeProps.labelCol = undefined;
}
return mergeProps;
}); });
const getFormClass = computed(() => { const getFormClass = computed(() => {
@ -111,9 +118,9 @@
const getSchema = computed((): FormSchema[] => { const getSchema = computed((): FormSchema[] => {
const schemas: FormSchema[] = unref(schemaRef) || (unref(getProps).schemas as any); const schemas: FormSchema[] = unref(schemaRef) || (unref(getProps).schemas as any);
for (const schema of schemas) { for (const schema of schemas) {
const { defaultValue, component } = schema; const { defaultValue, component, isHandleDateDefaultValue = true } = schema;
// handle date type // handle date type
if (defaultValue && dateItemType.includes(component)) { if (isHandleDateDefaultValue && defaultValue && dateItemType.includes(component)) {
if (!Array.isArray(defaultValue)) { if (!Array.isArray(defaultValue)) {
schema.defaultValue = dateUtil(defaultValue); schema.defaultValue = dateUtil(defaultValue);
} else { } else {
@ -126,13 +133,15 @@
} }
} }
if (unref(getProps).showAdvancedButton) { if (unref(getProps).showAdvancedButton) {
return schemas.filter((schema) => schema.component !== 'Divider') as FormSchema[]; return cloneDeep(
schemas.filter((schema) => schema.component !== 'Divider') as FormSchema[],
);
} else { } else {
return schemas as FormSchema[]; return cloneDeep(schemas as FormSchema[]);
} }
}); });
const { handleToggleAdvanced } = useAdvanced({ const { handleToggleAdvanced, fieldsIsAdvancedMap } = useAdvanced({
advanceState, advanceState,
emit, emit,
getProps, getProps,
@ -165,7 +174,7 @@
updateSchema, updateSchema,
resetSchema, resetSchema,
appendSchemaByField, appendSchemaByField,
removeSchemaByFiled, removeSchemaByField,
resetFields, resetFields,
scrollToField, scrollToField,
} = useFormEvents({ } = useFormEvents({
@ -232,9 +241,12 @@
propsRef.value = deepMerge(unref(propsRef) || {}, formProps); propsRef.value = deepMerge(unref(propsRef) || {}, formProps);
} }
function setFormModel(key: string, value: any) { function setFormModel(key: string, value: any, schema: FormSchema) {
formModel[key] = value; formModel[key] = value;
const { validateTrigger } = unref(getBindValue); const { validateTrigger } = unref(getBindValue);
if (isFunction(schema.dynamicRules) || isArray(schema.rules)) {
return;
}
if (!validateTrigger || validateTrigger === 'change') { if (!validateTrigger || validateTrigger === 'change') {
validateFields([key]).catch((_) => {}); validateFields([key]).catch((_) => {});
} }
@ -259,7 +271,7 @@
updateSchema, updateSchema,
resetSchema, resetSchema,
setProps, setProps,
removeSchemaByFiled, removeSchemaByField,
appendSchemaByField, appendSchemaByField,
clearValidate, clearValidate,
validateFields, validateFields,
@ -290,55 +302,56 @@
getFormActionBindProps: computed( getFormActionBindProps: computed(
(): Recordable => ({ ...getProps.value, ...advanceState }), (): Recordable => ({ ...getProps.value, ...advanceState }),
), ),
fieldsIsAdvancedMap,
...formActionType, ...formActionType,
}; };
}, },
}); });
</script> </script>
<style lang="less"> <style lang="less">
@prefix-cls: ~'@{namespace}-basic-form'; @prefix-cls: ~'@{namespace}-basic-form';
.@{prefix-cls} {
.ant-form-item {
&-label label::after {
margin: 0 6px 0 2px;
}
&-with-help {
margin-bottom: 0;
}
&:not(.ant-form-item-with-help) { .@{prefix-cls} {
margin-bottom: 20px; .ant-form-item {
} &-label label::after {
margin: 0 6px 0 2px;
}
&.suffix-item { &-with-help {
.ant-form-item-children { margin-bottom: 0;
display: flex;
} }
.ant-form-item-control { &:not(.ant-form-item-with-help) {
margin-top: 4px; margin-bottom: 20px;
} }
.suffix { &.suffix-item {
display: inline-flex; .ant-form-item-children {
padding-left: 6px; display: flex;
margin-top: 1px; }
line-height: 1;
align-items: center; .ant-form-item-control {
margin-top: 4px;
}
.suffix {
display: inline-flex;
padding-left: 6px;
margin-top: 1px;
line-height: 1;
align-items: center;
}
} }
} }
}
.ant-form-explain { .ant-form-explain {
font-size: 14px; font-size: 14px;
} }
&--compact { &--compact {
.ant-form-item { .ant-form-item {
margin-bottom: 8px !important; margin-bottom: 8px !important;
}
} }
} }
}
</style> </style>

2
src/components/Form/src/componentMap.ts

@ -27,6 +27,7 @@ import ApiSelect from './components/ApiSelect.vue';
import ApiTree from './components/ApiTree.vue'; import ApiTree from './components/ApiTree.vue';
import ApiTreeSelect from './components/ApiTreeSelect.vue'; import ApiTreeSelect from './components/ApiTreeSelect.vue';
import ApiCascader from './components/ApiCascader.vue'; import ApiCascader from './components/ApiCascader.vue';
import ApiTransfer from './components/ApiTransfer.vue';
import { BasicUpload } from '/@/components/Upload'; import { BasicUpload } from '/@/components/Upload';
import { StrengthMeter } from '/@/components/StrengthMeter'; import { StrengthMeter } from '/@/components/StrengthMeter';
import { IconPicker } from '/@/components/Icon'; import { IconPicker } from '/@/components/Icon';
@ -57,6 +58,7 @@ componentMap.set('ApiCascader', ApiCascader);
componentMap.set('Cascader', Cascader); componentMap.set('Cascader', Cascader);
componentMap.set('Slider', Slider); componentMap.set('Slider', Slider);
componentMap.set('Rate', Rate); componentMap.set('Rate', Rate);
componentMap.set('ApiTransfer', ApiTransfer);
componentMap.set('DatePicker', DatePicker); componentMap.set('DatePicker', DatePicker);
componentMap.set('MonthPicker', DatePicker.MonthPicker); componentMap.set('MonthPicker', DatePicker.MonthPicker);

31
src/components/Form/src/components/ApiCascader.vue

@ -1,7 +1,6 @@
<template> <template>
<a-cascader <a-cascader
v-model:value="state" v-model:value="state"
v-bind="$attrs"
:options="options" :options="options"
:load-data="loadData" :load-data="loadData"
change-on-select change-on-select
@ -41,7 +40,6 @@
LoadingOutlined, LoadingOutlined,
[Cascader.name]: Cascader, [Cascader.name]: Cascader,
}, },
inheritAttrs: false,
props: { props: {
value: { value: {
type: Array, type: Array,
@ -83,12 +81,12 @@
const [state] = useRuleFormItem(props, 'value', 'change', emitData); const [state] = useRuleFormItem(props, 'value', 'change', emitData);
watch( watch(
apiData, apiData,
(data) => { (data) => {
const opts = generatorOptions(data); const opts = generatorOptions(data);
options.value = opts; options.value = opts;
}, },
{ deep: true }, { deep: true },
); );
function generatorOptions(options: any[]): Option[] { function generatorOptions(options: any[]): Option[] {
@ -97,10 +95,10 @@
if (next) { if (next) {
const value = next[valueField]; const value = next[valueField];
const item = { const item = {
isLeaf: isLeaf && typeof isLeaf === 'function' ? isLeaf(next) : false,
...omit(next, [labelField, valueField]), ...omit(next, [labelField, valueField]),
label: next[labelField], label: next[labelField],
value: numberToString ? `${value}` : value, value: numberToString ? `${value}` : value,
isLeaf: isLeaf && typeof isLeaf === 'function' ? isLeaf(next) : false,
}; };
const children = Reflect.get(next, childrenField); const children = Reflect.get(next, childrenField);
if (children) { if (children) {
@ -164,15 +162,15 @@
}); });
watch( watch(
() => props.initFetchParams, () => props.initFetchParams,
() => { () => {
!unref(isFirstLoad) && initialFetch(); !unref(isFirstLoad) && initialFetch();
}, },
{ deep: true }, { deep: true },
); );
function handleChange(keys, args) { function handleChange(keys, args) {
emitData.value = keys; emitData.value = args;
emit('defaultChange', keys, args); emit('defaultChange', keys, args);
} }
@ -186,15 +184,14 @@
return ''; return '';
} }
return { return {
state, state,
options, options,
loading, loading,
t, t,
handleRenderDisplay,
handleChange, handleChange,
loadData, loadData,
handleRenderDisplay,
}; };
}, },
}); });

29
src/components/Form/src/components/ApiRadioGroup.vue

@ -1,17 +1,20 @@
<!--
* @Description:It is troublesome to implement radio button group in the form. So it is extracted independently as a separate component
-->
<template> <template>
<RadioGroup v-model:value="state" <RadioGroup v-bind="attrs" v-model:value="state" button-style="solid">
v-bind="attrs"
button-style="solid"
@change="handleChange"
>
<template v-for="item in getOptions" :key="`${item.value}`"> <template v-for="item in getOptions" :key="`${item.value}`">
<RadioButton v-if="props.isBtn" :value="item.value" :disabled="item.disabled"> <RadioButton
v-if="props.isBtn"
:value="item.value"
:disabled="item.disabled"
@click="handleClick(item)"
>
{{ item.label }} {{ item.label }}
</RadioButton> </RadioButton>
<Radio v-else :value="item.value" :disabled="item.disabled"> <Radio
v-else
:value="item.value"
:disabled="item.disabled"
@click="handleClick(item)"
>
{{ item.label }} {{ item.label }}
</Radio> </Radio>
</template> </template>
@ -66,7 +69,7 @@
const attrs = useAttrs(); const attrs = useAttrs();
const { t } = useI18n(); const { t } = useI18n();
// Embedded in the form, just use the hook binding to perform form verification // Embedded in the form, just use the hook binding to perform form verification
const [state] = useRuleFormItem(props); const [state] = useRuleFormItem(props, 'value', 'change', emitData);
// Processing options value // Processing options value
const getOptions = computed(() => { const getOptions = computed(() => {
@ -124,11 +127,11 @@
emit('options-change', unref(getOptions)); emit('options-change', unref(getOptions));
} }
function handleChange(_, ...args) { function handleClick(...args) {
emitData.value = args; emitData.value = args;
} }
return { state, getOptions, attrs, loading, t, handleChange, props }; return { state, getOptions, attrs, loading, t, handleClick, props };
}, },
}); });
</script> </script>

26
src/components/Form/src/components/ApiSelect.vue

@ -1,7 +1,7 @@
<template> <template>
<Select <Select
v-model:value="state"
v-bind="$attrs" v-bind="$attrs"
v-model:value="state"
:options="getOptions" :options="getOptions"
@dropdown-visible-change="handleFetch" @dropdown-visible-change="handleFetch"
@change="handleChange" @change="handleChange"
@ -48,10 +48,7 @@
default: null, default: null,
}, },
// api params // api params
params: { params: propTypes.any.def({}),
type: Object as PropType<Recordable>,
default: () => ({}),
},
// support xxx.xxx.xx // support xxx.xxx.xx
resultField: propTypes.string.def(''), resultField: propTypes.string.def(''),
labelField: propTypes.string.def('label'), labelField: propTypes.string.def('label'),
@ -59,7 +56,7 @@
immediate: propTypes.bool.def(true), immediate: propTypes.bool.def(true),
alwaysLoad: propTypes.bool.def(false), alwaysLoad: propTypes.bool.def(false),
}, },
emits: ['options-change', 'change'], emits: ['options-change', 'change', 'update:value'],
setup(props, { emit }) { setup(props, { emit }) {
const options = ref<OptionsItem[]>([]); const options = ref<OptionsItem[]>([]);
const loading = ref(false); const loading = ref(false);
@ -92,11 +89,18 @@
}); });
watch( watch(
() => props.params, () => state.value,
() => { (v) => {
!unref(isFirstLoad) && fetch(); emit('update:value', v);
}, },
{ deep: true }, );
watch(
() => props.params,
() => {
!unref(isFirstLoad) && fetch();
},
{ deep: true },
); );
async function fetch() { async function fetch() {

138
src/components/Form/src/components/ApiTransfer.vue

@ -0,0 +1,138 @@
<template>
<Transfer
:data-source="getDataSource"
:filter-option="filterOption"
:render="(item) => item.title"
:showSelectAll="showSelectAll"
:selectedKeys="selectedKeys"
:targetKeys="getTargetKeys"
:showSearch="showSearch"
@change="handleChange"
/>
</template>
<script lang="ts">
import { computed, defineComponent, watch, ref, unref, watchEffect } from 'vue';
import { Transfer } from 'ant-design-vue';
import { isFunction } from '/@/utils/is';
import { get, omit } from 'lodash-es';
import { propTypes } from '/@/utils/propTypes';
import { useI18n } from '/@/hooks/web/useI18n';
import { TransferDirection, TransferItem } from 'ant-design-vue/lib/transfer';
export default defineComponent({
name: 'ApiTransfer',
components: { Transfer },
props: {
value: { type: Array as PropType<Array<string>> },
api: {
type: Function as PropType<(arg?: Recordable) => Promise<TransferItem[]>>,
default: null,
},
params: { type: Object },
dataSource: { type: Array as PropType<Array<TransferItem>> },
immediate: propTypes.bool.def(true),
alwaysLoad: propTypes.bool.def(false),
afterFetch: { type: Function as PropType<Fn> },
resultField: propTypes.string.def(''),
labelField: propTypes.string.def('title'),
valueField: propTypes.string.def('key'),
showSearch: { type: Boolean, default: false },
disabled: { type: Boolean, default: false },
filterOption: {
type: Function as PropType<(inputValue: string, item: TransferItem) => boolean>,
},
selectedKeys: { type: Array as PropType<Array<string>> },
showSelectAll: { type: Boolean, default: false },
targetKeys: { type: Array as PropType<Array<string>> },
},
emits: ['options-change', 'change'],
setup(props, { attrs, emit }) {
const _dataSource = ref<TransferItem[]>([]);
const _targetKeys = ref<string[]>([]);
const { t } = useI18n();
const getAttrs = computed(() => {
return {
...(!props.api ? { dataSource: unref(_dataSource) } : {}),
...attrs,
};
});
const getDataSource = computed(() => {
const { labelField, valueField } = props;
return unref(_dataSource).reduce((prev, next: Recordable) => {
if (next) {
prev.push({
...omit(next, [labelField, valueField]),
title: next[labelField],
key: next[valueField],
});
}
return prev;
}, [] as TransferItem[]);
});
const getTargetKeys = computed<string[]>(() => {
if (unref(_targetKeys).length > 0) {
return unref(_targetKeys);
}
if (Array.isArray(props.value)) {
return props.value;
}
if (Array.isArray(props.targetKeys)){
return props.targetKeys;
}
return [];
});
function handleChange(keys: string[], direction: TransferDirection, moveKeys: string[]) {
_targetKeys.value = keys;
console.log(direction);
console.log(moveKeys);
emit('change', keys);
}
watchEffect(() => {
props.immediate && !props.alwaysLoad && fetch();
});
watch(
() => props.params,
() => {
fetch();
},
{ deep: true },
);
async function fetch() {
const api = props.api;
if (!api || !isFunction(api)) {
if (Array.isArray(props.dataSource)) {
_dataSource.value = props.dataSource;
}
return;
}
_dataSource.value = [];
try {
const res = await api(props.params);
if (Array.isArray(res)) {
_dataSource.value = res;
emitChange();
return;
}
if (props.resultField) {
_dataSource.value = get(res, props.resultField) || [];
}
emitChange();
} catch (error) {
console.warn(error);
} finally {
}
}
function emitChange() {
emit('options-change', unref(getDataSource));
}
return { getTargetKeys, getDataSource, t, getAttrs, handleChange };
},
});
</script>

31
src/components/Form/src/components/FormItem.vue

@ -35,7 +35,7 @@
default: () => ({}), default: () => ({}),
}, },
setFormModel: { setFormModel: {
type: Function as PropType<(key: string, value: any) => void>, type: Function as PropType<(key: string, value: any, schema: FormSchema) => void>,
default: null, default: null,
}, },
tableAction: { tableAction: {
@ -44,6 +44,9 @@
formActionType: { formActionType: {
type: Object as PropType<FormActionType>, type: Object as PropType<FormActionType>,
}, },
isAdvanced: {
type: Boolean,
},
}, },
setup(props, { slots }) { setup(props, { slots }) {
const { t } = useI18n(); const { t } = useI18n();
@ -77,10 +80,14 @@
componentProps = componentProps({ schema, tableAction, formModel, formActionType }) ?? {}; componentProps = componentProps({ schema, tableAction, formModel, formActionType }) ?? {};
} }
if (schema.component === 'Divider') { if (schema.component === 'Divider') {
componentProps = Object.assign({ type: 'horizontal' }, componentProps, { componentProps = Object.assign(
orientation: 'left', { type: 'horizontal' },
plain: true, {
}); orientation: 'left',
plain: true,
},
componentProps,
);
} }
return componentProps as Recordable; return componentProps as Recordable;
}); });
@ -103,8 +110,8 @@
const { show, ifShow } = props.schema; const { show, ifShow } = props.schema;
const { showAdvancedButton } = props.formProps; const { showAdvancedButton } = props.formProps;
const itemIsAdvanced = showAdvancedButton const itemIsAdvanced = showAdvancedButton
? isBoolean(props.schema.isAdvanced) ? isBoolean(props.isAdvanced)
? props.schema.isAdvanced ? props.isAdvanced
: true : true
: true; : true;
@ -250,7 +257,7 @@
} }
const target = e ? e.target : null; const target = e ? e.target : null;
const value = target ? (isCheck ? target.checked : target.value) : e; const value = target ? (isCheck ? target.checked : target.value) : e;
props.setFormModel(field, value); props.setFormModel(field, value, props.schema);
}, },
}; };
const Comp = componentMap.get(component) as ReturnType<typeof defineComponent>; const Comp = componentMap.get(component) as ReturnType<typeof defineComponent>;
@ -298,8 +305,8 @@
const { label, helpMessage, helpComponentProps, subLabel } = props.schema; const { label, helpMessage, helpComponentProps, subLabel } = props.schema;
const renderLabel = subLabel ? ( const renderLabel = subLabel ? (
<span> <span>
{label} <span class="text-secondary">{subLabel}</span> {label} <span class="text-secondary">{subLabel}</span>
</span> </span>
) : ( ) : (
label label
); );
@ -311,9 +318,9 @@
} }
return ( return (
<span> <span>
{renderLabel} {renderLabel}
<BasicHelp placement="top" class="mx-1" text={getHelpMessage} {...helpComponentProps} /> <BasicHelp placement="top" class="mx-1" text={getHelpMessage} {...helpComponentProps} />
</span> </span>
); );
} }

19
src/components/Form/src/components/RadioButtonGroup.vue

@ -1,17 +1,14 @@
<!--
* @Description:It is troublesome to implement radio button group in the form. So it is extracted independently as a separate component
-->
<template> <template>
<RadioGroup v-model:value="state" v-bind="attrs" button-style="solid"> <RadioGroup v-bind="attrs" v-model:value="state" button-style="solid">
<template v-for="item in getOptions" :key="`${item.value}`"> <template v-for="item in getOptions" :key="`${item.value}`">
<RadioButton :value="item.value" :disabled="item.disabled"> <RadioButton :value="item.value" :disabled="item.disabled" @click="handleClick(item)">
{{ item.label }} {{ item.label }}
</RadioButton> </RadioButton>
</template> </template>
</RadioGroup> </RadioGroup>
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent, PropType, computed } from 'vue'; import { defineComponent, PropType, computed, ref } from 'vue';
import { Radio } from 'ant-design-vue'; import { Radio } from 'ant-design-vue';
import { isString } from '/@/utils/is'; import { isString } from '/@/utils/is';
import { useRuleFormItem } from '/@/hooks/component/useFormItem'; import { useRuleFormItem } from '/@/hooks/component/useFormItem';
@ -35,10 +32,12 @@
default: () => [], default: () => [],
}, },
}, },
emits: ['change'],
setup(props) { setup(props) {
const attrs = useAttrs(); const attrs = useAttrs();
const emitData = ref<any[]>([]);
// Embedded in the form, just use the hook binding to perform form verification // Embedded in the form, just use the hook binding to perform form verification
const [state] = useRuleFormItem(props); const [state] = useRuleFormItem(props, 'value', 'change', emitData);
// Processing options value // Processing options value
const getOptions = computed((): OptionsItem[] => { const getOptions = computed((): OptionsItem[] => {
@ -51,7 +50,11 @@
return options.map((item) => ({ label: item, value: item })) as OptionsItem[]; return options.map((item) => ({ label: item, value: item })) as OptionsItem[];
}); });
return { state, getOptions, attrs }; function handleClick(...args) {
emitData.value = args;
}
return { state, getOptions, attrs, handleClick };
}, },
}); });
</script> </script>

4
src/components/Form/src/helper.ts

@ -3,7 +3,7 @@ import type { ComponentType } from './types/index';
import { useI18n } from '/@/hooks/web/useI18n'; import { useI18n } from '/@/hooks/web/useI18n';
import { dateUtil } from '/@/utils/dateUtil'; import { dateUtil } from '/@/utils/dateUtil';
import { isNumber, isObject } from '/@/utils/is'; import { isNumber, isObject } from '/@/utils/is';
import type { ConfigType } from 'dayjs';
const { t } = useI18n(); const { t } = useI18n();
/** /**
@ -52,7 +52,7 @@ export function setComponentRuleType(
export function processDateValue(attr: Recordable, component: string) { export function processDateValue(attr: Recordable, component: string) {
const { valueFormat, value } = attr; const { valueFormat, value } = attr;
if (valueFormat) { if (valueFormat) {
attr.value = isObject(value) ? dateUtil(value).format(valueFormat) : value; attr.value = isObject(value) ? dateUtil(value as ConfigType).format(valueFormat) : value;
} else if (DATE_TYPE.includes(component) && value) { } else if (DATE_TYPE.includes(component) && value) {
attr.value = dateUtil(attr.value); attr.value = dateUtil(attr.value);
} }

8
src/components/Form/src/hooks/useAdvanced.ts

@ -1,6 +1,6 @@
import type { ColEx } from '../types'; import type { ColEx } from '../types';
import type { AdvanceState } from '../types/hooks'; import type { AdvanceState } from '../types/hooks';
import { ComputedRef, getCurrentInstance, Ref } from 'vue'; import { ComputedRef, getCurrentInstance, Ref, shallowReactive } from 'vue';
import type { FormProps, FormSchema } from '../types/form'; import type { FormProps, FormSchema } from '../types/form';
import { computed, unref, watch } from 'vue'; import { computed, unref, watch } from 'vue';
import { isBoolean, isFunction, isNumber, isObject } from '/@/utils/is'; import { isBoolean, isFunction, isNumber, isObject } from '/@/utils/is';
@ -113,6 +113,8 @@ export default function ({
} }
} }
const fieldsIsAdvancedMap = shallowReactive({});
function updateAdvanced() { function updateAdvanced() {
let itemColSum = 0; let itemColSum = 0;
let realItemColSum = 0; let realItemColSum = 0;
@ -148,7 +150,7 @@ export default function ({
if (isAdvanced) { if (isAdvanced) {
realItemColSum = itemColSum; realItemColSum = itemColSum;
} }
schema.isAdvanced = isAdvanced; fieldsIsAdvancedMap[schema.field] = isAdvanced;
} }
} }
@ -166,5 +168,5 @@ export default function ({
advanceState.isAdvanced = !advanceState.isAdvanced; advanceState.isAdvanced = !advanceState.isAdvanced;
} }
return { handleToggleAdvanced }; return { handleToggleAdvanced, fieldsIsAdvancedMap };
} }

10
src/components/Form/src/hooks/useForm.ts

@ -79,8 +79,8 @@ export function useForm(props?: Props): UseFormReturnType {
}); });
}, },
removeSchemaByFiled: async (field: string | string[]) => { removeSchemaByField: async (field: string | string[]) => {
unref(formRef)?.removeSchemaByFiled(field); unref(formRef)?.removeSchemaByField(field);
}, },
// TODO promisify // TODO promisify
@ -88,13 +88,13 @@ export function useForm(props?: Props): UseFormReturnType {
return unref(formRef)?.getFieldsValue() as T; return unref(formRef)?.getFieldsValue() as T;
}, },
setFieldsValue: async <T>(values: T) => { setFieldsValue: async (values: Recordable) => {
const form = await getForm(); const form = await getForm();
form.setFieldsValue<T>(values); form.setFieldsValue(values);
}, },
appendSchemaByField: async ( appendSchemaByField: async (
schema: FormSchema, schema: FormSchema | FormSchema[],
prefixField: string | undefined, prefixField: string | undefined,
first: boolean, first: boolean,
) => { ) => {

80
src/components/Form/src/hooks/useFormEvents.ts

@ -2,7 +2,15 @@ import type { ComputedRef, Ref } from 'vue';
import type { FormProps, FormSchema, FormActionType } from '../types/form'; import type { FormProps, FormSchema, FormActionType } from '../types/form';
import type { NamePath } from 'ant-design-vue/lib/form/interface'; import type { NamePath } from 'ant-design-vue/lib/form/interface';
import { unref, toRaw, nextTick } from 'vue'; import { unref, toRaw, nextTick } from 'vue';
import { isArray, isFunction, isObject, isString, isDef, isNullOrUnDef } from '/@/utils/is'; import {
isArray,
isFunction,
isObject,
isString,
isDef,
isNullOrUnDef,
isEmpty,
} from '/@/utils/is';
import { deepMerge } from '/@/utils'; import { deepMerge } from '/@/utils';
import { dateItemType, handleInputNumberValue, defaultValueComponents } from '../helper'; import { dateItemType, handleInputNumberValue, defaultValueComponents } from '../helper';
import { dateUtil } from '/@/utils/dateUtil'; import { dateUtil } from '/@/utils/dateUtil';
@ -58,7 +66,7 @@ export function useFormEvents({
// key 支持 a.b.c 的嵌套写法 // key 支持 a.b.c 的嵌套写法
const delimiter = '.'; const delimiter = '.';
const nestKeyArray = fields.filter((item) => item.indexOf(delimiter) >= 0); const nestKeyArray = fields.filter((item) => String(item).indexOf(delimiter) >= 0);
const validKeys: string[] = []; const validKeys: string[] = [];
Object.keys(values).forEach((key) => { Object.keys(values).forEach((key) => {
@ -68,6 +76,11 @@ export function useFormEvents({
const hasKey = Reflect.has(values, key); const hasKey = Reflect.has(values, key);
value = handleInputNumberValue(schema?.component, value); value = handleInputNumberValue(schema?.component, value);
const { componentProps } = schema || {};
let _props = componentProps as any;
if (typeof componentProps === 'function') {
_props = _props({ formModel: unref(formModel) });
}
// 0| '' is allow // 0| '' is allow
if (hasKey && fields.includes(key)) { if (hasKey && fields.includes(key)) {
// time type // time type
@ -77,31 +90,29 @@ export function useFormEvents({
for (const ele of value) { for (const ele of value) {
arr.push(ele ? dateUtil(ele) : null); arr.push(ele ? dateUtil(ele) : null);
} }
formModel[key] = arr; unref(formModel)[key] = arr;
} else { } else {
const { componentProps } = schema || {}; unref(formModel)[key] = value ? (_props?.valueFormat ? value : dateUtil(value)) : null;
let _props = componentProps as any;
if (typeof componentProps === 'function') {
_props = _props({ formModel });
}
formModel[key] = value ? (_props?.valueFormat ? value : dateUtil(value)) : null;
} }
} else { } else {
formModel[key] = value; unref(formModel)[key] = value;
}
if (_props?.onChange) {
_props?.onChange(value);
} }
validKeys.push(key); validKeys.push(key);
} else { } else {
nestKeyArray.forEach((nestKey: string) => { nestKeyArray.forEach((nestKey: string) => {
try { try {
const value = eval('values' + delimiter + nestKey); const value = nestKey.split('.').reduce((out, item) => out[item], values);
if (isDef(value)) { if (isDef(value)) {
formModel[nestKey] = value; unref(formModel)[nestKey] = unref(value);
validKeys.push(nestKey); validKeys.push(nestKey);
} }
} catch (e) { } catch (e) {
// key not exist // key not exist
if (isDef(defaultValueRef.value[nestKey])) { if (isDef(defaultValueRef.value[nestKey])) {
formModel[nestKey] = cloneDeep(defaultValueRef.value[nestKey]); unref(formModel)[nestKey] = cloneDeep(unref(defaultValueRef.value[nestKey]));
} }
} }
}); });
@ -112,7 +123,7 @@ export function useFormEvents({
/** /**
* @description: Delete based on field name * @description: Delete based on field name
*/ */
async function removeSchemaByFiled(fields: string | string[]): Promise<void> { async function removeSchemaByField(fields: string | string[]): Promise<void> {
const schemaList: FormSchema[] = cloneDeep(unref(getSchema)); const schemaList: FormSchema[] = cloneDeep(unref(getSchema));
if (!fields) { if (!fields) {
return; return;
@ -123,7 +134,7 @@ export function useFormEvents({
fieldList = [fields]; fieldList = [fields];
} }
for (const field of fieldList) { for (const field of fieldList) {
_removeSchemaByFiled(field, schemaList); _removeSchemaByFeild(field, schemaList);
} }
schemaRef.value = schemaList; schemaRef.value = schemaList;
} }
@ -131,7 +142,7 @@ export function useFormEvents({
/** /**
* @description: Delete based on field name * @description: Delete based on field name
*/ */
function _removeSchemaByFiled(field: string, schemaList: FormSchema[]): void { function _removeSchemaByFeild(field: string, schemaList: FormSchema[]): void {
if (isString(field)) { if (isString(field)) {
const index = schemaList.findIndex((schema) => schema.field === field); const index = schemaList.findIndex((schema) => schema.field === field);
if (index !== -1) { if (index !== -1) {
@ -144,19 +155,23 @@ export function useFormEvents({
/** /**
* @description: Insert after a certain field, if not insert the last * @description: Insert after a certain field, if not insert the last
*/ */
async function appendSchemaByField(schema: FormSchema, prefixField?: string, first = false) { async function appendSchemaByField(
schema: FormSchema | FormSchema[],
prefixField?: string,
first = false,
) {
const schemaList: FormSchema[] = cloneDeep(unref(getSchema)); const schemaList: FormSchema[] = cloneDeep(unref(getSchema));
const index = schemaList.findIndex((schema) => schema.field === prefixField); const index = schemaList.findIndex((schema) => schema.field === prefixField);
const _schemaList = isObject(schema) ? [schema as FormSchema] : (schema as FormSchema[]);
if (!prefixField || index === -1 || first) { if (!prefixField || index === -1 || first) {
first ? schemaList.unshift(schema) : schemaList.push(schema); first ? schemaList.unshift(..._schemaList) : schemaList.push(..._schemaList);
schemaRef.value = schemaList; schemaRef.value = schemaList;
_setDefaultValue(schema); _setDefaultValue(schema);
return; return;
} }
if (index !== -1) { if (index !== -1) {
schemaList.splice(index + 1, 0, schema); schemaList.splice(index + 1, 0, ..._schemaList);
} }
_setDefaultValue(schema); _setDefaultValue(schema);
@ -205,15 +220,19 @@ export function useFormEvents({
return; return;
} }
const schema: FormSchema[] = []; const schema: FormSchema[] = [];
updateData.forEach((item) => { unref(getSchema).forEach((val) => {
unref(getSchema).forEach((val) => { let _val;
updateData.forEach((item) => {
if (val.field === item.field) { if (val.field === item.field) {
const newSchema = deepMerge(val, item); _val = item;
schema.push(newSchema as FormSchema);
} else {
schema.push(val);
} }
}); });
if (_val !== undefined && val.field === _val.field) {
const newSchema = deepMerge(val, _val);
schema.push(newSchema as FormSchema);
} else {
schema.push(val);
}
}); });
_setDefaultValue(schema); _setDefaultValue(schema);
@ -237,7 +256,9 @@ export function useFormEvents({
Reflect.has(item, 'field') && Reflect.has(item, 'field') &&
item.field && item.field &&
!isNullOrUnDef(item.defaultValue) && !isNullOrUnDef(item.defaultValue) &&
!(item.field in currentFieldsValue) (!(item.field in currentFieldsValue) ||
isNullOrUnDef(currentFieldsValue[item.field]) ||
isEmpty(currentFieldsValue[item.field]))
) { ) {
obj[item.field] = item.defaultValue; obj[item.field] = item.defaultValue;
} }
@ -293,6 +314,9 @@ export function useFormEvents({
const res = handleFormValues(values); const res = handleFormValues(values);
emit('submit', res); emit('submit', res);
} catch (error: any) { } catch (error: any) {
if (error?.outOfDate === false && error?.errorFields) {
return;
}
throw new Error(error); throw new Error(error);
} }
} }
@ -306,7 +330,7 @@ export function useFormEvents({
updateSchema, updateSchema,
resetSchema, resetSchema,
appendSchemaByField, appendSchemaByField,
removeSchemaByFiled, removeSchemaByField,
resetFields, resetFields,
setFieldsValue, setFieldsValue,
scrollToField, scrollToField,

20
src/components/Form/src/hooks/useFormValues.ts

@ -76,7 +76,12 @@ export function useFormValues({
} }
// Remove spaces // Remove spaces
if (isString(value)) { if (isString(value)) {
value = value.trim(); // remove params from URL
if(value === '') {
value = undefined;
}else {
value = value.trim();
}
} }
if (!tryDeconstructArray(key, value, res) && !tryDeconstructObject(key, value, res)) { if (!tryDeconstructArray(key, value, res) && !tryDeconstructObject(key, value, res)) {
// 没有解构成功的,按原样赋值 // 没有解构成功的,按原样赋值
@ -97,14 +102,21 @@ export function useFormValues({
} }
for (const [field, [startTimeKey, endTimeKey], format = 'YYYY-MM-DD'] of fieldMapToTime) { for (const [field, [startTimeKey, endTimeKey], format = 'YYYY-MM-DD'] of fieldMapToTime) {
if (!field || !startTimeKey || !endTimeKey || !values[field]) { if (!field || !startTimeKey || !endTimeKey) {
continue;
}
// If the value to be converted is empty, remove the field
if (!values[field]) {
Reflect.deleteProperty(values, field);
continue; continue;
} }
const [startTime, endTime]: string[] = values[field]; const [startTime, endTime]: string[] = values[field];
values[startTimeKey] = dateUtil(startTime).format(format); const [startTimeFormat, endTimeFormat] = Array.isArray(format) ? format : [format, format];
values[endTimeKey] = dateUtil(endTime).format(format);
values[startTimeKey] = dateUtil(startTime).format(startTimeFormat);
values[endTimeKey] = dateUtil(endTime).format(endTimeFormat);
Reflect.deleteProperty(values, field); Reflect.deleteProperty(values, field);
} }

10
src/components/Form/src/props.ts

@ -5,11 +5,13 @@ import type { TableActionType } from '/@/components/Table';
import type { ButtonProps } from 'ant-design-vue/es/button/buttonTypes'; import type { ButtonProps } from 'ant-design-vue/es/button/buttonTypes';
import type { RowProps } from 'ant-design-vue/lib/grid/Row'; import type { RowProps } from 'ant-design-vue/lib/grid/Row';
import { propTypes } from '/@/utils/propTypes'; import { propTypes } from '/@/utils/propTypes';
import componentSetting from '/@/settings/componentSetting';
const { form } = componentSetting;
export const basicProps = { export const basicProps = {
model: { model: {
type: Object as PropType<Recordable>, type: Object as PropType<Recordable>,
default: {}, default: () => ({}),
}, },
// 标签宽度 固定宽度 // 标签宽度 固定宽度
labelWidth: { labelWidth: {
@ -23,7 +25,7 @@ export const basicProps = {
compact: propTypes.bool, compact: propTypes.bool,
// 表单配置规则 // 表单配置规则
schemas: { schemas: {
type: [Array] as PropType<FormSchema[]>, type: Array as PropType<FormSchema[]>,
default: () => [], default: () => [],
}, },
mergeDynamicData: { mergeDynamicData: {
@ -45,7 +47,7 @@ export const basicProps = {
// 禁用表单 // 禁用表单
disabled: propTypes.bool, disabled: propTypes.bool,
emptySpan: { emptySpan: {
type: [Number, Object] as PropType<number>, type: [Number, Object] as PropType<number | Recordable>,
default: 0, default: 0,
}, },
// 是否显示收起展开按钮 // 是否显示收起展开按钮
@ -95,7 +97,7 @@ export const basicProps = {
wrapperCol: Object as PropType<Partial<ColEx>>, wrapperCol: Object as PropType<Partial<ColEx>>,
colon: propTypes.bool, colon: propTypes.bool.def(form.colon),
labelAlign: propTypes.string, labelAlign: propTypes.string,

12
src/components/Form/src/types/form.ts

@ -7,7 +7,7 @@ import type { TableActionType } from '/@/components/Table/src/types/table';
import type { CSSProperties } from 'vue'; import type { CSSProperties } from 'vue';
import type { RowProps } from 'ant-design-vue/lib/grid/Row'; import type { RowProps } from 'ant-design-vue/lib/grid/Row';
export type FieldMapToTime = [string, [string, string], string?][]; export type FieldMapToTime = [string, [string, string], (string | [string, string])?][];
export type Rule = RuleObject & { export type Rule = RuleObject & {
trigger?: 'blur' | 'change' | ['change', 'blur']; trigger?: 'blur' | 'change' | ['change', 'blur'];
@ -26,16 +26,16 @@ export interface ButtonProps extends AntdButtonProps {
export interface FormActionType { export interface FormActionType {
submit: () => Promise<void>; submit: () => Promise<void>;
setFieldsValue: <T>(values: T) => Promise<void>; setFieldsValue: (values: Recordable) => Promise<void>;
resetFields: () => Promise<void>; resetFields: () => Promise<void>;
getFieldsValue: () => Recordable; getFieldsValue: () => Recordable;
clearValidate: (name?: string | string[]) => Promise<void>; clearValidate: (name?: string | string[]) => Promise<void>;
updateSchema: (data: Partial<FormSchema> | Partial<FormSchema>[]) => Promise<void>; updateSchema: (data: Partial<FormSchema> | Partial<FormSchema>[]) => Promise<void>;
resetSchema: (data: Partial<FormSchema> | Partial<FormSchema>[]) => Promise<void>; resetSchema: (data: Partial<FormSchema> | Partial<FormSchema>[]) => Promise<void>;
setProps: (formProps: Partial<FormProps>) => Promise<void>; setProps: (formProps: Partial<FormProps>) => Promise<void>;
removeSchemaByFiled: (field: string | string[]) => Promise<void>; removeSchemaByField: (field: string | string[]) => Promise<void>;
appendSchemaByField: ( appendSchemaByField: (
schema: FormSchema, schema: FormSchema | FormSchema[],
prefixField: string | undefined, prefixField: string | undefined,
first?: boolean | undefined, first?: boolean | undefined,
) => Promise<void>; ) => Promise<void>;
@ -175,6 +175,10 @@ export interface FormSchema {
// 默认值 // 默认值
defaultValue?: any; defaultValue?: any;
// 是否自动处理与时间相关组件的默认值
isHandleDateDefaultValue?: boolean;
isAdvanced?: boolean; isAdvanced?: boolean;
// Matching details components // Matching details components

3
src/components/Form/src/types/index.ts

@ -113,4 +113,5 @@ export type ComponentType =
| 'Render' | 'Render'
| 'Slider' | 'Slider'
| 'Rate' | 'Rate'
| 'Divider'; | 'Divider'
| 'ApiTransfer';

2
src/components/Icon/src/Icon.vue

@ -26,7 +26,7 @@
CSSProperties, CSSProperties,
} from 'vue'; } from 'vue';
import SvgIcon from './SvgIcon.vue'; import SvgIcon from './SvgIcon.vue';
import Iconify from '@iconify/iconify'; import Iconify from '@purge-icons/generated';
import { isString } from '/@/utils/is'; import { isString } from '/@/utils/is';
import { propTypes } from '/@/utils/propTypes'; import { propTypes } from '/@/utils/propTypes';

178
src/components/Icon/src/IconPicker.vue

@ -7,7 +7,7 @@
:class="prefixCls" :class="prefixCls"
> >
<template #addonAfter> <template #addonAfter>
<Popover <a-popover
v-model="visible" v-model="visible"
placement="bottomLeft" placement="bottomLeft"
trigger="click" trigger="click"
@ -18,7 +18,7 @@
<a-input <a-input
:placeholder="t('component.icon.search')" :placeholder="t('component.icon.search')"
allowClear allowClear
@change="handleSearchChange" @change="debounceHandleSearchChange"
/> />
</div> </div>
</template> </template>
@ -31,18 +31,7 @@
v-for="icon in getPaginationList" v-for="icon in getPaginationList"
:key="icon" :key="icon"
:class="currentSelect === icon ? 'border border-primary' : ''" :class="currentSelect === icon ? 'border border-primary' : ''"
class=" class="p-2 w-1/8 cursor-pointer mr-1 mt-1 flex justify-center items-center border border-solid hover:border-primary"
p-2
w-1/8
cursor-pointer
mr-1
mt-1
flex
justify-center
items-center
border border-solid
hover:border-primary
"
:title="icon" :title="icon"
@click="handleClick(icon)" @click="handleClick(icon)"
> >
@ -53,7 +42,7 @@
</ul> </ul>
</ScrollContainer> </ScrollContainer>
<div v-if="getTotal >= pageSize" class="flex py-2 items-center justify-center"> <div v-if="getTotal >= pageSize" class="flex py-2 items-center justify-center">
<Pagination <a-pagination
showLessItems showLessItems
size="small" size="small"
:pageSize="pageSize" :pageSize="pageSize"
@ -62,7 +51,7 @@
/> />
</div> </div>
</div> </div>
<template v-else><div class="p-5"> <Empty/></div> <template v-else><div class="p-5"><a-empty/></div>
</template> </template>
</template> </template>
@ -70,17 +59,18 @@
<SvgIcon :name="currentSelect"/> <SvgIcon :name="currentSelect"/>
</span> </span>
<Icon v-else :icon="currentSelect || 'ion:apps-outline'" class="cursor-pointer px-2 py-1"/> <Icon v-else :icon="currentSelect || 'ion:apps-outline'" class="cursor-pointer px-2 py-1"/>
</Popover> </a-popover>
</template> </template>
</a-input> </a-input>
</template> </template>
<script lang="ts"> <script lang="ts" setup>
import { defineComponent, ref, watchEffect, watch, unref } from 'vue'; import { ref, watchEffect, watch, unref } from 'vue';
import { useDesign } from '/@/hooks/web/useDesign'; import { useDesign } from '/@/hooks/web/useDesign';
import { ScrollContainer } from '/@/components/Container'; import { ScrollContainer } from '/@/components/Container';
import { Input, Popover, Pagination, Empty } from 'ant-design-vue'; import { Input, Popover, Pagination, Empty } from 'ant-design-vue';
import Icon from './Icon.vue'; import Icon from './Icon.vue';
import SvgIcon from './SvgIcon.vue'; import SvgIcon from './SvgIcon.vue';
import iconsData from '../data/icons.data'; import iconsData from '../data/icons.data';
import { propTypes } from '/@/utils/propTypes'; import { propTypes } from '/@/utils/propTypes';
import { usePagination } from '/@/hooks/web/usePagination'; import { usePagination } from '/@/hooks/web/usePagination';
@ -90,6 +80,12 @@
import { useMessage } from '/@/hooks/web/useMessage'; import { useMessage } from '/@/hooks/web/useMessage';
import svgIcons from 'virtual:svg-icons-names'; import svgIcons from 'virtual:svg-icons-names';
// 使WebStormunused
const AInput = Input;
const APopover = Popover;
const APagination = Pagination;
const AEmpty = Empty;
function getIcons() { function getIcons() {
const data = iconsData as any; const data = iconsData as any;
const prefix: string = data?.prefix ?? ''; const prefix: string = data?.prefix ?? '';
@ -106,85 +102,79 @@
return svgIcons.map((icon) => icon.replace('icon-', '')); return svgIcons.map((icon) => icon.replace('icon-', ''));
} }
export default defineComponent({ const props = defineProps({
name: 'IconPicker', value: propTypes.string,
components: { [Input.name]: Input, Icon, Popover, ScrollContainer, Pagination, Empty, SvgIcon }, width: propTypes.string.def('100%'),
inheritAttrs: false, pageSize: propTypes.number.def(140),
props: { copy: propTypes.bool.def(false),
value: propTypes.string, mode: propTypes.oneOf<('svg' | 'iconify')[]>(['svg', 'iconify']).def('iconify'),
width: propTypes.string.def('100%'), });
pageSize: propTypes.number.def(140),
copy: propTypes.bool.def(false),
mode: propTypes.oneOf<('svg' | 'iconify')[]>(['svg', 'iconify']).def('iconify'),
},
emits: ['change'],
setup(props, { emit }) {
const isSvgMode = props.mode === 'svg';
const icons = isSvgMode ? getSvgIcons() : getIcons();
const currentSelect = ref('');
const visible = ref(false);
const currentList = ref(icons);
const { t } = useI18n();
const { prefixCls } = useDesign('icon-picker');
const debounceHandleSearchChange = useDebounceFn(handleSearchChange, 100);
const { clipboardRef, isSuccessRef } = useCopyToClipboard(props.value);
const { createMessage } = useMessage();
const { getPaginationList, getTotal, setCurrentPage } = usePagination(
currentList,
props.pageSize
);
watchEffect(() => {
currentSelect.value = props.value;
});
watch(
() => currentSelect.value,
(v) => emit('change', v)
);
function handlePageChange(page: number) {
setCurrentPage(page);
}
function handleClick(icon: string) { const emit = defineEmits(['change', 'update:value']);
currentSelect.value = icon;
if (props.copy) {
clipboardRef.value = icon;
if (unref(isSuccessRef)) {
createMessage.success(t('component.icon.copy'));
}
}
}
function handleSearchChange(e: ChangeEvent) { const isSvgMode = props.mode === 'svg';
const value = e.target.value; const icons = isSvgMode ? getSvgIcons() : getIcons();
if (!value) {
setCurrentPage(1); const currentSelect = ref('');
currentList.value = icons; const visible = ref(false);
return; const currentList = ref(icons);
}
currentList.value = icons.filter((item) => item.includes(value)); const { t } = useI18n();
} const { prefixCls } = useDesign('icon-picker');
const debounceHandleSearchChange = useDebounceFn(handleSearchChange, 100);
let clipboardRef;
let isSuccessRef;
if (props.copy) {
const clipboard = useCopyToClipboard(props.value);
clipboardRef = clipboard?.clipboardRef;
isSuccessRef = clipboard?.isSuccessRef;
}
return { const { createMessage } = useMessage();
t,
prefixCls, const { getPaginationList, getTotal, setCurrentPage } = usePagination(
visible, currentList,
isSvgMode, props.pageSize,
getTotal, );
getPaginationList,
handlePageChange, watchEffect(() => {
handleClick, currentSelect.value = props.value;
currentSelect,
handleSearchChange: debounceHandleSearchChange,
};
},
}); });
watch(
() => currentSelect.value,
(v) => {
emit('update:value', v);
return emit('change', v);
},
);
function handlePageChange(page: number) {
setCurrentPage(page);
}
function handleClick(icon: string) {
currentSelect.value = icon;
if (props.copy) {
clipboardRef.value = icon;
if (unref(isSuccessRef)) {
createMessage.success(t('component.icon.copy'));
}
}
}
function handleSearchChange(e: ChangeEvent) {
const value = e.target.value;
if (!value) {
setCurrentPage(1);
currentList.value = icons;
return;
}
currentList.value = icons.filter((item) => item.includes(value));
}
</script> </script>
<style lang="less"> <style lang="less">
@prefix-cls: ~'@{namespace}-icon-picker'; @prefix-cls: ~'@{namespace}-icon-picker';

27
src/components/Loading/src/Loading.vue

@ -1,9 +1,15 @@
<template> <template>
<section v-show="loading" class="full-loading" :class="{ absolute }"> <section
<Spin v-bind="$attrs" v-show="loading"
:tip="tip" class="full-loading"
:size="size" :class="{ absolute, [theme]: !!theme }"
:spinning="loading" :style="[background ? `background-color: ${background}` : '']"
>
<Spin
v-bind="$attrs"
:tip="tip"
:size="size"
:spinning="loading"
/> />
</section> </section>
</template> </template>
@ -39,6 +45,9 @@
background: { background: {
type: String as PropType<string>, type: String as PropType<string>,
}, },
theme: {
type: String as PropType<'dark' | 'light'>,
},
}, },
}); });
</script> </script>
@ -53,7 +62,7 @@
height: 100%; height: 100%;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
background-color: rgba(240, 242, 245, 0.4); background-color: rgb(240 242 245 / 40%);
&.absolute { &.absolute {
position: absolute; position: absolute;
@ -64,8 +73,12 @@
} }
html[data-theme='dark'] { html[data-theme='dark'] {
.full-loading { .full-loading:not(.light) {
background-color: @modal-mask-bg; background-color: @modal-mask-bg;
} }
} }
.full-loading.dark {
background-color: @modal-mask-bg;
}
</style> </style>

4
src/components/Loading/src/useLoading.ts

@ -4,7 +4,7 @@ import type { LoadingProps } from './typing';
import type { Ref } from 'vue'; import type { Ref } from 'vue';
export interface UseLoadingOptions { export interface UseLoadingOptions {
target?: HTMLElement | Ref<ElRef>; target?: any;
props?: Partial<LoadingProps>; props?: Partial<LoadingProps>;
} }
@ -16,7 +16,7 @@ export function useLoading(props: Partial<LoadingProps>): [Fn, Fn, (string) => v
export function useLoading(opt: Partial<UseLoadingOptions>): [Fn, Fn, (string) => void]; export function useLoading(opt: Partial<UseLoadingOptions>): [Fn, Fn, (string) => void];
export function useLoading( export function useLoading(
opt: Partial<LoadingProps> | Partial<UseLoadingOptions> opt: Partial<LoadingProps> | Partial<UseLoadingOptions>,
): [Fn, Fn, (string) => void] { ): [Fn, Fn, (string) => void] {
let props: Partial<LoadingProps>; let props: Partial<LoadingProps>;
let target: HTMLElement | Ref<ElRef> = document.body; let target: HTMLElement | Ref<ElRef> = document.body;

2
src/components/Markdown/index.ts

@ -1,5 +1,7 @@
import { withInstall } from '/@/utils'; import { withInstall } from '/@/utils';
import markDown from './src/Markdown.vue'; import markDown from './src/Markdown.vue';
import markDownViewer from './src/MarkdownViewer.vue';
export const MarkDown = withInstall(markDown); export const MarkDown = withInstall(markDown);
export const MarkdownViewer = withInstall(markDownViewer);
export * from './src/typing'; export * from './src/typing';

236
src/components/Markdown/src/Markdown.vue

@ -2,143 +2,157 @@
<div ref="wrapRef"/> <div ref="wrapRef"/>
</template> </template>
<script lang="ts"> <script lang="ts">
import { import type { Ref } from 'vue';
defineComponent, import {
ref, defineComponent,
unref, ref,
nextTick, unref,
computed, nextTick,
watch, computed,
onBeforeUnmount, watch,
onDeactivated, onBeforeUnmount,
} from 'vue'; onDeactivated,
import Vditor from 'vditor'; } from 'vue';
import 'vditor/dist/index.css'; import Vditor from 'vditor';
import { useLocale } from '/@/locales/useLocale'; import 'vditor/dist/index.css';
import { useModalContext } from '../../Modal'; import { useLocale } from '/@/locales/useLocale';
import { useRootSetting } from '/@/hooks/setting/useRootSetting'; import { useModalContext } from '../../Modal';
import { onMountedOrActivated } from '/@/hooks/core/onMountedOrActivated'; import { useRootSetting } from '/@/hooks/setting/useRootSetting';
import { onMountedOrActivated } from '/@/hooks/core/onMountedOrActivated';
import { getTheme } from './getTheme';
type Lang = 'zh_CN' | 'en_US' | 'ja_JP' | 'ko_KR' | undefined; type Lang = 'zh_CN' | 'en_US' | 'ja_JP' | 'ko_KR' | undefined;
export default defineComponent({ export default defineComponent({
inheritAttrs: false, inheritAttrs: false,
props: { props: {
height: { type: Number, default: 360 }, height: { type: Number, default: 360 },
value: { type: String, default: '' }, value: { type: String, default: '' },
}, },
emits: ['change', 'get', 'update:value'], emits: ['change', 'get', 'update:value'],
setup(props, { attrs, emit }) { setup(props, { attrs, emit }) {
const wrapRef = ref<ElRef>(null); const wrapRef = ref<ElRef>(null);
const vditorRef = ref<Nullable<any>>(null); const vditorRef = ref(null) as Ref<Nullable<Vditor>>;
const initedRef = ref(false); const initedRef = ref(false);
const modalFn = useModalContext(); const modalFn = useModalContext();
const { getLocale } = useLocale(); const { getLocale } = useLocale();
const { getDarkMode } = useRootSetting(); const { getDarkMode } = useRootSetting();
const valueRef = ref(''); const valueRef = ref(props.value || '');
watch( watch(
[() => getDarkMode.value, () => initedRef.value], [() => getDarkMode.value, () => initedRef.value],
([val, inited]) => { ([val, inited]) => {
if (!inited) { if (!inited) {
return; return;
} }
const theme = val === 'dark' ? 'dark' : 'classic'; instance
instance.getVditor()?.setTheme(theme); .getVditor()
?.setTheme(getTheme(val) as any, getTheme(val, 'content'), getTheme(val, 'code'));
}, },
{ {
immediate: true, immediate: true,
flush: 'post', flush: 'post',
} },
); );
watch( watch(
() => props.value, () => props.value,
(v) => { (v) => {
if (v !== valueRef.value) { if (v !== valueRef.value) {
instance.getVditor()?.setValue(v); instance.getVditor()?.setValue(v);
} }
valueRef.value = v; valueRef.value = v;
} },
); );
const getCurrentLang = computed((): 'zh_CN' | 'en_US' | 'ja_JP' | 'ko_KR' => { const getCurrentLang = computed((): 'zh_CN' | 'en_US' | 'ja_JP' | 'ko_KR' => {
let lang: Lang; let lang: Lang;
switch (unref(getLocale)) { switch (unref(getLocale)) {
case 'en': case 'en':
lang = 'en_US'; lang = 'en_US';
break; break;
case 'ja': case 'ja':
lang = 'ja_JP'; lang = 'ja_JP';
break; break;
case 'ko': case 'ko':
lang = 'ko_KR'; lang = 'ko_KR';
break; break;
default: default:
lang = 'zh_CN'; lang = 'zh_CN';
} }
return lang; return lang;
}); });
function init() { function init() {
const wrapEl = unref(wrapRef) as HTMLElement; const wrapEl = unref(wrapRef) as HTMLElement;
if (!wrapEl) return; if (!wrapEl) return;
const bindValue = { ...attrs, ...props }; const bindValue = { ...attrs, ...props };
const insEditor = new Vditor(wrapEl, { const insEditor = new Vditor(wrapEl, {
theme: getDarkMode.value === 'dark' ? 'dark' : 'classic', //
lang: unref(getCurrentLang), theme: getTheme(getDarkMode.value) as any,
mode: 'sv', lang: unref(getCurrentLang),
preview: { mode: 'sv',
actions: [], fullscreen: {
}, index: 520,
input: (v) => { },
valueRef.value = v; preview: {
emit('update:value', v); theme: {
emit('change', v); //
}, current: getTheme(getDarkMode.value, 'content'),
after: () => {
nextTick(() => {
modalFn?.redoModalHeight?.();
insEditor.setValue(valueRef.value);
vditorRef.value = insEditor;
initedRef.value = true;
emit('get', instance);
});
},
blur: () => {
//unref(vditorRef)?.setValue(props.value);
}, },
...bindValue, hljs: {
cache: { //
enable: false, style: getTheme(getDarkMode.value, 'code'),
}, },
}); actions: [],
} },
input: (v) => {
valueRef.value = v;
emit('update:value', v);
emit('change', v);
},
after: () => {
nextTick(() => {
modalFn?.redoModalHeight?.();
insEditor.setValue(valueRef.value);
vditorRef.value = insEditor;
initedRef.value = true;
emit('get', instance);
});
},
blur: () => {
//unref(vditorRef)?.setValue(props.value);
},
...bindValue,
cache: {
enable: false,
},
});
}
const instance = { const instance = {
// @ts-ignore getVditor: (): Vditor => vditorRef.value!,
getVditor: (): Vditor => vditorRef.value!, };
};
function destroy() { function destroy() {
const vditorInstance = unref(vditorRef); const vditorInstance = unref(vditorRef);
if (!vditorInstance) return; if (!vditorInstance) return;
try { try {
vditorInstance?.destroy?.(); vditorInstance?.destroy?.();
} catch (error) {} } catch (error) {}
vditorRef.value = null; vditorRef.value = null;
initedRef.value = false; initedRef.value = false;
} }
onMountedOrActivated(init); onMountedOrActivated(init);
onBeforeUnmount(destroy); onBeforeUnmount(destroy);
onDeactivated(destroy); onDeactivated(destroy);
return { return {
wrapRef, wrapRef,
...instance, ...instance,
}; };
}, },
}); });
</script> </script>

62
src/components/Markdown/src/MarkdownViewer.vue

@ -0,0 +1,62 @@
<template>
<div id="markdownViewer" ref="viewerRef" :class="$props.class"/>
</template>
<script lang="ts" setup>
import { defineProps, onBeforeUnmount, onDeactivated, Ref, ref, unref, watch } from 'vue';
import VditorPreview from 'vditor/dist/method.min';
import { onMountedOrActivated } from '/@/hooks/core/onMountedOrActivated';
import { useRootSetting } from '/@/hooks/setting/useRootSetting';
import { getTheme } from './getTheme';
const props = defineProps({
value: { type: String },
class: { type: String },
});
const viewerRef = ref<ElRef>(null);
const vditorPreviewRef = ref(null) as Ref<Nullable<VditorPreview>>;
const { getDarkMode } = useRootSetting();
function init() {
const viewerEl = unref(viewerRef) as HTMLElement;
vditorPreviewRef.value = VditorPreview.preview(viewerEl, props.value, {
mode: getTheme(getDarkMode.value, 'content'),
theme: {
//
current: getTheme(getDarkMode.value, 'content'),
},
hljs: {
//
style: getTheme(getDarkMode.value, 'code'),
},
});
}
watch(
() => getDarkMode.value,
(val) => {
VditorPreview.setContentTheme(getTheme(val, 'content'));
VditorPreview.setCodeTheme(getTheme(val, 'code'));
init();
},
);
watch(
() => props.value,
(v, oldValue) => {
v !== oldValue && init();
},
);
function destroy() {
const vditorInstance = unref(vditorPreviewRef);
if (!vditorInstance) return;
try {
vditorInstance?.destroy?.();
} catch (error) {}
vditorPreviewRef.value = null;
}
onMountedOrActivated(init);
onBeforeUnmount(destroy);
onDeactivated(destroy);
</script>

19
src/components/Markdown/src/getTheme.ts

@ -0,0 +1,19 @@
/**
*
* @param darkModeVal
* @param themeMode (), ,
*/
export const getTheme = (
darkModeVal: 'light' | 'dark' | string,
themeMode: 'default' | 'content' | 'code' = 'default',
) => {
const isDark = darkModeVal === 'dark';
switch (themeMode) {
case 'default':
return isDark ? 'dark' : 'classic';
case 'content':
return isDark ? 'dark' : 'light';
case 'code':
return isDark ? 'dracula' : 'github';
}
};

5
src/components/Menu/src/BasicMenu.vue

@ -9,7 +9,7 @@
:class="getMenuClass" :class="getMenuClass"
:subMenuOpenDelay="0.2" :subMenuOpenDelay="0.2"
v-bind="getInlineCollapseOptions" v-bind="getInlineCollapseOptions"
@openChange="handleOpenChange" @open-change="handleOpenChange"
@click="handleMenuClick" @click="handleMenuClick"
> >
<template v-for="item in items" :key="item.path"> <template v-for="item in items" :key="item.path">
@ -30,9 +30,8 @@
import { useMenuSetting } from '/@/hooks/setting/useMenuSetting'; import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
import { REDIRECT_NAME } from '/@/router/constant'; import { REDIRECT_NAME } from '/@/router/constant';
import { useDesign } from '/@/hooks/web/useDesign'; import { useDesign } from '/@/hooks/web/useDesign';
import { getCurrentParentPath } from '/@/router/helper/menuHelper';
import { listenerRouteChange } from '/@/logics/mitt/routeChange'; import { listenerRouteChange } from '/@/logics/mitt/routeChange';
import { getAllParentPath } from '/@/router/helper/menuHelper'; import { getAllParentPath, getCurrentParentPath } from '/@/router/helper/menuHelper';
export default defineComponent({ export default defineComponent({
name: 'BasicMenu', name: 'BasicMenu',

11
src/components/Menu/src/components/BasicMenuItem.vue

@ -6,20 +6,15 @@
<script lang="ts"> <script lang="ts">
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
import { Menu } from 'ant-design-vue'; import { Menu } from 'ant-design-vue';
import { useDesign } from '/@/hooks/web/useDesign';
import { itemProps } from '../props'; import { itemProps } from '../props';
import MenuItemContent from './MenuItemContent.vue';
import MenuItemContent from './MenuItemContent.vue';
export default defineComponent({ export default defineComponent({
name: 'BasicMenuItem', name: 'BasicMenuItem',
components: { MenuItem: Menu.Item, MenuItemContent }, components: { MenuItem: Menu.Item, MenuItemContent },
props: itemProps, props: itemProps,
setup() // props setup() {
{ return {};
const { prefixCls } = useDesign('basic-menu-item');
return {
prefixCls,
};
}, },
}); });
</script> </script>

8
src/components/Menu/src/components/BasicSubMenuItem.vue

@ -39,10 +39,10 @@
const getShowMenu = computed(() => !props.item.meta?.hideMenu); const getShowMenu = computed(() => !props.item.meta?.hideMenu);
function menuHasChildren(menuTreeItem: MenuType): boolean { function menuHasChildren(menuTreeItem: MenuType): boolean {
return ( return (
!menuTreeItem.meta?.hideChildrenInMenu && !menuTreeItem.meta?.hideChildrenInMenu &&
Reflect.has(menuTreeItem, 'children') && Reflect.has(menuTreeItem, 'children') &&
!!menuTreeItem.children && !!menuTreeItem.children &&
menuTreeItem.children.length > 0 menuTreeItem.children.length > 0
); );
} }
return { return {

11
src/components/Menu/src/components/MenuItemContent.vue

@ -1,9 +1,10 @@
<template> <template>
<span :class="`${prefixCls}- flex items-center `"> <span :class="`${prefixCls}- flex items-center`">
<Icon v-if="getIcon" <Icon
:icon="getIcon" v-if="getIcon"
:size="18" :icon="getIcon"
:class="`${prefixCls}-wrapper__icon mr-2`" :size="18"
:class="`${prefixCls}-wrapper__icon mr-2`"
/> />
{{ getI18nName }} {{ getI18nName }}
</span> </span>

9
src/components/Menu/src/props.ts

@ -4,6 +4,8 @@ import type { PropType } from 'vue';
import { MenuModeEnum, MenuTypeEnum } from '/@/enums/menuEnum'; import { MenuModeEnum, MenuTypeEnum } from '/@/enums/menuEnum';
import { ThemeEnum } from '/@/enums/appEnum'; import { ThemeEnum } from '/@/enums/appEnum';
import { propTypes } from '/@/utils/propTypes'; import { propTypes } from '/@/utils/propTypes';
import type { MenuTheme } from 'ant-design-vue';
import type { MenuMode } from 'ant-design-vue/lib/menu/src/interface';
export const basicProps = { export const basicProps = {
items: { items: {
type: Array as PropType<Menu[]>, type: Array as PropType<Menu[]>,
@ -22,7 +24,10 @@ export const basicProps = {
type: String as PropType<MenuTypeEnum>, type: String as PropType<MenuTypeEnum>,
default: MenuTypeEnum.MIX, default: MenuTypeEnum.MIX,
}, },
theme: propTypes.string.def(ThemeEnum.DARK), theme: {
type: String as PropType<MenuTheme>,
default: ThemeEnum.DARK,
},
inlineCollapsed: propTypes.bool, inlineCollapsed: propTypes.bool,
mixSider: propTypes.bool, mixSider: propTypes.bool,
@ -36,7 +41,7 @@ export const basicProps = {
export const itemProps = { export const itemProps = {
item: { item: {
type: Object as PropType<Menu>, type: Object as PropType<Menu>,
default: {}, default: () => ({}),
}, },
level: propTypes.number, level: propTypes.number,
theme: propTypes.oneOf(['dark', 'light']), theme: propTypes.oneOf(['dark', 'light']),

4
src/components/Menu/src/useOpenKeys.ts

@ -14,7 +14,7 @@ export function useOpenKeys(
menuState: MenuState, menuState: MenuState,
menus: Ref<MenuType[]>, menus: Ref<MenuType[]>,
mode: Ref<MenuModeEnum>, mode: Ref<MenuModeEnum>,
accordion: Ref<boolean> accordion: Ref<boolean>,
) { ) {
const { getCollapsed, getIsMixSidebar } = useMenuSetting(); const { getCollapsed, getIsMixSidebar } = useMenuSetting();
@ -37,7 +37,7 @@ export function useOpenKeys(
} }
}, },
16, 16,
!native !native,
); );
} }

44
src/components/Modal/src/BasicModal.vue

@ -44,7 +44,7 @@
</ModalWrapper> </ModalWrapper>
<template v-for="item in Object.keys(omit($slots, 'default'))" #[item]="data"> <template v-for="item in Object.keys(omit($slots, 'default'))" #[item]="data">
<slot :name="item" v-bind="data"/> <slot :name="item" v-bind="data || {}"/>
</template> </template>
</Modal> </Modal>
</template> </template>
@ -72,6 +72,7 @@
import { basicProps } from './props'; import { basicProps } from './props';
import { useFullScreen } from './hooks/useModalFullScreen'; import { useFullScreen } from './hooks/useModalFullScreen';
import { omit } from 'lodash-es'; import { omit } from 'lodash-es';
import { useDesign } from '/@/hooks/web/useDesign';
export default defineComponent({ export default defineComponent({
name: 'BasicModal', name: 'BasicModal',
@ -83,6 +84,7 @@
const visibleRef = ref(false); const visibleRef = ref(false);
const propsRef = ref<Partial<ModalProps> | null>(null); const propsRef = ref<Partial<ModalProps> | null>(null);
const modalWrapperRef = ref<any>(null); const modalWrapperRef = ref<any>(null);
const { prefixCls } = useDesign('basic-modal');
// modal Bottom and top height // modal Bottom and top height
const extHeightRef = ref(0); const extHeightRef = ref(0);
@ -104,7 +106,7 @@
} }
// Custom title component: get title // Custom title component: get title
const getMergeProps = computed((): ModalProps => { const getMergeProps = computed((): Recordable => {
return { return {
...props, ...props,
...(unref(propsRef) as any), ...(unref(propsRef) as any),
@ -118,7 +120,7 @@
}); });
// modal component does not need title and origin buttons // modal component does not need title and origin buttons
const getProps = computed((): ModalProps => { const getProps = computed((): Recordable => {
const opt = { const opt = {
...unref(getMergeProps), ...unref(getMergeProps),
visible: unref(visibleRef), visible: unref(visibleRef),
@ -137,8 +139,9 @@
...attrs, ...attrs,
...unref(getMergeProps), ...unref(getMergeProps),
visible: unref(visibleRef), visible: unref(visibleRef),
wrapClassName: unref(getWrapClassName),
}; };
attr['wrapClassName'] = `${attr?.['wrapClassName'] || ''} ${unref(getWrapClassName)}`;
if (unref(fullScreenRef)) { if (unref(fullScreenRef)) {
return omit(attr, ['height', 'title']); return omit(attr, ['height', 'title']);
} }
@ -156,26 +159,27 @@
}); });
watch( watch(
() => unref(visibleRef), () => unref(visibleRef),
(v) => { (v) => {
emit('visible-change', v); emit('visible-change', v);
emit('update:visible', v); emit('update:visible', v);
instance && modalMethods.emitVisible?.(v, instance.uid); instance && modalMethods.emitVisible?.(v, instance.uid);
nextTick(() => { nextTick(() => {
if (props.scrollTop && v && unref(modalWrapperRef)) { if (props.scrollTop && v && unref(modalWrapperRef)) {
(unref(modalWrapperRef) as any).scrollTop(); (unref(modalWrapperRef) as any).scrollTop();
} }
}); });
}, },
{ {
immediate: false, immediate: false,
} },
); );
// //
async function handleCancel(e: Event) { async function handleCancel(e: Event) {
e?.stopPropagation(); e?.stopPropagation();
//
if ((e.target as HTMLElement)?.classList?.contains(prefixCls + '-close--custom')) return;
if (props.closeFunc && isFunction(props.closeFunc)) { if (props.closeFunc && isFunction(props.closeFunc)) {
const isClose: boolean = await props.closeFunc(); const isClose: boolean = await props.closeFunc();
visibleRef.value = !isClose; visibleRef.value = !isClose;
@ -212,7 +216,7 @@
extHeightRef.value = height; extHeightRef.value = height;
} }
function handleTitleDbClick(e: ChangeEvent) { function handleTitleDbClick(e) {
if (!props.canFullscreen) return; if (!props.canFullscreen) return;
e.stopPropagation(); e.stopPropagation();
handleFullScreen(e); handleFullScreen(e);

9
src/components/Modal/src/components/Modal.tsx

@ -9,7 +9,8 @@ export default defineComponent({
name: 'Modal', name: 'Modal',
inheritAttrs: false, inheritAttrs: false,
props: basicProps, props: basicProps,
setup(props, { slots }) { emits: ['cancel'],
setup(props, { slots, emit }) {
const { visible, draggable, destroyOnClose } = toRefs(props); const { visible, draggable, destroyOnClose } = toRefs(props);
const attrs = useAttrs(); const attrs = useAttrs();
useModalDragMove({ useModalDragMove({
@ -18,8 +19,12 @@ export default defineComponent({
draggable, draggable,
}); });
const onCancel = (e: Event) => {
emit('cancel', e);
};
return () => { return () => {
const propsData = { ...unref(attrs), ...props } as Recordable; const propsData = { ...unref(attrs), ...props, onCancel } as Recordable;
return <Modal {...propsData}>{extendSlots(slots)}</Modal>; return <Modal {...propsData}>{extendSlots(slots)}</Modal>;
}; };
}, },

2
src/components/Modal/src/components/ModalClose.vue

@ -97,7 +97,7 @@
} }
} }
& span:nth-child(2) { & span:last-child {
&:hover { &:hover {
color: @error-color; color: @error-color;
} }

1
src/components/Modal/src/components/ModalFooter.vue

@ -19,7 +19,6 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
import { basicProps } from '../props'; import { basicProps } from '../props';
export default defineComponent({ export default defineComponent({
name: 'BasicModalFooter', name: 'BasicModalFooter',

1
src/components/Modal/src/components/ModalHeader.vue

@ -17,5 +17,6 @@
}, },
title: { type: String }, title: { type: String },
}, },
emits: ['dblclick'],
}); });
</script> </script>

65
src/components/Modal/src/components/ModalWrapper.vue

@ -1,9 +1,10 @@
<template> <template>
<ScrollContainer ref="wrapperRef"> <ScrollContainer ref="wrapperRef">
<div ref="spinRef" <div
v-loading="loading" ref="spinRef"
:style="spinStyle" v-loading="loading"
:loading-tip="loadingTip" :style="spinStyle"
:loading-tip="loadingTip"
> >
<slot/> <slot/>
</div> </div>
@ -25,7 +26,7 @@
import { useWindowSizeFn } from '/@/hooks/event/useWindowSizeFn'; import { useWindowSizeFn } from '/@/hooks/event/useWindowSizeFn';
import { ScrollContainer } from '/@/components/Container'; import { ScrollContainer } from '/@/components/Container';
import { createModalContext } from '../hooks/useModalContext'; import { createModalContext } from '../hooks/useModalContext';
import {MaybeElementRef, useMutationObserver} from '@vueuse/core'; import { useMutationObserver } from '@vueuse/core';
const props = { const props = {
loading: { type: Boolean }, loading: { type: Boolean },
@ -59,14 +60,14 @@
useWindowSizeFn(setModalHeight.bind(null, false)); useWindowSizeFn(setModalHeight.bind(null, false));
useMutationObserver( useMutationObserver(
spinRef as MaybeElementRef, spinRef,
() => { () => {
setModalHeight(); setModalHeight();
}, },
{ {
attributes: true, attributes: true,
subtree: true, subtree: true,
} },
); );
createModalContext({ createModalContext({
@ -85,15 +86,15 @@
}); });
watch( watch(
() => props.fullScreen, () => props.fullScreen,
(v) => { (v) => {
setModalHeight(); setModalHeight();
if (!v) { if (!v) {
realHeightRef.value = minRealHeightRef.value; realHeightRef.value = minRealHeightRef.value;
} else { } else {
minRealHeightRef.value = realHeightRef.value; minRealHeightRef.value = realHeightRef.value;
} }
} },
); );
onMounted(() => { onMounted(() => {
@ -132,11 +133,11 @@
const modalRect = getComputedStyle(modalDom as Element).top; const modalRect = getComputedStyle(modalDom as Element).top;
const modalTop = Number.parseInt(modalRect); const modalTop = Number.parseInt(modalRect);
let maxHeight = let maxHeight =
window.innerHeight - window.innerHeight -
modalTop * 2 + modalTop * 2 +
(props.footerOffset! || 0) - (props.footerOffset! || 0) -
props.modalFooterHeight - props.modalFooterHeight -
props.modalHeaderHeight; props.modalHeaderHeight;
// //
if (modalTop < 40) { if (modalTop < 40) {
@ -153,13 +154,13 @@
if (props.fullScreen) { if (props.fullScreen) {
realHeightRef.value = realHeightRef.value =
window.innerHeight - props.modalFooterHeight - props.modalHeaderHeight - 28; window.innerHeight - props.modalFooterHeight - props.modalHeaderHeight - 28;
} else { } else {
realHeightRef.value = props.height realHeightRef.value = props.height
? props.height ? props.height
: realHeight > maxHeight : realHeight > maxHeight
? maxHeight ? maxHeight
: realHeight; : realHeight;
} }
emit('height-change', unref(realHeightRef)); emit('height-change', unref(realHeightRef));
} catch (error) { } catch (error) {

16
src/components/Modal/src/hooks/useModal.ts

@ -40,11 +40,11 @@ export function useModal(): UseModalReturnType {
} }
uid.value = uuid; uid.value = uuid;
isProdMode() && isProdMode() &&
onUnmounted(() => { onUnmounted(() => {
modal.value = null; modal.value = null;
loaded.value = false; loaded.value = false;
dataTransfer[unref(uid)] = null; dataTransfer[unref(uid)] = null;
}); });
if (unref(loaded) && isProdMode() && modalMethod === unref(modal)) return; if (unref(loaded) && isProdMode() && modalMethod === unref(modal)) return;
modal.value = modalMethod; modal.value = modalMethod;
@ -115,9 +115,9 @@ export const useModalInner = (callbackFn?: Fn): UseModalInnerReturnType => {
const register = (modalInstance: ModalMethods, uuid: string) => { const register = (modalInstance: ModalMethods, uuid: string) => {
isProdMode() && isProdMode() &&
tryOnUnmounted(() => { tryOnUnmounted(() => {
modalInstanceRef.value = null; modalInstanceRef.value = null;
}); });
uidRef.value = uuid; uidRef.value = uuid;
modalInstanceRef.value = modalInstance; modalInstanceRef.value = modalInstance;
currentInstance?.emit('register', modalInstance, uuid); currentInstance?.emit('register', modalInstance, uuid);

5
src/components/Modal/src/index.less

@ -19,14 +19,13 @@
width: 520px; width: 520px;
padding-bottom: 0; padding-bottom: 0;
.scrollbar { .ant-modal-body > .scrollbar {
padding: 14px; padding: 14px;
} }
&-title { &-title {
font-size: 16px; font-size: 16px;
font-weight: bold; font-weight: bold;
line-height: 16px;
.base-title { .base-title {
cursor: move !important; cursor: move !important;
@ -54,7 +53,7 @@
} }
&-content { &-content {
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19); box-shadow: 0 4px 8px 0 rgb(0 0 0 / 20%), 0 6px 20px 0 rgb(0 0 0 / 19%);
} }
&-footer { &-footer {

2
src/components/Page/index.ts

@ -5,3 +5,5 @@ import pageWrapper from './src/PageWrapper.vue';
export const PageFooter = withInstall(pageFooter); export const PageFooter = withInstall(pageFooter);
export const PageWrapper = withInstall(pageWrapper); export const PageWrapper = withInstall(pageWrapper);
export const PageWrapperFixedHeightKey = 'PageWrapperFixedHeight';

4
src/components/Page/src/PageFooter.vue

@ -39,8 +39,8 @@
line-height: 44px; line-height: 44px;
background-color: @component-background; background-color: @component-background;
border-top: 1px solid @border-color-base; border-top: 1px solid @border-color-base;
box-shadow: 0 -6px 16px -8px rgba(0, 0, 0, 0.08), 0 -9px 28px 0 rgba(0, 0, 0, 0.05), box-shadow: 0 -6px 16px -8px rgb(0 0 0 / 8%), 0 -9px 28px 0 rgb(0 0 0 / 5%),
0 -12px 48px 16px rgba(0, 0, 0, 0.03); 0 -12px 48px 16px rgb(0 0 0 / 3%);
transition: width 0.2s; transition: width 0.2s;
&__left { &__left {

51
src/components/Page/src/PageWrapper.vue

@ -1,7 +1,7 @@
<template> <template>
<div ref="wrapperRef" :class="getClass"> <div ref="wrapperRef" :class="getClass">
<PageHeader <PageHeader
v-if="content || $slots.headerContent || title || getHeaderSlots.length" v-if="getShowHeader"
v-bind="omit($attrs, 'class')" v-bind="omit($attrs, 'class')"
ref="headerRef" ref="headerRef"
:ghost="ghost" :ghost="ghost"
@ -18,10 +18,11 @@
</template> </template>
</PageHeader> </PageHeader>
<div ref="contentRef" <div
class="overflow-hidden" ref="contentRef"
:class="getContentClass" class="overflow-hidden"
:style="getContentStyle" :class="getContentClass"
:style="getContentStyle"
> >
<slot/> <slot/>
</div> </div>
@ -37,7 +38,7 @@
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import type { CSSProperties, PropType } from 'vue'; import { CSSProperties, PropType, provide } from 'vue';
import { defineComponent, computed, watch, ref, unref } from 'vue'; import { defineComponent, computed, watch, ref, unref } from 'vue';
import PageFooter from './PageFooter.vue'; import PageFooter from './PageFooter.vue';
import { useDesign } from '/@/hooks/web/useDesign'; import { useDesign } from '/@/hooks/web/useDesign';
@ -45,6 +46,7 @@
import { omit } from 'lodash-es'; import { omit } from 'lodash-es';
import { PageHeader } from 'ant-design-vue'; import { PageHeader } from 'ant-design-vue';
import { useContentHeight } from '/@/hooks/web/useContentHeight'; import { useContentHeight } from '/@/hooks/web/useContentHeight';
import { PageWrapperFixedHeightKey } from '..';
export default defineComponent({ export default defineComponent({
name: 'PageWrapper', name: 'PageWrapper',
@ -62,6 +64,7 @@
contentFullHeight: propTypes.bool, contentFullHeight: propTypes.bool,
contentClass: propTypes.string, contentClass: propTypes.string,
fixedHeight: propTypes.bool, fixedHeight: propTypes.bool,
upwardSpace: propTypes.oneOfType([propTypes.number, propTypes.string]).def(0),
}, },
setup(props, { slots, attrs }) { setup(props, { slots, attrs }) {
const wrapperRef = ref(null); const wrapperRef = ref(null);
@ -70,15 +73,22 @@
const footerRef = ref(null); const footerRef = ref(null);
const { prefixCls } = useDesign('page-wrapper'); const { prefixCls } = useDesign('page-wrapper');
provide(
PageWrapperFixedHeightKey,
computed(() => props.fixedHeight),
);
const getIsContentFullHeight = computed(() => { const getIsContentFullHeight = computed(() => {
return props.contentFullHeight; return props.contentFullHeight;
}); });
const getUpwardSpace = computed(() => props.upwardSpace);
const { redoHeight, setCompensation, contentHeight } = useContentHeight( const { redoHeight, setCompensation, contentHeight } = useContentHeight(
getIsContentFullHeight, getIsContentFullHeight,
wrapperRef, wrapperRef,
[headerRef, footerRef], [headerRef, footerRef],
[contentRef] [contentRef],
getUpwardSpace,
); );
setCompensation({ useLayoutFooter: true, elements: [footerRef] }); setCompensation({ useLayoutFooter: true, elements: [footerRef] });
@ -92,6 +102,10 @@
]; ];
}); });
const getShowHeader = computed(
() => props.content || slots?.headerContent || props.title || getHeaderSlots.value.length,
);
const getShowFooter = computed(() => slots?.leftFooter || slots?.rightFooter); const getShowFooter = computed(() => slots?.leftFooter || slots?.rightFooter);
const getHeaderSlots = computed(() => { const getHeaderSlots = computed(() => {
@ -124,14 +138,14 @@
}); });
watch( watch(
() => [getShowFooter.value], () => [getShowFooter.value],
() => { () => {
redoHeight(); redoHeight();
}, },
{ {
flush: 'post', flush: 'post',
immediate: true, immediate: true,
} },
); );
return { return {
@ -143,6 +157,7 @@
getClass, getClass,
getHeaderSlots, getHeaderSlots,
prefixCls, prefixCls,
getShowHeader,
getShowFooter, getShowFooter,
omit, omit,
getContentClass, getContentClass,

4
src/components/Qrcode/src/Qrcode.vue

@ -6,7 +6,7 @@
<script lang="ts"> <script lang="ts">
import { defineComponent, watch, PropType, ref, unref, onMounted } from 'vue'; import { defineComponent, watch, PropType, ref, unref, onMounted } from 'vue';
import { toCanvas, QRCodeRenderersOptions, LogoType } from './qrcodePlus'; import { toCanvas, QRCodeRenderersOptions, LogoType } from './qrcodePlus';
import { toDataURL } from 'qrcode'; import qrcode from 'qrcode';
import { downloadByUrl } from '/@/utils/file/download'; import { downloadByUrl } from '/@/utils/file/download';
import { QrcodeDoneEventParams } from './typing'; import { QrcodeDoneEventParams } from './typing';
@ -63,7 +63,7 @@
} }
if (tag === 'img') { if (tag === 'img') {
const url = await toDataURL(renderValue, { const url = await qrcode.toDataURL(renderValue, {
errorCorrectionLevel: 'H', errorCorrectionLevel: 'H',
width, width,
...options, ...options,

11
src/components/Scrollbar/src/Scrollbar.vue

@ -152,7 +152,7 @@
display: none; display: none;
width: 0; width: 0;
height: 0; height: 0;
opacity: 0; opacity: 0%;
} }
} }
} }
@ -163,12 +163,12 @@
width: 0; width: 0;
height: 0; height: 0;
cursor: pointer; cursor: pointer;
background-color: rgba(144, 147, 153, 0.3); background-color: rgb(144 147 153 / 30%);
border-radius: inherit; border-radius: inherit;
transition: 0.3s background-color; transition: 0.3s background-color;
&:hover { &:hover {
background-color: rgba(144, 147, 153, 0.5); background-color: rgb(144 147 153 / 50%);
} }
} }
@ -178,8 +178,7 @@
bottom: 2px; bottom: 2px;
z-index: 1; z-index: 1;
border-radius: 4px; border-radius: 4px;
opacity: 0; opacity: 0%;
-webkit-transition: opacity 80ms ease;
transition: opacity 80ms ease; transition: opacity 80ms ease;
&.is-vertical { &.is-vertical {
@ -205,7 +204,7 @@
.scrollbar:active > .scrollbar__bar, .scrollbar:active > .scrollbar__bar,
.scrollbar:focus > .scrollbar__bar, .scrollbar:focus > .scrollbar__bar,
.scrollbar:hover > .scrollbar__bar { .scrollbar:hover > .scrollbar__bar {
opacity: 1; opacity: 100%;
transition: opacity 340ms ease-out; transition: opacity 340ms ease-out;
} }
</style> </style>

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save