فهرست منبع

!317 优化 vue3 配置,修复部分 bug
Merge pull request !317 from xingyu/dev

芋道源码 2 سال پیش
والد
کامیت
7f0c011123
100فایلهای تغییر یافته به همراه1414 افزوده شده و 1104 حذف شده
  1. 7 7
      yudao-dependencies/pom.xml
  2. 2 2
      yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/object/ObjectUtils.java
  3. 1 1
      yudao-framework/yudao-spring-boot-starter-biz-pay/pom.xml
  4. 1 1
      yudao-framework/yudao-spring-boot-starter-file/pom.xml
  5. 1 1
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmTaskConvert.java
  6. 4 4
      yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/api/api.ts.vm
  7. 17 2
      yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/data.ts.vm
  8. 3 3
      yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/index.vue.vm
  9. 0 1
      yudao-ui-admin-vue3/.prettierignore
  10. 76 0
      yudao-ui-admin-vue3/build/vite/index.ts
  11. 40 0
      yudao-ui-admin-vue3/build/vite/optimize.ts
  12. 18 0
      yudao-ui-admin-vue3/build/vite/styleImport.ts
  13. 22 22
      yudao-ui-admin-vue3/package.json
  14. 0 11
      yudao-ui-admin-vue3/plop/component/component.hbs
  15. 0 3
      yudao-ui-admin-vue3/plop/component/index.hbs
  16. 0 38
      yudao-ui-admin-vue3/plop/component/prompt.js
  17. 0 37
      yudao-ui-admin-vue3/plop/view/prompt.js
  18. 0 5
      yudao-ui-admin-vue3/plop/view/view.hbs
  19. 0 7
      yudao-ui-admin-vue3/plopfile.js
  20. 275 194
      yudao-ui-admin-vue3/pnpm-lock.yaml
  21. 4 9
      yudao-ui-admin-vue3/src/App.vue
  22. 1 1
      yudao-ui-admin-vue3/src/api/infra/fileConfig/index.ts
  23. 1 1
      yudao-ui-admin-vue3/src/api/login/index.ts
  24. 4 1
      yudao-ui-admin-vue3/src/components/Form/src/componentMap.ts
  25. 2 1
      yudao-ui-admin-vue3/src/components/UploadFile/index.ts
  26. 186 0
      yudao-ui-admin-vue3/src/components/UploadFile/src/UploadFile.vue
  27. 39 32
      yudao-ui-admin-vue3/src/components/UploadFile/src/UploadImg.vue
  28. 1 0
      yudao-ui-admin-vue3/src/components/Verifition/src/Verify.vue
  29. 168 198
      yudao-ui-admin-vue3/src/components/Verifition/src/Verify/VerifyPoints.vue
  30. 271 310
      yudao-ui-admin-vue3/src/components/Verifition/src/Verify/VerifySlide.vue
  31. 2 2
      yudao-ui-admin-vue3/src/components/XModal/src/XModal.vue
  32. 0 2
      yudao-ui-admin-vue3/src/components/index.ts
  33. 7 3
      yudao-ui-admin-vue3/src/config/axios/service.ts
  34. 2 2
      yudao-ui-admin-vue3/src/directives/permission/hasPermi.ts
  35. 2 2
      yudao-ui-admin-vue3/src/directives/permission/hasRole.ts
  36. 10 0
      yudao-ui-admin-vue3/src/hooks/web/useCache.ts
  37. 3 1
      yudao-ui-admin-vue3/src/hooks/web/useVxeCrudSchemas.ts
  38. 6 4
      yudao-ui-admin-vue3/src/hooks/web/useVxeGrid.ts
  39. 1 1
      yudao-ui-admin-vue3/src/layout/Layout.vue
  40. 1 1
      yudao-ui-admin-vue3/src/layout/components/AppView.vue
  41. 0 0
      yudao-ui-admin-vue3/src/layout/components/Breadcrumb/index.ts
  42. 0 0
      yudao-ui-admin-vue3/src/layout/components/Breadcrumb/src/Breadcrumb.vue
  43. 0 0
      yudao-ui-admin-vue3/src/layout/components/Breadcrumb/src/helper.ts
  44. 0 0
      yudao-ui-admin-vue3/src/layout/components/Collapse/index.ts
  45. 0 0
      yudao-ui-admin-vue3/src/layout/components/Collapse/src/Collapse.vue
  46. 0 0
      yudao-ui-admin-vue3/src/layout/components/ContextMenu/index.ts
  47. 0 0
      yudao-ui-admin-vue3/src/layout/components/ContextMenu/src/ContextMenu.vue
  48. 0 0
      yudao-ui-admin-vue3/src/layout/components/Footer/index.ts
  49. 0 0
      yudao-ui-admin-vue3/src/layout/components/Footer/src/Footer.vue
  50. 0 0
      yudao-ui-admin-vue3/src/layout/components/LocaleDropdown/index.ts
  51. 0 0
      yudao-ui-admin-vue3/src/layout/components/LocaleDropdown/src/LocaleDropdown.vue
  52. 0 0
      yudao-ui-admin-vue3/src/layout/components/Logo/index.ts
  53. 0 0
      yudao-ui-admin-vue3/src/layout/components/Logo/src/Logo.vue
  54. 0 0
      yudao-ui-admin-vue3/src/layout/components/Menu/index.ts
  55. 0 0
      yudao-ui-admin-vue3/src/layout/components/Menu/src/Menu.vue
  56. 0 0
      yudao-ui-admin-vue3/src/layout/components/Menu/src/components/useRenderMenuItem.tsx
  57. 0 0
      yudao-ui-admin-vue3/src/layout/components/Menu/src/components/useRenderMenuTitle.tsx
  58. 0 0
      yudao-ui-admin-vue3/src/layout/components/Menu/src/helper.ts
  59. 0 0
      yudao-ui-admin-vue3/src/layout/components/Screenfull/index.ts
  60. 0 0
      yudao-ui-admin-vue3/src/layout/components/Screenfull/src/Screenfull.vue
  61. 0 0
      yudao-ui-admin-vue3/src/layout/components/Setting/index.ts
  62. 5 5
      yudao-ui-admin-vue3/src/layout/components/Setting/src/Setting.vue
  63. 0 0
      yudao-ui-admin-vue3/src/layout/components/Setting/src/components/ColorRadioPicker.vue
  64. 0 0
      yudao-ui-admin-vue3/src/layout/components/Setting/src/components/InterfaceDisplay.vue
  65. 0 0
      yudao-ui-admin-vue3/src/layout/components/Setting/src/components/LayoutRadioPicker.vue
  66. 0 0
      yudao-ui-admin-vue3/src/layout/components/SizeDropdown/index.ts
  67. 0 0
      yudao-ui-admin-vue3/src/layout/components/SizeDropdown/src/SizeDropdown.vue
  68. 0 0
      yudao-ui-admin-vue3/src/layout/components/TabMenu/index.ts
  69. 1 1
      yudao-ui-admin-vue3/src/layout/components/TabMenu/src/TabMenu.vue
  70. 1 1
      yudao-ui-admin-vue3/src/layout/components/TabMenu/src/helper.ts
  71. 0 0
      yudao-ui-admin-vue3/src/layout/components/TagsView/index.ts
  72. 3 4
      yudao-ui-admin-vue3/src/layout/components/TagsView/src/TagsView.vue
  73. 0 0
      yudao-ui-admin-vue3/src/layout/components/TagsView/src/helper.ts
  74. 0 0
      yudao-ui-admin-vue3/src/layout/components/ThemeSwitch/index.ts
  75. 0 0
      yudao-ui-admin-vue3/src/layout/components/ThemeSwitch/src/ThemeSwitch.vue
  76. 6 6
      yudao-ui-admin-vue3/src/layout/components/ToolHeader.vue
  77. 0 0
      yudao-ui-admin-vue3/src/layout/components/UserInfo/index.ts
  78. 2 2
      yudao-ui-admin-vue3/src/layout/components/UserInfo/src/UserInfo.vue
  79. 4 4
      yudao-ui-admin-vue3/src/layout/components/useRenderLayout.tsx
  80. 8 8
      yudao-ui-admin-vue3/src/router/index.ts
  81. 7 7
      yudao-ui-admin-vue3/src/store/modules/app.ts
  82. 3 3
      yudao-ui-admin-vue3/src/store/modules/dict.ts
  83. 4 4
      yudao-ui-admin-vue3/src/store/modules/locale.ts
  84. 4 4
      yudao-ui-admin-vue3/src/store/modules/permission.ts
  85. 9 2
      yudao-ui-admin-vue3/src/store/modules/user.ts
  86. 2 0
      yudao-ui-admin-vue3/src/types/components.d.ts
  87. 25 23
      yudao-ui-admin-vue3/src/utils/auth.ts
  88. 3 3
      yudao-ui-admin-vue3/src/utils/routerHelper.ts
  89. 9 0
      yudao-ui-admin-vue3/src/utils/tree.ts
  90. 1 1
      yudao-ui-admin-vue3/src/views/Error/403.vue
  91. 1 1
      yudao-ui-admin-vue3/src/views/Error/404.vue
  92. 1 1
      yudao-ui-admin-vue3/src/views/Error/500.vue
  93. 5 4
      yudao-ui-admin-vue3/src/views/Home/Index.vue
  94. 2 2
      yudao-ui-admin-vue3/src/views/Login/Login.vue
  95. 92 74
      yudao-ui-admin-vue3/src/views/Login/components/LoginForm.vue
  96. 20 13
      yudao-ui-admin-vue3/src/views/Login/components/MobileForm.vue
  97. 4 6
      yudao-ui-admin-vue3/src/views/Login/components/QrCodeForm.vue
  98. 9 7
      yudao-ui-admin-vue3/src/views/Login/components/RegisterForm.vue
  99. 2 5
      yudao-ui-admin-vue3/src/views/Profile/components/BasicInfo.vue
  100. 3 3
      yudao-ui-admin-vue3/src/views/bpm/form/index.vue

+ 7 - 7
yudao-dependencies/pom.xml

@@ -28,15 +28,15 @@
         <dynamic-datasource.version>3.5.2</dynamic-datasource.version>
         <redisson.version>3.18.0</redisson.version>
         <!-- 服务保障相关 -->
-        <lock4j.version>2.2.2</lock4j.version>
+        <lock4j.version>2.2.3</lock4j.version>
         <resilience4j.version>1.7.1</resilience4j.version>
         <!-- 监控相关 -->
         <skywalking.version>8.12.0</skywalking.version>
         <spring-boot-admin.version>2.7.7</spring-boot-admin.version>
         <opentracing.version>0.33.0</opentracing.version>
         <!-- Test 测试相关 -->
-        <podam.version>7.2.9.RELEASE</podam.version>
-        <jedis-mock.version>1.0.4</jedis-mock.version>
+        <podam.version>7.2.11.RELEASE</podam.version>
+        <jedis-mock.version>1.0.5</jedis-mock.version>
         <mockito-inline.version>4.8.0</mockito-inline.version>
         <!-- Bpm 工作流相关 -->
         <flowable.version>6.7.2</flowable.version>
@@ -50,19 +50,19 @@
         <fastjson.version>1.2.83</fastjson.version>
         <guava.version>31.1-jre</guava.version>
         <guice.version>5.1.0</guice.version>
-        <transmittable-thread-local.version>2.14.0</transmittable-thread-local.version>
+        <transmittable-thread-local.version>2.14.2</transmittable-thread-local.version>
         <commons-net.version>3.8.0</commons-net.version>
         <jsch.version>0.1.55</jsch.version>
         <tika-core.version>2.5.0</tika-core.version>
         <aj-captcha.version>1.3.0</aj-captcha.version>
-        <netty-all.version>4.1.82.Final</netty-all.version>
+        <netty-all.version>4.1.85.Final</netty-all.version>
         <!-- 三方云服务相关 -->
         <okio.version>3.0.0</okio.version>
         <okhttp3.version>4.10.0</okhttp3.version>
         <minio.version>8.4.6</minio.version>
-        <aliyun-java-sdk-core.version>4.6.2</aliyun-java-sdk-core.version>
+        <aliyun-java-sdk-core.version>4.6.3</aliyun-java-sdk-core.version>
         <aliyun-java-sdk-dysmsapi.version>2.2.1</aliyun-java-sdk-dysmsapi.version>
-        <tencentcloud-sdk-java.version>3.1.635</tencentcloud-sdk-java.version>
+        <tencentcloud-sdk-java.version>3.1.637</tencentcloud-sdk-java.version>
         <justauth.version>1.4.0</justauth.version>
         <jimureport.version>1.5.4</jimureport.version>
         <xercesImpl.version>2.12.2</xercesImpl.version>

+ 2 - 2
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/object/ObjectUtils.java

@@ -1,12 +1,10 @@
 package cn.iocoder.yudao.framework.common.util.object;
 
-import cn.hutool.core.util.ArrayUtil;
 import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.core.util.ReflectUtil;
 
 import java.lang.reflect.Field;
 import java.util.Arrays;
-import java.util.Objects;
 import java.util.function.Consumer;
 
 /**
@@ -47,6 +45,7 @@ public class ObjectUtils {
         return obj1.compareTo(obj2) > 0 ? obj1 : obj2;
     }
 
+    @SafeVarargs
     public static <T> T defaultIfNull(T... array) {
         for (T item : array) {
             if (item != null) {
@@ -56,6 +55,7 @@ public class ObjectUtils {
         return null;
     }
 
+    @SafeVarargs
     public static <T> boolean equalsAny(T obj, T... array) {
         return Arrays.asList(array).contains(obj);
     }

+ 1 - 1
yudao-framework/yudao-spring-boot-starter-biz-pay/pom.xml

@@ -52,7 +52,7 @@
         <dependency>
             <groupId>com.alipay.sdk</groupId>
             <artifactId>alipay-sdk-java</artifactId>
-            <version>4.33.44.ALL</version>
+            <version>4.34.71.ALL</version>
             <exclusions>
                 <exclusion>
                     <groupId>org.bouncycastle</groupId>

+ 1 - 1
yudao-framework/yudao-spring-boot-starter-file/pom.xml

@@ -46,7 +46,7 @@
         <dependency>
             <groupId>com.fasterxml.jackson.core</groupId>
             <artifactId>jackson-databind</artifactId>
-            <version>2.13.2.2</version>
+            <version>2.13.4</version>
         </dependency>
         <dependency>
             <groupId>com.fasterxml.jackson.core</groupId>

+ 1 - 1
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmTaskConvert.java

@@ -48,7 +48,7 @@ public interface BpmTaskConvert {
             return null;
         }
         try {
-            T newInstance = target.newInstance();
+            T newInstance = target.getDeclaredConstructor().newInstance();
             BeanUtils.copyProperties(source, newInstance);
             return newInstance;
         } catch (Exception e) {

+ 4 - 4
yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/api/api.ts.vm

@@ -5,7 +5,7 @@ export interface ${simpleClassName}VO {
 #if ($column.createOperation || $column.updateOperation)
 #if(${column.javaType.toLowerCase()} == "long" || ${column.javaType.toLowerCase()} == "integer" || ${column.javaType.toLowerCase()} == "double")
   ${column.javaField}: number
-#elseif(${column.javaType.toLowerCase()} == "date")
+#elseif(${column.javaType.toLowerCase()} == "date" || ${column.javaType.toLowerCase()} == "localdatetime")
   ${column.javaField}: Date
 #else
   ${column.javaField}: ${column.javaType.toLowerCase()}
@@ -19,7 +19,7 @@ export interface ${simpleClassName}PageReqVO extends PageParam {
 #if (${column.listOperation})##查询操作
 #if(${column.javaType.toLowerCase()} == "long" || ${column.javaType.toLowerCase()} == "integer" || ${column.javaType.toLowerCase()} == "double")
   ${column.javaField}?: number
-#elseif(${column.javaType.toLowerCase()} == "date")
+#elseif(${column.javaType.toLowerCase()} == "date" || ${column.javaType.toLowerCase()} == "localdatetime")
   ${column.javaField}?: Date[]
 #else
   ${column.javaField}?: ${column.javaType.toLowerCase()}
@@ -33,8 +33,8 @@ export interface ${simpleClassName}ExcelReqVO {
 #if (${column.listOperation})##查询操作
 #if(${column.javaType.toLowerCase()} == "long" || ${column.javaType.toLowerCase()} == "integer" || ${column.javaType.toLowerCase()} == "double")
   ${column.javaField}?: number
-#elseif(${column.javaType.toLowerCase()} == "date")
-  ${column.javaField}?: string[]
+#elseif(${column.javaType.toLowerCase()} == "date" || ${column.javaType.toLowerCase()} == "localdatetime")
+  ${column.javaField}?: Date[]
 #else
   ${column.javaField}?: ${column.javaType.toLowerCase()}
 #end

+ 17 - 2
yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/data.ts.vm

@@ -28,6 +28,9 @@ const crudSchemas = reactive<VxeCrudSchema>({
     {
       title: '${column.columnComment}',
       field: '${column.javaField}',
+      #if (!$column.listOperationResult)
+      isTable: false,
+      #end
       #if ("" != $dictType)## 有数据字典
       dictType: DICT_TYPE.$dictType.toUpperCase(),
       #if (${column.javaType.toLowerCase()} == "long" || ${column.javaType.toLowerCase()} == "integer")
@@ -39,7 +42,8 @@ const crudSchemas = reactive<VxeCrudSchema>({
       #if (!$column.createOperation && !$column.updateOperation)
       isForm: false,
       #elseif(!("" != $column.dictType))
-      #if ($column.htmlType == "datetime")## 时间框
+      #if (${column.javaType.toLowerCase()} == "date" || ${column.javaType.toLowerCase()} == "localdatetime")
+      formatter: 'formatDate',
       form: {
         component: 'DatePicker',
         componentProps: {
@@ -73,9 +77,20 @@ const crudSchemas = reactive<VxeCrudSchema>({
         component: 'InputNumber',
         value: 0
       },
+      #elseif($column.htmlType == "imageUpload")## 图片上传
+      form: {
+        component: 'UploadImg',
+        componentProps: {
+          limit: 1
+        }
+      },
+      #elseif($column.htmlType == "fileUpload")## 图片上传
+      form: {
+        component: 'UploadFile'
+      },
       #end
       #end
-      #if ($column.listOperationResult)
+      #if ($column.listOperation)
       #if($column.htmlType == "input")
       isSearch: true,
       #elseif("" != $dictType)

+ 3 - 3
yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/index.vue.vm

@@ -74,7 +74,7 @@
     </template>
   </XModal>
 </template>
-<script setup lang="ts" name="${table.moduleName}">
+<script setup lang="ts" name="${simpleClassName}">
 // 全局相关的 import
 import { ref, unref } from 'vue'
 import { useI18n } from '@/hooks/web/useI18n'
@@ -91,7 +91,7 @@ const message = useMessage() // 消息弹窗
 
 // 列表相关的变量
 const xGrid = ref<VxeGridInstance>() // 列表 Grid Ref
-const { gridOptions, reloadList, deleteData, exportList } = useVxeGrid<${simpleClassName}Api.${simpleClassName}VO>({
+const { gridOptions, getList, deleteData, exportList } = useVxeGrid<${simpleClassName}Api.${simpleClassName}VO>({
   allSchemas: allSchemas,
   getListApi: ${simpleClassName}Api.get${simpleClassName}PageApi,
   deleteApi: ${simpleClassName}Api.delete${simpleClassName}Api,
@@ -169,7 +169,7 @@ const submitForm = async () => {
       } finally {
         actionLoading.value = false
         // 刷新列表
-        await reloadList(xGrid)
+        await getList(xGrid)
       }
     }
   })

+ 0 - 1
yudao-ui-admin-vue3/.prettierignore

@@ -6,5 +6,4 @@
 /vite.config.ts
 /src/types/env.d.ts
 /docs/**/*
-/plop/**/*
 CHANGELOG

+ 76 - 0
yudao-ui-admin-vue3/build/vite/index.ts

@@ -0,0 +1,76 @@
+import { resolve } from 'path'
+import Vue from '@vitejs/plugin-vue'
+import VueJsx from '@vitejs/plugin-vue-jsx'
+import VueI18n from '@intlify/vite-plugin-vue-i18n'
+import WindiCSS from 'vite-plugin-windicss'
+import progress from 'vite-plugin-progress'
+import EslintPlugin from 'vite-plugin-eslint'
+import PurgeIcons from 'vite-plugin-purge-icons'
+import { ViteEjsPlugin } from 'vite-plugin-ejs'
+import viteCompression from 'vite-plugin-compression'
+import vueSetupExtend from 'vite-plugin-vue-setup-extend'
+import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'
+import {
+  createStyleImportPlugin,
+  ElementPlusResolve,
+  VxeTableResolve
+} from 'vite-plugin-style-import'
+export function createVitePlugins(VITE_APP_TITLE: string) {
+  const root = process.cwd()
+  // 路径查找
+  function pathResolve(dir: string) {
+    return resolve(root, '.', dir)
+  }
+  return [
+    Vue(),
+    VueJsx(),
+    WindiCSS(),
+    progress(),
+    PurgeIcons(),
+    vueSetupExtend(),
+    createStyleImportPlugin({
+      resolves: [ElementPlusResolve(), VxeTableResolve()],
+      libs: [
+        {
+          libraryName: 'element-plus',
+          esModule: true,
+          resolveStyle: (name) => {
+            return `element-plus/es/components/${name.substring(3)}/style/css`
+          }
+        },
+        {
+          libraryName: 'vxe-table',
+          esModule: true,
+          resolveStyle: (name) => {
+            return `vxe-table/es/${name}/style.css`
+          }
+        }
+      ]
+    }),
+    EslintPlugin({
+      cache: false,
+      include: ['src/**/*.vue', 'src/**/*.ts', 'src/**/*.tsx'] // 检查的文件
+    }),
+    VueI18n({
+      runtimeOnly: true,
+      compositionOnly: true,
+      include: [resolve(__dirname, 'src/locales/**')]
+    }),
+    createSvgIconsPlugin({
+      iconDirs: [pathResolve('src/assets/svgs')],
+      symbolId: 'icon-[dir]-[name]',
+      svgoOptions: true
+    }),
+    viteCompression({
+      verbose: true, // 是否在控制台输出压缩结果
+      disable: false, // 是否禁用
+      threshold: 10240, // 体积大于 threshold 才会被压缩,单位 b
+      algorithm: 'gzip', // 压缩算法,可选 [ 'gzip' , 'brotliCompress' ,'deflate' , 'deflateRaw']
+      ext: '.gz', // 生成的压缩包后缀
+      deleteOriginFile: false //压缩后是否删除源文件
+    }),
+    ViteEjsPlugin({
+      title: VITE_APP_TITLE
+    })
+  ]
+}

+ 40 - 0
yudao-ui-admin-vue3/build/vite/optimize.ts

@@ -0,0 +1,40 @@
+const include = [
+  'qs',
+  'url',
+  'vue',
+  'sass',
+  'mitt',
+  'axios',
+  'pinia',
+  'dayjs',
+  'qrcode',
+  'windicss',
+  'vue-router',
+  'vue-types',
+  'vue-i18n',
+  'xe-utils',
+  'crypto-js',
+  'lodash-es',
+  'vxe-table',
+  'nprogress',
+  'animate.css',
+  'vxe-table/es/style',
+  'web-storage-cache',
+  'element-plus',
+  'element-plus/es/locale/lang/zh-cn',
+  'element-plus/es/locale/lang/en',
+  '@iconify/iconify',
+  '@vueuse/core',
+  '@zxcvbn-ts/core',
+  'echarts/core',
+  'echarts/charts',
+  'echarts/components',
+  'echarts/renderers',
+  'echarts-wordcloud',
+  '@wangeditor/editor',
+  '@wangeditor/editor-for-vue'
+]
+
+const exclude = ['@iconify/json']
+
+export { include, exclude }

+ 18 - 0
yudao-ui-admin-vue3/build/vite/styleImport.ts

@@ -0,0 +1,18 @@
+export const styleImportPlugin = {
+  libs: [
+    {
+      libraryName: 'element-plus',
+      esModule: true,
+      resolveStyle: (name) => {
+        return `element-plus/es/components/${name.substring(3)}/style/css`
+      }
+    },
+    {
+      libraryName: 'vxe-table',
+      esModule: true,
+      resolveStyle: (name) => {
+        return `vxe-table/es/${name}/style.css`
+      }
+    }
+  ]
+}

+ 22 - 22
yudao-ui-admin-vue3/package.json

@@ -1,12 +1,12 @@
 {
-  "name": "ruoyi-vue-pro-vue3",
-  "version": "1.6.4.1863",
+  "name": "yudao-ui-admin-vue3",
+  "version": "1.6.5.1874",
   "description": "基于vue3、vite3、element-plus、typesScript",
   "author": "xingyu",
   "private": false,
   "scripts": {
     "i": "pnpm install",
-    "dev": "vite --mode base --open",
+    "dev": "vite --mode base",
     "ts:check": "vue-tsc --noEmit",
     "build:pro": "vite build --mode pro",
     "build:dev": "vite build --mode dev",
@@ -21,8 +21,7 @@
     "lint:format": "prettier --write --loglevel warn \"src/**/*.{js,ts,json,tsx,css,less,scss,vue,html,md}\"",
     "lint:style": "stylelint --fix \"**/*.{vue,less,postcss,css,scss}\" --cache --cache-location node_modules/.cache/stylelint/",
     "lint:lint-staged": "lint-staged -c ",
-    "lint:pretty": "pretty-quick --staged",
-    "p": "plop"
+    "lint:pretty": "pretty-quick --staged"
   },
   "dependencies": {
     "@iconify/iconify": "^3.0.1",
@@ -31,14 +30,13 @@
     "@wangeditor/editor-for-vue": "^5.1.10",
     "@zxcvbn-ts/core": "^2.1.0",
     "animate.css": "^4.1.1",
-    "axios": "^1.2.0",
+    "axios": "^1.2.1",
     "crypto-js": "^4.1.1",
-    "dayjs": "^1.11.6",
+    "dayjs": "^1.11.7",
     "echarts": "^5.4.0",
     "echarts-wordcloud": "^2.1.0",
-    "element-plus": "2.2.25",
+    "element-plus": "2.2.26",
     "intro.js": "^6.0.0",
-    "js-cookie": "^3.0.1",
     "jsencrypt": "^3.3.1",
     "lodash-es": "^4.17.21",
     "mitt": "^3.0.0",
@@ -59,52 +57,54 @@
   "devDependencies": {
     "@commitlint/cli": "^17.3.0",
     "@commitlint/config-conventional": "^17.3.0",
-    "@iconify/json": "^2.1.145",
+    "@iconify/json": "^2.1.149",
     "@intlify/vite-plugin-vue-i18n": "^6.0.3",
     "@purge-icons/generated": "^0.9.0",
     "@types/intro.js": "^5.1.0",
     "@types/lodash-es": "^4.17.6",
-    "@types/node": "^18.11.9",
+    "@types/node": "^18.11.11",
     "@types/nprogress": "^0.2.0",
     "@types/qrcode": "^1.5.0",
     "@types/qs": "^6.9.7",
-    "@typescript-eslint/eslint-plugin": "^5.45.0",
-    "@typescript-eslint/parser": "^5.45.0",
+    "@typescript-eslint/eslint-plugin": "^5.45.1",
+    "@typescript-eslint/parser": "^5.45.1",
+    "@vitejs/plugin-legacy": "^2.3.1",
     "@vitejs/plugin-vue": "^3.2.0",
     "@vitejs/plugin-vue-jsx": "^2.1.1",
     "autoprefixer": "^10.4.13",
-    "eslint": "^8.28.0",
+    "consola": "^2.15.3",
+    "eslint": "^8.29.0",
     "eslint-config-prettier": "^8.5.0",
     "eslint-define-config": "^1.12.0",
     "eslint-plugin-prettier": "^4.2.1",
     "eslint-plugin-vue": "^9.8.0",
-    "lint-staged": "^13.0.4",
-    "plop": "^3.1.1",
+    "lint-staged": "^13.1.0",
     "postcss": "^8.4.19",
     "postcss-html": "^1.5.0",
     "postcss-scss": "^4.0.6",
     "prettier": "^2.8.0",
     "rimraf": "^3.0.2",
-    "rollup": "^3.5.0",
+    "rollup": "^3.6.0",
     "sass": "^1.56.1",
-    "stylelint": "^14.15.0",
+    "stylelint": "^14.16.0",
     "stylelint-config-html": "^1.1.0",
     "stylelint-config-prettier": "^9.0.4",
     "stylelint-config-recommended": "^9.0.0",
     "stylelint-config-standard": "^29.0.0",
     "stylelint-order": "^5.0.0",
+    "terser": "^5.16.1",
     "typescript": "4.9.3",
-    "vite": "3.2.4",
+    "vite": "3.2.5",
     "vite-plugin-compression": "^0.5.1",
+    "vite-plugin-ejs": "^1.6.4",
     "vite-plugin-eslint": "^1.8.1",
-    "vite-plugin-html": "^3.2.0",
     "vite-plugin-progress": "^0.0.6",
     "vite-plugin-purge-icons": "^0.9.1",
     "vite-plugin-style-import": "2.0.0",
     "vite-plugin-svg-icons": "^2.0.1",
     "vite-plugin-vue-setup-extend": "^0.4.0",
     "vite-plugin-windicss": "^1.8.8",
-    "vue-tsc": "^1.0.10",
+    "vue-tsc": "^1.0.11",
     "windicss": "^3.5.6"
   },
   "engines": {
@@ -116,7 +116,7 @@
     "url": "git+https://gitee.com/zhijiantianya/ruoyi-vue-pro"
   },
   "bugs": {
-    "url": "https://gitee.com/zhijiantianya/ruoyi-vue-pro/issues/I57XOQ"
+    "url": "https://gitee.com/zhijiantianya/ruoyi-vue-pro/issues"
   },
   "homepage": "https://gitee.com/zhijiantianya/ruoyi-vue-pro"
 }

+ 0 - 11
yudao-ui-admin-vue3/plop/component/component.hbs

@@ -1,11 +0,0 @@
-<script setup lang="ts">
-import { useDesign } from '@/hooks/web/useDesign'
-
-const { getPrefixCls } = useDesign()
-
-const prefixCls = getPrefixCls('{{ name }}')
-</script>
-
-<template>
-  <div :class="`${prefixCls}-{{ name }}`">{{ upperFirstName }}</div>
-</template>

+ 0 - 3
yudao-ui-admin-vue3/plop/component/index.hbs

@@ -1,3 +0,0 @@
-import {{ upperFirstName }} from './src/{{ upperFirstName }}.vue'
-
-export { {{ upperFirstName }} }

+ 0 - 38
yudao-ui-admin-vue3/plop/component/prompt.js

@@ -1,38 +0,0 @@
-const toUpperCase = (str) => str.charAt(0).toUpperCase() + str.slice(1)
-
-module.exports = {
-  description: 'Create vue component',
-  prompts: [
-    {
-      type: 'input',
-      name: 'name',
-      message: '请输入组件名称(Please enter the component name)'
-    }
-  ],
-  actions: (data) => {
-    const { name } = data
-    const upperFirstName = toUpperCase(name)
-
-    const actions = []
-    if (name) {
-      actions.push({
-        type: 'add',
-        path: `./src/components/${upperFirstName}/src/${upperFirstName}.vue`,
-        templateFile: './plop/component/component.hbs',
-        data: {
-          name,
-          upperFirstName
-        }
-      }, {
-        type: 'add',
-        path: `./src/components/${upperFirstName}/index.ts`,
-        templateFile: './plop/component/index.hbs',
-        data: {
-          upperFirstName
-        }
-      })
-    }
-
-    return actions
-  }
-}

+ 0 - 37
yudao-ui-admin-vue3/plop/view/prompt.js

@@ -1,37 +0,0 @@
-const toUpperCase = (str) => str.charAt(0).toUpperCase() + str.slice(1)
-
-module.exports = {
-  description: 'Create vue view',
-  prompts: [
-    {
-      type: 'input',
-      name: 'path',
-      message: '请输入路径(Please enter a path)',
-      default: 'views'
-    },
-    {
-      type: 'input',
-      name: 'name',
-      message: '请输入模块名称(Please enter module name)'
-    }
-  ],
-  actions: (data) => {
-    const { name, path } = data
-    const upperFirstName = toUpperCase(name)
-
-    const actions = []
-    if (name) {
-      actions.push({
-        type: 'add',
-        path: `./src/${path}/${upperFirstName}.vue`,
-        templateFile: './plop/view/view.hbs',
-        data: {
-          name,
-          upperFirstName
-        }
-      })
-    }
-
-    return actions
-  }
-}

+ 0 - 5
yudao-ui-admin-vue3/plop/view/view.hbs

@@ -1,5 +0,0 @@
-<template>
-  <ContentWrap title="{{ upperFirstName }}"> {{ name }} </ContentWrap>
-</template>
-<script setup lang="ts" name="{{ name }}">
-</script>

+ 0 - 7
yudao-ui-admin-vue3/plopfile.js

@@ -1,7 +0,0 @@
-const viewGenerator = require('./plop/view/prompt.js')
-const componentGenerator = require('./plop/component/prompt.js')
-
-module.exports = function (plop) {
-  plop.setGenerator('view', viewGenerator)
-  plop.setGenerator('component', componentGenerator)
-}

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 275 - 194
yudao-ui-admin-vue3/pnpm-lock.yaml


+ 4 - 9
yudao-ui-admin-vue3/src/App.vue

@@ -4,7 +4,7 @@ import { isDark } from '@/utils/is'
 import { useAppStore } from '@/store/modules/app'
 import { useDesign } from '@/hooks/web/useDesign'
 import { ConfigGlobal } from '@/components/ConfigGlobal'
-import { useCache } from '@/hooks/web/useCache'
+import { CACHE_KEY, useCache } from '@/hooks/web/useCache'
 
 const { getPrefixCls } = useDesign()
 const prefixCls = getPrefixCls('app')
@@ -15,15 +15,10 @@ const { wsCache } = useCache()
 
 // 根据浏览器当前主题设置系统主题色
 const setDefaultTheme = () => {
-  if (wsCache.get('isDark')) {
-    if (wsCache.get('isDark') || wsCache.get('isDark') === 'true') {
-      appStore.setIsDark(true)
-    } else {
-      appStore.setIsDark(false)
-    }
-    return
+  let isDarkTheme = wsCache.get(CACHE_KEY.IS_DARK)
+  if (isDarkTheme === null) {
+    isDarkTheme = isDark()
   }
-  const isDarkTheme = isDark()
   appStore.setIsDark(isDarkTheme)
 }
 setDefaultTheme()

+ 1 - 1
yudao-ui-admin-vue3/src/api/infra/fileConfig/index.ts

@@ -42,7 +42,7 @@ export const getFileConfigApi = (id: number) => {
 
 // 更新文件配置为主配置
 export const updateFileConfigMasterApi = (id: number) => {
-  return request.get({ url: '/infra/file-config/update-master?id=' + id })
+  return request.put({ url: '/infra/file-config/update-master?id=' + id })
 }
 
 // 新增文件配置

+ 1 - 1
yudao-ui-admin-vue3/src/api/login/index.ts

@@ -57,7 +57,7 @@ export const smsLoginApi = (data: SmsLoginVO) => {
 }
 
 // 社交授权的跳转
-export const socialAuthRedirectApi = (type: string, redirectUri: string) => {
+export const socialAuthRedirectApi = (type: number, redirectUri: string) => {
   return request.get({
     url: '/system/auth/social-auth-redirect?type=' + type + '&redirectUri=' + redirectUri
   })

+ 4 - 1
yudao-ui-admin-vue3/src/components/Form/src/componentMap.ts

@@ -21,6 +21,7 @@ import {
 } from 'element-plus'
 import { InputPassword } from '@/components/InputPassword'
 import { Editor } from '@/components/Editor'
+import { UploadImg, UploadFile } from '@/components/UploadFile'
 import { ComponentName } from '@/types/components'
 
 const componentMap: Recordable<Component, ComponentName> = {
@@ -45,7 +46,9 @@ const componentMap: Recordable<Component, ComponentName> = {
   TreeSelect: ElTreeSelect,
   RadioButton: ElRadioGroup,
   InputPassword: InputPassword,
-  Editor: Editor
+  Editor: Editor,
+  UploadImg: UploadImg,
+  UploadFile: UploadFile
 }
 
 export { componentMap }

+ 2 - 1
yudao-ui-admin-vue3/src/components/UploadFile/index.ts

@@ -1,3 +1,4 @@
 import UploadImg from './src/UploadImg.vue'
+import UploadFile from './src/UploadFile.vue'
 
-export { UploadImg }
+export { UploadImg, UploadFile }

+ 186 - 0
yudao-ui-admin-vue3/src/components/UploadFile/src/UploadFile.vue

@@ -0,0 +1,186 @@
+<template>
+  <div class="upload-file">
+    <el-upload
+      ref="uploadRef"
+      :multiple="props.limit > 1"
+      name="file"
+      v-model="valueRef"
+      v-model:file-list="fileList"
+      :show-file-list="true"
+      :auto-upload="autoUpload"
+      :action="updateUrl"
+      :headers="uploadHeaders"
+      :limit="props.limit"
+      :drag="drag"
+      :before-upload="beforeUpload"
+      :on-exceed="handleExceed"
+      :on-success="handleFileSuccess"
+      :on-error="excelUploadError"
+      :on-remove="handleRemove"
+      :on-preview="handlePreview"
+      class="upload-file-uploader"
+    >
+      <el-button type="primary"><Icon icon="ep:upload-filled" />选取文件</el-button>
+      <template v-if="isShowTip" #tip>
+        <div style="font-size: 8px">
+          大小不超过 <b style="color: #f56c6c">{{ fileSize }}MB</b>
+        </div>
+        <div style="font-size: 8px">
+          格式为 <b style="color: #f56c6c">{{ fileType.join('/') }}</b> 的文件
+        </div>
+      </template>
+    </el-upload>
+  </div>
+</template>
+<script setup lang="ts">
+import { ref, watch } from 'vue'
+import { useMessage } from '@/hooks/web/useMessage'
+import { propTypes } from '@/utils/propTypes'
+import { getAccessToken, getTenantId } from '@/utils/auth'
+import { ElUpload, UploadInstance, UploadProps, UploadRawFile, UploadUserFile } from 'element-plus'
+
+const message = useMessage() // 消息弹窗
+const emit = defineEmits(['update:modelValue'])
+
+const props = defineProps({
+  modelValue: propTypes.oneOfType([String, Object, Array]),
+  title: propTypes.string.def('文件上传'),
+  updateUrl: propTypes.string.def(import.meta.env.VITE_UPLOAD_URL),
+  fileType: propTypes.array.def(['doc', 'xls', 'ppt', 'txt', 'pdf']), // 文件类型, 例如['png', 'jpg', 'jpeg']
+  fileSize: propTypes.number.def(5), // 大小限制(MB)
+  limit: propTypes.number.def(5), // 数量限制
+  autoUpload: propTypes.bool.def(true), // 自动上传
+  drag: propTypes.bool.def(false), // 拖拽上传
+  isShowTip: propTypes.bool.def(true) // 是否显示提示
+})
+// ========== 上传相关 ==========
+const valueRef = ref(props.modelValue)
+const uploadRef = ref<UploadInstance>()
+const uploadList = ref<UploadUserFile[]>([])
+const fileList = ref<UploadUserFile[]>([])
+const uploadNumber = ref<number>(0)
+const uploadHeaders = ref({
+  Authorization: 'Bearer ' + getAccessToken(),
+  'tenant-id': getTenantId()
+})
+watch(
+  () => props.modelValue,
+  (val) => {
+    if (val) {
+      // 首先将值转为数组, 当只穿了一个图片时,会报map方法错误
+      const list = Array.isArray(props.modelValue)
+        ? props.modelValue
+        : Array.isArray(props.modelValue?.split(','))
+        ? props.modelValue?.split(',')
+        : Array.of(props.modelValue)
+      // 然后将数组转为对象数组
+      fileList.value = list.map((item) => {
+        if (typeof item === 'string') {
+          // edit by 芋道源码
+          item = { name: item, url: item }
+        }
+        return item
+      })
+    } else {
+      fileList.value = []
+      return []
+    }
+  },
+  {
+    deep: true,
+    immediate: true
+  }
+)
+// 文件上传之前判断
+const beforeUpload: UploadProps['beforeUpload'] = (file: UploadRawFile) => {
+  if (fileList.value.length >= props.limit) {
+    message.error(`上传文件数量不能超过${props.limit}个!`)
+    return false
+  }
+  let fileExtension = ''
+  if (file.name.lastIndexOf('.') > -1) {
+    fileExtension = file.name.slice(file.name.lastIndexOf('.') + 1)
+  }
+  const isImg = props.fileType.some((type: string) => {
+    if (file.type.indexOf(type) > -1) return true
+    return !!(fileExtension && fileExtension.indexOf(type) > -1)
+  })
+  const isLimit = file.size < props.fileSize * 1024 * 1024
+  if (!isImg) {
+    message.error(`文件格式不正确, 请上传${props.fileType.join('/')}格式!`)
+    return false
+  }
+  if (!isLimit) {
+    message.error(`上传文件大小不能超过${props.fileSize}MB!`)
+    return false
+  }
+  message.success('正在上传文件,请稍候...')
+  uploadNumber.value++
+}
+// 处理上传的文件发生变化
+// const handleFileChange = (uploadFile: UploadFile): void => {
+//   uploadRef.value.data.path = uploadFile.name
+// }
+// 文件上传成功
+const handleFileSuccess: UploadProps['onSuccess'] = (res: any): void => {
+  message.success('上传成功')
+  uploadList.value.push({ name: res.data, url: res.data })
+  if (uploadList.value.length == uploadNumber.value) {
+    fileList.value = fileList.value.concat(uploadList.value)
+    uploadList.value = []
+    uploadNumber.value = 0
+    emit('update:modelValue', listToString(fileList.value))
+  }
+}
+// 文件数超出提示
+const handleExceed: UploadProps['onExceed'] = (): void => {
+  message.error(`上传文件数量不能超过${props.limit}个!`)
+}
+// 上传错误提示
+const excelUploadError: UploadProps['onError'] = (): void => {
+  message.error('导入数据失败,请您重新上传!')
+}
+// 删除上传文件
+const handleRemove = (file) => {
+  const findex = fileList.value.map((f) => f.name).indexOf(file.name)
+  if (findex > -1) {
+    fileList.value.splice(findex, 1)
+    emit('update:modelValue', listToString(fileList.value))
+  }
+}
+const handlePreview: UploadProps['onPreview'] = (uploadFile) => {
+  console.log(uploadFile)
+}
+// 对象转成指定字符串分隔
+const listToString = (list: UploadUserFile[], separator?: string) => {
+  let strs = ''
+  separator = separator || ','
+  for (let i in list) {
+    strs += list[i].url + separator
+  }
+  return strs != '' ? strs.substr(0, strs.length - 1) : ''
+}
+</script>
+<style scoped lang="scss">
+.upload-file-uploader {
+  margin-bottom: 5px;
+}
+:deep(.upload-file-list .el-upload-list__item) {
+  border: 1px solid #e4e7ed;
+  line-height: 2;
+  margin-bottom: 10px;
+  position: relative;
+}
+:deep(.el-upload-list__item-file-name) {
+  max-width: 250px;
+}
+:deep(.upload-file-list .ele-upload-list__item-content) {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  color: inherit;
+}
+:deep(.ele-upload-list__item-content-action .el-link) {
+  margin-right: 10px;
+}
+</style>

+ 39 - 32
yudao-ui-admin-vue3/src/components/UploadFile/src/UploadImg.vue

@@ -1,24 +1,27 @@
 <template>
-  <el-upload
-    ref="uploadRef"
-    :multiple="limit > 1"
-    name="file"
-    list-type="picture-card"
-    v-model:file-list="fileList"
-    :show-file-list="true"
-    :action="updateUrl"
-    :headers="uploadHeaders"
-    :limit="limit"
-    :before-upload="beforeUpload"
-    :on-exceed="handleExceed"
-    :on-success="handleFileSuccess"
-    :on-error="excelUploadError"
-    :on-remove="handleRemove"
-    :on-preview="handlePictureCardPreview"
-    :class="{ hide: fileList.length >= limit }"
-  >
-    <Icon icon="ep:upload-filled" />
-  </el-upload>
+  <div class="component-upload-image">
+    <el-upload
+      ref="uploadRef"
+      :multiple="props.limit > 1"
+      name="file"
+      v-model="valueRef"
+      list-type="picture-card"
+      v-model:file-list="fileList"
+      :show-file-list="true"
+      :action="updateUrl"
+      :headers="uploadHeaders"
+      :limit="props.limit"
+      :before-upload="beforeUpload"
+      :on-exceed="handleExceed"
+      :on-success="handleFileSuccess"
+      :on-error="excelUploadError"
+      :on-remove="handleRemove"
+      :on-preview="handlePictureCardPreview"
+      :class="{ hide: fileList.length >= props.limit }"
+    >
+      <Icon icon="ep:upload-filled" />
+    </el-upload>
+  </div>
   <!-- 文件列表 -->
   <Dialog v-model="dialogVisible" title="预览" width="800" append-to-body>
     <img :src="dialogImageUrl" style="display: block; max-width: 100%; margin: 0 auto" />
@@ -26,16 +29,17 @@
 </template>
 <script setup lang="ts">
 import { ref, watch } from 'vue'
+import { Dialog } from '@/components/Dialog'
 import { useMessage } from '@/hooks/web/useMessage'
 import { propTypes } from '@/utils/propTypes'
 import { getAccessToken, getTenantId } from '@/utils/auth'
 import { ElUpload, UploadInstance, UploadProps, UploadRawFile, UploadUserFile } from 'element-plus'
 
 const message = useMessage() // 消息弹窗
-const emit = defineEmits(['input'])
+const emit = defineEmits(['update:modelValue'])
 
 const props = defineProps({
-  imgs: propTypes.oneOfType([String, Object, Array]),
+  modelValue: propTypes.oneOfType([String, Object, Array]),
   title: propTypes.string.def('图片上传'),
   updateUrl: propTypes.string.def(import.meta.env.VITE_UPLOAD_URL),
   fileType: propTypes.array.def(['jpg', 'png', 'gif', 'jpeg']), // 文件类型, 例如['png', 'jpg', 'jpeg']
@@ -44,6 +48,7 @@ const props = defineProps({
   isShowTip: propTypes.bool.def(false) // 是否显示提示
 })
 // ========== 上传相关 ==========
+const valueRef = ref(props.modelValue)
 const uploadRef = ref<UploadInstance>()
 const uploadList = ref<UploadUserFile[]>([])
 const fileList = ref<UploadUserFile[]>([])
@@ -55,15 +60,15 @@ const uploadHeaders = ref({
   'tenant-id': getTenantId()
 })
 watch(
-  () => props.imgs,
+  () => props.modelValue,
   (val) => {
     if (val) {
       // 首先将值转为数组, 当只穿了一个图片时,会报map方法错误
-      const list = Array.isArray(props.imgs)
-        ? props.imgs
-        : Array.isArray(props.imgs?.split(','))
-        ? props.imgs?.split(',')
-        : Array.of(props.imgs)
+      const list = Array.isArray(props.modelValue)
+        ? props.modelValue
+        : Array.isArray(props.modelValue?.split(','))
+        ? props.modelValue?.split(',')
+        : Array.of(props.modelValue)
       // 然后将数组转为对象数组
       fileList.value = list.map((item) => {
         if (typeof item === 'string') {
@@ -84,6 +89,10 @@ watch(
 )
 // 文件上传之前判断
 const beforeUpload: UploadProps['beforeUpload'] = (file: UploadRawFile) => {
+  if (fileList.value.length >= props.limit) {
+    message.error(`上传文件数量不能超过${props.limit}个!`)
+    return false
+  }
   let fileExtension = ''
   if (file.name.lastIndexOf('.') > -1) {
     fileExtension = file.name.slice(file.name.lastIndexOf('.') + 1)
@@ -111,14 +120,12 @@ const beforeUpload: UploadProps['beforeUpload'] = (file: UploadRawFile) => {
 // 文件上传成功
 const handleFileSuccess: UploadProps['onSuccess'] = (res: any): void => {
   message.success('上传成功')
-  console.info(uploadList.value)
-  console.info(fileList.value)
   uploadList.value.push({ name: res.data, url: res.data })
   if (uploadList.value.length == uploadNumber.value) {
     fileList.value = fileList.value.concat(uploadList.value)
     uploadList.value = []
     uploadNumber.value = 0
-    emit('input', listToString(fileList.value))
+    emit('update:modelValue', listToString(fileList.value))
   }
 }
 // 文件数超出提示
@@ -134,7 +141,7 @@ const handleRemove = (file) => {
   const findex = fileList.value.map((f) => f.name).indexOf(file.name)
   if (findex > -1) {
     fileList.value.splice(findex, 1)
-    emit('input', listToString(fileList.value))
+    emit('update:modelValue', listToString(fileList.value))
   }
 }
 // 对象转成指定字符串分隔

+ 1 - 0
yudao-ui-admin-vue3/src/components/Verifition/src/Verify.vue

@@ -191,6 +191,7 @@ export default {
   transition: all 0.5s;
 }
 .verify-tips {
+  text-indent: 10px;
   position: absolute;
   left: 0px;
   bottom: 0px;

+ 168 - 198
yudao-ui-admin-vue3/src/components/Verifition/src/Verify/VerifyPoints.vue

@@ -57,7 +57,7 @@
     </div>
   </div>
 </template>
-<script type="text/babel">
+<script type="text/babel" setup>
 /**
  * VerifyPoints
  * @description 点选
@@ -67,215 +67,185 @@ import { aesEncrypt } from './../utils/ase'
 import { getCodeApi, reqCheckApi } from '@/api/login'
 import { onMounted, reactive, ref, nextTick, toRefs, getCurrentInstance } from 'vue'
 import { useI18n } from '@/hooks/web/useI18n'
-export default {
-  name: 'VerifyPoints',
-  props: {
-    //弹出式pop,固定fixed
-    mode: {
-      type: String,
-      default: 'fixed'
-    },
-    captchaType: {
-      type: String
-    },
-    //间隔
-    vSpace: {
-      type: Number,
-      default: 5
-    },
-    imgSize: {
-      type: Object,
-      default() {
-        return {
-          width: '310px',
-          height: '155px'
-        }
-      }
-    },
-    barSize: {
-      type: Object,
-      default() {
-        return {
-          width: '310px',
-          height: '40px'
-        }
+
+const props = defineProps({
+  //弹出式pop,固定fixed
+  mode: {
+    type: String,
+    default: 'fixed'
+  },
+  captchaType: {
+    type: String
+  },
+  //间隔
+  vSpace: {
+    type: Number,
+    default: 5
+  },
+  imgSize: {
+    type: Object,
+    default() {
+      return {
+        width: '310px',
+        height: '155px'
       }
     }
   },
-  setup(props) {
-    const { t } = useI18n()
-    const { mode, captchaType } = toRefs(props)
-    const { proxy } = getCurrentInstance()
-    let secretKey = ref(''), //后端返回的ase加密秘钥
-      checkNum = ref(3), //默认需要点击的字数
-      fontPos = reactive([]), //选中的坐标信息
-      checkPosArr = reactive([]), //用户点击的坐标
-      num = ref(1), //点击的记数
-      pointBackImgBase = ref(''), //后端获取到的背景图片
-      poinTextList = reactive([]), //后端返回的点击字体顺序
-      backToken = ref(''), //后端返回的token值
-      setSize = reactive({
-        imgHeight: 0,
-        imgWidth: 0,
-        barHeight: 0,
-        barWidth: 0
-      }),
-      tempPoints = reactive([]),
-      text = ref(''),
-      barAreaColor = ref(undefined),
-      barAreaBorderColor = ref(undefined),
-      showRefresh = ref(true),
-      bindingClick = ref(true)
-
-    const init = () => {
-      //加载页面
-      fontPos.splice(0, fontPos.length)
-      checkPosArr.splice(0, checkPosArr.length)
-      num.value = 1
-      getPictrue()
-      nextTick(() => {
-        let { imgHeight, imgWidth, barHeight, barWidth } = resetSize(proxy)
-        setSize.imgHeight = imgHeight
-        setSize.imgWidth = imgWidth
-        setSize.barHeight = barHeight
-        setSize.barWidth = barWidth
-        proxy.$parent.$emit('ready', proxy)
-      })
-    }
-    onMounted(() => {
-      // 禁止拖拽
-      init()
-      proxy.$el.onselectstart = function () {
-        return false
-      }
-    })
-    const canvas = ref(null)
-    const canvasClick = (e) => {
-      checkPosArr.push(getMousePos(canvas, e))
-      if (num.value == checkNum.value) {
-        num.value = createPoint(getMousePos(canvas, e))
-        //按比例转换坐标值
-        let arr = pointTransfrom(checkPosArr, setSize)
-        checkPosArr.length = 0
-        checkPosArr.push(...arr)
-        //等创建坐标执行完
-        setTimeout(() => {
-          // var flag = this.comparePos(this.fontPos, this.checkPosArr);
-          //发送后端请求
-          var captchaVerification = secretKey.value
-            ? aesEncrypt(backToken.value + '---' + JSON.stringify(checkPosArr), secretKey.value)
-            : backToken.value + '---' + JSON.stringify(checkPosArr)
-          let data = {
-            captchaType: captchaType.value,
-            pointJson: secretKey.value
-              ? aesEncrypt(JSON.stringify(checkPosArr), secretKey.value)
-              : JSON.stringify(checkPosArr),
-            token: backToken.value
-          }
-          reqCheckApi(data).then((res) => {
-            if (res.repCode == '0000') {
-              barAreaColor.value = '#4cae4c'
-              barAreaBorderColor.value = '#5cb85c'
-              text.value = t('captcha.success')
-              bindingClick.value = false
-              if (mode.value == 'pop') {
-                setTimeout(() => {
-                  proxy.$parent.clickShow = false
-                  refresh()
-                }, 1500)
-              }
-              proxy.$parent.$emit('success', { captchaVerification })
-            } else {
-              proxy.$parent.$emit('error', proxy)
-              barAreaColor.value = '#d9534f'
-              barAreaBorderColor.value = '#d9534f'
-              text.value = t('captcha.fail')
-              setTimeout(() => {
-                refresh()
-              }, 700)
-            }
-          })
-        }, 400)
-      }
-      if (num.value < checkNum.value) {
-        num.value = createPoint(getMousePos(canvas, e))
+  barSize: {
+    type: Object,
+    default() {
+      return {
+        width: '310px',
+        height: '40px'
       }
     }
-    //获取坐标
-    const getMousePos = function (obj, e) {
-      var x = e.offsetX
-      var y = e.offsetY
-      return { x, y }
-    }
-    //创建坐标点
-    const createPoint = function (pos) {
-      tempPoints.push(Object.assign({}, pos))
-      return num.value + 1
-    }
-    const refresh = function () {
-      tempPoints.splice(0, tempPoints.length)
-      barAreaColor.value = '#000'
-      barAreaBorderColor.value = '#ddd'
-      bindingClick.value = true
-      fontPos.splice(0, fontPos.length)
-      checkPosArr.splice(0, checkPosArr.length)
-      num.value = 1
-      getPictrue()
-      text.value = t('captcha.fail')
-      showRefresh.value = true
-    }
+  }
+})
 
-    // 请求背景图片和验证图片
-    function getPictrue() {
+const { t } = useI18n()
+const { mode, captchaType } = toRefs(props)
+const { proxy } = getCurrentInstance()
+let secretKey = ref(''), //后端返回的ase加密秘钥
+  checkNum = ref(3), //默认需要点击的字数
+  fontPos = reactive([]), //选中的坐标信息
+  checkPosArr = reactive([]), //用户点击的坐标
+  num = ref(1), //点击的记数
+  pointBackImgBase = ref(''), //后端获取到的背景图片
+  poinTextList = reactive([]), //后端返回的点击字体顺序
+  backToken = ref(''), //后端返回的token值
+  setSize = reactive({
+    imgHeight: 0,
+    imgWidth: 0,
+    barHeight: 0,
+    barWidth: 0
+  }),
+  tempPoints = reactive([]),
+  text = ref(''),
+  barAreaColor = ref(undefined),
+  barAreaBorderColor = ref(undefined),
+  showRefresh = ref(true),
+  bindingClick = ref(true)
+
+const init = () => {
+  //加载页面
+  fontPos.splice(0, fontPos.length)
+  checkPosArr.splice(0, checkPosArr.length)
+  num.value = 1
+  getPictrue()
+  nextTick(() => {
+    let { imgHeight, imgWidth, barHeight, barWidth } = resetSize(proxy)
+    setSize.imgHeight = imgHeight
+    setSize.imgWidth = imgWidth
+    setSize.barHeight = barHeight
+    setSize.barWidth = barWidth
+    proxy.$parent.$emit('ready', proxy)
+  })
+}
+onMounted(() => {
+  // 禁止拖拽
+  init()
+  proxy.$el.onselectstart = function () {
+    return false
+  }
+})
+const canvas = ref(null)
+const canvasClick = (e) => {
+  checkPosArr.push(getMousePos(canvas, e))
+  if (num.value == checkNum.value) {
+    num.value = createPoint(getMousePos(canvas, e))
+    //按比例转换坐标值
+    let arr = pointTransfrom(checkPosArr, setSize)
+    checkPosArr.length = 0
+    checkPosArr.push(...arr)
+    //等创建坐标执行完
+    setTimeout(() => {
+      // var flag = this.comparePos(this.fontPos, this.checkPosArr);
+      //发送后端请求
+      var captchaVerification = secretKey.value
+        ? aesEncrypt(backToken.value + '---' + JSON.stringify(checkPosArr), secretKey.value)
+        : backToken.value + '---' + JSON.stringify(checkPosArr)
       let data = {
-        captchaType: captchaType.value
+        captchaType: captchaType.value,
+        pointJson: secretKey.value
+          ? aesEncrypt(JSON.stringify(checkPosArr), secretKey.value)
+          : JSON.stringify(checkPosArr),
+        token: backToken.value
       }
-      getCodeApi(data).then((res) => {
+      reqCheckApi(data).then((res) => {
         if (res.repCode == '0000') {
-          pointBackImgBase.value = res.repData.originalImageBase64
-          backToken.value = res.repData.token
-          secretKey.value = res.repData.secretKey
-          poinTextList.value = res.repData.wordList
-          text.value = t('captcha.point') + '【' + poinTextList.value.join(',') + '】'
+          barAreaColor.value = '#4cae4c'
+          barAreaBorderColor.value = '#5cb85c'
+          text.value = t('captcha.success')
+          bindingClick.value = false
+          if (mode.value == 'pop') {
+            setTimeout(() => {
+              proxy.$parent.clickShow = false
+              refresh()
+            }, 1500)
+          }
+          proxy.$parent.$emit('success', { captchaVerification })
         } else {
-          text.value = res.repMsg
+          proxy.$parent.$emit('error', proxy)
+          barAreaColor.value = '#d9534f'
+          barAreaBorderColor.value = '#d9534f'
+          text.value = t('captcha.fail')
+          setTimeout(() => {
+            refresh()
+          }, 700)
         }
       })
-    }
-    //坐标转换函数
-    const pointTransfrom = function (pointArr, imgSize) {
-      var newPointArr = pointArr.map((p) => {
-        let x = Math.round((310 * p.x) / parseInt(imgSize.imgWidth))
-        let y = Math.round((155 * p.y) / parseInt(imgSize.imgHeight))
-        return { x, y }
-      })
-      return newPointArr
-    }
-    return {
-      secretKey,
-      checkNum,
-      fontPos,
-      checkPosArr,
-      num,
-      pointBackImgBase,
-      poinTextList,
-      backToken,
-      setSize,
-      tempPoints,
-      text,
-      barAreaColor,
-      barAreaBorderColor,
-      showRefresh,
-      bindingClick,
-      init,
-      canvas,
-      canvasClick,
-      getMousePos,
-      createPoint,
-      refresh,
-      getPictrue,
-      pointTransfrom
-    }
+    }, 400)
+  }
+  if (num.value < checkNum.value) {
+    num.value = createPoint(getMousePos(canvas, e))
   }
 }
+//获取坐标
+const getMousePos = function (obj, e) {
+  var x = e.offsetX
+  var y = e.offsetY
+  return { x, y }
+}
+//创建坐标点
+const createPoint = function (pos) {
+  tempPoints.push(Object.assign({}, pos))
+  return num.value + 1
+}
+const refresh = async function () {
+  tempPoints.splice(0, tempPoints.length)
+  barAreaColor.value = '#000'
+  barAreaBorderColor.value = '#ddd'
+  bindingClick.value = true
+  fontPos.splice(0, fontPos.length)
+  checkPosArr.splice(0, checkPosArr.length)
+  num.value = 1
+  await getPictrue()
+  showRefresh.value = true
+}
+
+// 请求背景图片和验证图片
+const getPictrue = async () => {
+  let data = {
+    captchaType: captchaType.value
+  }
+  const res = await getCodeApi(data)
+  if (res.repCode == '0000') {
+    pointBackImgBase.value = res.repData.originalImageBase64
+    backToken.value = res.repData.token
+    secretKey.value = res.repData.secretKey
+    poinTextList.value = res.repData.wordList
+    text.value = t('captcha.point') + '【' + poinTextList.value.join(',') + '】'
+  } else {
+    text.value = res.repMsg
+  }
+}
+//坐标转换函数
+const pointTransfrom = function (pointArr, imgSize) {
+  var newPointArr = pointArr.map((p) => {
+    let x = Math.round((310 * p.x) / parseInt(imgSize.imgWidth))
+    let y = Math.round((155 * p.y) / parseInt(imgSize.imgHeight))
+    return { x, y }
+  })
+  return newPointArr
+}
 </script>

+ 271 - 310
yudao-ui-admin-vue3/src/components/Verifition/src/Verify/VerifySlide.vue

@@ -71,7 +71,7 @@
     </div>
   </div>
 </template>
-<script type="text/babel">
+<script type="text/babel" setup>
 /**
  * VerifySlide
  * @description 滑块
@@ -90,337 +90,298 @@ import {
   toRefs,
   getCurrentInstance
 } from 'vue'
-//  "captchaType":"blockPuzzle",
-export default {
-  name: 'VerifySlide',
-  props: {
-    captchaType: {
-      type: String
-    },
-    type: {
-      type: String,
-      default: '1'
-    },
-    //弹出式pop,固定fixed
-    mode: {
-      type: String,
-      default: 'fixed'
-    },
-    vSpace: {
-      type: Number,
-      default: 5
-    },
-    explain: {
-      type: String,
-      default: ''
-    },
-    imgSize: {
-      type: Object,
-      default() {
-        return {
-          width: '310px',
-          height: '155px'
-        }
-      }
-    },
-    blockSize: {
-      type: Object,
-      default() {
-        return {
-          width: '50px',
-          height: '50px'
-        }
+
+const props = defineProps({
+  captchaType: {
+    type: String
+  },
+  type: {
+    type: String,
+    default: '1'
+  },
+  //弹出式pop,固定fixed
+  mode: {
+    type: String,
+    default: 'fixed'
+  },
+  vSpace: {
+    type: Number,
+    default: 5
+  },
+  explain: {
+    type: String,
+    default: ''
+  },
+  imgSize: {
+    type: Object,
+    default() {
+      return {
+        width: '310px',
+        height: '155px'
       }
-    },
-    barSize: {
-      type: Object,
-      default() {
-        return {
-          width: '310px',
-          height: '30px'
-        }
+    }
+  },
+  blockSize: {
+    type: Object,
+    default() {
+      return {
+        width: '50px',
+        height: '50px'
       }
     }
   },
-  setup(props) {
-    const { t } = useI18n()
-    const { mode, captchaType, type, blockSize, explain } = toRefs(props)
-    const { proxy } = getCurrentInstance()
-    let secretKey = ref(''), //后端返回的ase加密秘钥
-      passFlag = ref(''), //是否通过的标识
-      backImgBase = ref(''), //验证码背景图片
-      blockBackImgBase = ref(''), //验证滑块的背景图片
-      backToken = ref(''), //后端返回的唯一token值
-      startMoveTime = ref(''), //移动开始的时间
-      endMovetime = ref(''), //移动结束的时间
-      tipsBackColor = ref(''), //提示词的背景颜色
-      tipWords = ref(''),
-      text = ref(''),
-      finishText = ref(''),
-      setSize = reactive({
-        imgHeight: 0,
-        imgWidth: 0,
-        barHeight: 0,
-        barWidth: 0
-      }),
-      top = ref(0),
-      left = ref(0),
-      moveBlockLeft = ref(undefined),
-      leftBarWidth = ref(undefined),
-      // 移动中样式
-      moveBlockBackgroundColor = ref(undefined),
-      leftBarBorderColor = ref('#ddd'),
-      iconColor = ref(undefined),
-      iconClass = ref('icon-right'),
-      status = ref(false), //鼠标状态
-      isEnd = ref(false), //是够验证完成
-      showRefresh = ref(true),
-      transitionLeft = ref(''),
-      transitionWidth = ref(''),
-      startLeft = ref(0)
-
-    const barArea = computed(() => {
-      return proxy.$el.querySelector('.verify-bar-area')
-    })
-    function init() {
-      if (explain.value === '') {
-        text.value = t('captcha.slide')
-      } else {
-        text.value = explain.value
+  barSize: {
+    type: Object,
+    default() {
+      return {
+        width: '310px',
+        height: '30px'
       }
-      getPictrue()
-      nextTick(() => {
-        let { imgHeight, imgWidth, barHeight, barWidth } = resetSize(proxy)
-        setSize.imgHeight = imgHeight
-        setSize.imgWidth = imgWidth
-        setSize.barHeight = barHeight
-        setSize.barWidth = barWidth
-        proxy.$parent.$emit('ready', proxy)
-      })
+    }
+  }
+})
 
-      window.removeEventListener('touchmove', function (e) {
-        move(e)
-      })
-      window.removeEventListener('mousemove', function (e) {
-        move(e)
-      })
+const { t } = useI18n()
+const { mode, captchaType, type, blockSize, explain } = toRefs(props)
+const { proxy } = getCurrentInstance()
+let secretKey = ref(''), //后端返回的ase加密秘钥
+  passFlag = ref(''), //是否通过的标识
+  backImgBase = ref(''), //验证码背景图片
+  blockBackImgBase = ref(''), //验证滑块的背景图片
+  backToken = ref(''), //后端返回的唯一token值
+  startMoveTime = ref(''), //移动开始的时间
+  endMovetime = ref(''), //移动结束的时间
+  tipWords = ref(''),
+  text = ref(''),
+  finishText = ref(''),
+  setSize = reactive({
+    imgHeight: 0,
+    imgWidth: 0,
+    barHeight: 0,
+    barWidth: 0
+  }),
+  moveBlockLeft = ref(undefined),
+  leftBarWidth = ref(undefined),
+  // 移动中样式
+  moveBlockBackgroundColor = ref(undefined),
+  leftBarBorderColor = ref('#ddd'),
+  iconColor = ref(undefined),
+  iconClass = ref('icon-right'),
+  status = ref(false), //鼠标状态
+  isEnd = ref(false), //是够验证完成
+  showRefresh = ref(true),
+  transitionLeft = ref(''),
+  transitionWidth = ref(''),
+  startLeft = ref(0)
 
-      //鼠标松开
-      window.removeEventListener('touchend', function () {
-        end()
-      })
-      window.removeEventListener('mouseup', function () {
-        end()
-      })
+const barArea = computed(() => {
+  return proxy.$el.querySelector('.verify-bar-area')
+})
+const init = () => {
+  if (explain.value === '') {
+    text.value = t('captcha.slide')
+  } else {
+    text.value = explain.value
+  }
+  getPictrue()
+  nextTick(() => {
+    let { imgHeight, imgWidth, barHeight, barWidth } = resetSize(proxy)
+    setSize.imgHeight = imgHeight
+    setSize.imgWidth = imgWidth
+    setSize.barHeight = barHeight
+    setSize.barWidth = barWidth
+    proxy.$parent.$emit('ready', proxy)
+  })
 
-      window.addEventListener('touchmove', function (e) {
-        move(e)
-      })
-      window.addEventListener('mousemove', function (e) {
-        move(e)
-      })
+  window.removeEventListener('touchmove', function (e) {
+    move(e)
+  })
+  window.removeEventListener('mousemove', function (e) {
+    move(e)
+  })
 
-      //鼠标松开
-      window.addEventListener('touchend', function () {
-        end()
-      })
-      window.addEventListener('mouseup', function () {
-        end()
-      })
+  //鼠标松开
+  window.removeEventListener('touchend', function () {
+    end()
+  })
+  window.removeEventListener('mouseup', function () {
+    end()
+  })
+
+  window.addEventListener('touchmove', function (e) {
+    move(e)
+  })
+  window.addEventListener('mousemove', function (e) {
+    move(e)
+  })
+
+  //鼠标松开
+  window.addEventListener('touchend', function () {
+    end()
+  })
+  window.addEventListener('mouseup', function () {
+    end()
+  })
+}
+watch(type, () => {
+  init()
+})
+onMounted(() => {
+  // 禁止拖拽
+  init()
+  proxy.$el.onselectstart = function () {
+    return false
+  }
+})
+//鼠标按下
+const start = (e) => {
+  e = e || window.event
+  if (!e.touches) {
+    //兼容PC端
+    var x = e.clientX
+  } else {
+    //兼容移动端
+    var x = e.touches[0].pageX
+  }
+  startLeft.value = Math.floor(x - barArea.value.getBoundingClientRect().left)
+  startMoveTime.value = +new Date() //开始滑动的时间
+  if (isEnd.value == false) {
+    text.value = ''
+    moveBlockBackgroundColor.value = '#337ab7'
+    leftBarBorderColor.value = '#337AB7'
+    iconColor.value = '#fff'
+    e.stopPropagation()
+    status.value = true
+  }
+}
+//鼠标移动
+const move = (e) => {
+  e = e || window.event
+  if (status.value && isEnd.value == false) {
+    if (!e.touches) {
+      //兼容PC端
+      var x = e.clientX
+    } else {
+      //兼容移动端
+      var x = e.touches[0].pageX
     }
-    watch(type, () => {
-      init()
-    })
-    onMounted(() => {
-      // 禁止拖拽
-      init()
-      proxy.$el.onselectstart = function () {
-        return false
-      }
-    })
-    //鼠标按下
-    function start(e) {
-      e = e || window.event
-      if (!e.touches) {
-        //兼容PC端
-        var x = e.clientX
-      } else {
-        //兼容移动端
-        var x = e.touches[0].pageX
-      }
-      startLeft.value = Math.floor(x - barArea.value.getBoundingClientRect().left)
-      startMoveTime.value = +new Date() //开始滑动的时间
-      if (isEnd.value == false) {
-        text.value = ''
-        moveBlockBackgroundColor.value = '#337ab7'
-        leftBarBorderColor.value = '#337AB7'
-        iconColor.value = '#fff'
-        e.stopPropagation()
-        status.value = true
-      }
+    var bar_area_left = barArea.value.getBoundingClientRect().left
+    var move_block_left = x - bar_area_left //小方块相对于父元素的left值
+    if (
+      move_block_left >=
+      barArea.value.offsetWidth - parseInt(parseInt(blockSize.value.width) / 2) - 2
+    ) {
+      move_block_left =
+        barArea.value.offsetWidth - parseInt(parseInt(blockSize.value.width) / 2) - 2
     }
-    //鼠标移动
-    function move(e) {
-      e = e || window.event
-      if (status.value && isEnd.value == false) {
-        if (!e.touches) {
-          //兼容PC端
-          var x = e.clientX
-        } else {
-          //兼容移动端
-          var x = e.touches[0].pageX
-        }
-        var bar_area_left = barArea.value.getBoundingClientRect().left
-        var move_block_left = x - bar_area_left //小方块相对于父元素的left值
-        if (
-          move_block_left >=
-          barArea.value.offsetWidth - parseInt(parseInt(blockSize.value.width) / 2) - 2
-        ) {
-          move_block_left =
-            barArea.value.offsetWidth - parseInt(parseInt(blockSize.value.width) / 2) - 2
-        }
-        if (move_block_left <= 0) {
-          move_block_left = parseInt(parseInt(blockSize.value.width) / 2)
-        }
-        //拖动后小方块的left值
-        moveBlockLeft.value = move_block_left - startLeft.value + 'px'
-        leftBarWidth.value = move_block_left - startLeft.value + 'px'
-      }
+    if (move_block_left <= 0) {
+      move_block_left = parseInt(parseInt(blockSize.value.width) / 2)
     }
+    //拖动后小方块的left值
+    moveBlockLeft.value = move_block_left - startLeft.value + 'px'
+    leftBarWidth.value = move_block_left - startLeft.value + 'px'
+  }
+}
 
-    //鼠标松开
-    function end() {
-      endMovetime.value = +new Date()
-      //判断是否重合
-      if (status.value && isEnd.value == false) {
-        var moveLeftDistance = parseInt((moveBlockLeft.value || '').replace('px', ''))
-        moveLeftDistance = (moveLeftDistance * 310) / parseInt(setSize.imgWidth)
-        let data = {
-          captchaType: captchaType.value,
-          pointJson: secretKey.value
-            ? aesEncrypt(JSON.stringify({ x: moveLeftDistance, y: 5.0 }), secretKey.value)
-            : JSON.stringify({ x: moveLeftDistance, y: 5.0 }),
-          token: backToken.value
+//鼠标松开
+const end = () => {
+  endMovetime.value = +new Date()
+  //判断是否重合
+  if (status.value && isEnd.value == false) {
+    var moveLeftDistance = parseInt((moveBlockLeft.value || '').replace('px', ''))
+    moveLeftDistance = (moveLeftDistance * 310) / parseInt(setSize.imgWidth)
+    let data = {
+      captchaType: captchaType.value,
+      pointJson: secretKey.value
+        ? aesEncrypt(JSON.stringify({ x: moveLeftDistance, y: 5.0 }), secretKey.value)
+        : JSON.stringify({ x: moveLeftDistance, y: 5.0 }),
+      token: backToken.value
+    }
+    reqCheckApi(data).then((res) => {
+      if (res.repCode == '0000') {
+        moveBlockBackgroundColor.value = '#5cb85c'
+        leftBarBorderColor.value = '#5cb85c'
+        iconColor.value = '#fff'
+        iconClass.value = 'icon-check'
+        showRefresh.value = false
+        isEnd.value = true
+        if (mode.value == 'pop') {
+          setTimeout(() => {
+            proxy.$parent.clickShow = false
+            refresh()
+          }, 1500)
         }
-        reqCheckApi(data).then((res) => {
-          if (res.repCode == '0000') {
-            moveBlockBackgroundColor.value = '#5cb85c'
-            leftBarBorderColor.value = '#5cb85c'
-            iconColor.value = '#fff'
-            iconClass.value = 'icon-check'
-            showRefresh.value = false
-            isEnd.value = true
-            if (mode.value == 'pop') {
-              setTimeout(() => {
-                proxy.$parent.clickShow = false
-                refresh()
-              }, 1500)
-            }
-            passFlag.value = true
-            tipWords.value = `${((endMovetime.value - startMoveTime.value) / 1000).toFixed(2)}s 
+        passFlag.value = true
+        tipWords.value = `${((endMovetime.value - startMoveTime.value) / 1000).toFixed(2)}s 
             ${t('captcha.success')}`
-            var captchaVerification = secretKey.value
-              ? aesEncrypt(
-                  backToken.value + '---' + JSON.stringify({ x: moveLeftDistance, y: 5.0 }),
-                  secretKey.value
-                )
-              : backToken.value + '---' + JSON.stringify({ x: moveLeftDistance, y: 5.0 })
-            setTimeout(() => {
-              tipWords.value = ''
-              proxy.$parent.closeBox()
-              proxy.$parent.$emit('success', { captchaVerification })
-            }, 1000)
-          } else {
-            moveBlockBackgroundColor.value = '#d9534f'
-            leftBarBorderColor.value = '#d9534f'
-            iconColor.value = '#fff'
-            iconClass.value = 'icon-close'
-            passFlag.value = false
-            setTimeout(function () {
-              refresh()
-            }, 1000)
-            proxy.$parent.$emit('error', proxy)
-            tipWords.value = t('captcha.fail')
-            setTimeout(() => {
-              tipWords.value = ''
-            }, 1000)
-          }
-        })
-        status.value = false
+        var captchaVerification = secretKey.value
+          ? aesEncrypt(
+              backToken.value + '---' + JSON.stringify({ x: moveLeftDistance, y: 5.0 }),
+              secretKey.value
+            )
+          : backToken.value + '---' + JSON.stringify({ x: moveLeftDistance, y: 5.0 })
+        setTimeout(() => {
+          tipWords.value = ''
+          proxy.$parent.closeBox()
+          proxy.$parent.$emit('success', { captchaVerification })
+        }, 1000)
+      } else {
+        moveBlockBackgroundColor.value = '#d9534f'
+        leftBarBorderColor.value = '#d9534f'
+        iconColor.value = '#fff'
+        iconClass.value = 'icon-close'
+        passFlag.value = false
+        setTimeout(function () {
+          refresh()
+        }, 1000)
+        proxy.$parent.$emit('error', proxy)
+        tipWords.value = t('captcha.fail')
+        setTimeout(() => {
+          tipWords.value = ''
+        }, 1000)
       }
-    }
+    })
+    status.value = false
+  }
+}
 
-    const refresh = () => {
-      showRefresh.value = true
-      finishText.value = ''
+const refresh = async () => {
+  showRefresh.value = true
+  finishText.value = ''
 
-      transitionLeft.value = 'left .3s'
-      moveBlockLeft.value = 0
+  transitionLeft.value = 'left .3s'
+  moveBlockLeft.value = 0
 
-      leftBarWidth.value = undefined
-      transitionWidth.value = 'width .3s'
+  leftBarWidth.value = undefined
+  transitionWidth.value = 'width .3s'
 
-      leftBarBorderColor.value = '#ddd'
-      moveBlockBackgroundColor.value = '#fff'
-      iconColor.value = '#000'
-      iconClass.value = 'icon-right'
-      isEnd.value = false
+  leftBarBorderColor.value = '#ddd'
+  moveBlockBackgroundColor.value = '#fff'
+  iconColor.value = '#000'
+  iconClass.value = 'icon-right'
+  isEnd.value = false
 
-      getPictrue()
-      setTimeout(() => {
-        transitionWidth.value = ''
-        transitionLeft.value = ''
-        text.value = explain.value
-      }, 300)
-    }
+  await getPictrue()
+  setTimeout(() => {
+    transitionWidth.value = ''
+    transitionLeft.value = ''
+    text.value = explain.value
+  }, 300)
+}
 
-    // 请求背景图片和验证图片
-    function getPictrue() {
-      let data = {
-        captchaType: captchaType.value
-      }
-      getCodeApi(data).then((res) => {
-        if (res.repCode == '0000') {
-          backImgBase.value = res.repData.originalImageBase64
-          blockBackImgBase.value = res.repData.jigsawImageBase64
-          backToken.value = res.repData.token
-          secretKey.value = res.repData.secretKey
-        } else {
-          tipWords.value = res.repMsg
-        }
-      })
-    }
-    return {
-      secretKey, //后端返回的ase加密秘钥
-      passFlag, //是否通过的标识
-      backImgBase, //验证码背景图片
-      blockBackImgBase, //验证滑块的背景图片
-      backToken, //后端返回的唯一token值
-      startMoveTime, //移动开始的时间
-      endMovetime, //移动结束的时间
-      tipsBackColor, //提示词的背景颜色
-      tipWords,
-      text,
-      finishText,
-      setSize,
-      top,
-      left,
-      moveBlockLeft,
-      leftBarWidth,
-      // 移动中样式
-      moveBlockBackgroundColor,
-      leftBarBorderColor,
-      iconColor,
-      iconClass,
-      status, //鼠标状态
-      isEnd, //是够验证完成
-      showRefresh,
-      transitionLeft,
-      transitionWidth,
-      barArea,
-      refresh,
-      start
-    }
+// 请求背景图片和验证图片
+const getPictrue = async () => {
+  let data = {
+    captchaType: captchaType.value
+  }
+  const res = await getCodeApi(data)
+  if (res.repCode == '0000') {
+    backImgBase.value = res.repData.originalImageBase64
+    blockBackImgBase.value = res.repData.jigsawImageBase64
+    backToken.value = res.repData.token
+    secretKey.value = res.repData.secretKey
+  } else {
+    tipWords.value = res.repMsg
   }
 }
 </script>

+ 2 - 2
yudao-ui-admin-vue3/src/components/XModal/src/XModal.vue

@@ -10,8 +10,8 @@ const props = defineProps({
   fullscreen: propTypes.bool.def(false),
   loading: propTypes.bool.def(false),
   title: propTypes.string.def('弹窗'),
-  width: propTypes.string.def('800'),
-  height: propTypes.string.def('480'),
+  width: propTypes.string.def('40%'),
+  height: propTypes.string.def('60%'),
   minWidth: propTypes.string.def('460'),
   minHeight: propTypes.string.def('320'),
   showFooter: propTypes.bool.def(true)

+ 0 - 2
yudao-ui-admin-vue3/src/components/index.ts

@@ -3,7 +3,6 @@ import { Icon } from './Icon'
 import { Form } from '@/components/Form'
 import { Table } from '@/components/Table'
 import { Search } from '@/components/Search'
-import { Dialog } from '@/components/Dialog'
 import { XModal } from '@/components/XModal'
 import { XButton, XTextButton } from '@/components/XButton'
 import { DictTag } from '@/components/DictTag'
@@ -15,7 +14,6 @@ export const setupGlobCom = (app: App<Element>): void => {
   app.component('Form', Form)
   app.component('Table', Table)
   app.component('Search', Search)
-  app.component('Dialog', Dialog)
   app.component('XModal', XModal)
   app.component('XButton', XButton)
   app.component('XTextButton', XTextButton)

+ 7 - 3
yudao-ui-admin-vue3/src/config/axios/service.ts

@@ -168,15 +168,19 @@ service.interceptors.response.use(
       ElMessage.error(t('sys.api.errMsg500'))
       return Promise.reject(new Error(msg))
     } else if (code === 901) {
-      ElMessage.error(
-        '<div>' +
+      ElMessage.error({
+        duration: 5,
+        offset: 300,
+        dangerouslyUseHTMLString: true,
+        message:
+          '<div>' +
           t('sys.api.errMsg901') +
           '</div>' +
           '<div> &nbsp; </div>' +
           '<div>参考 https://doc.iocoder.cn/ 教程</div>' +
           '<div> &nbsp; </div>' +
           '<div>5 分钟搭建本地环境</div>'
-      )
+      })
       return Promise.reject(new Error(msg))
     } else if (code !== 200) {
       if (msg === '无效的刷新令牌') {

+ 2 - 2
yudao-ui-admin-vue3/src/directives/permission/hasPermi.ts

@@ -1,5 +1,5 @@
 import type { App } from 'vue'
-import { useCache } from '@/hooks/web/useCache'
+import { CACHE_KEY, useCache } from '@/hooks/web/useCache'
 import { useI18n } from '@/hooks/web/useI18n'
 const { t } = useI18n() // 国际化
 
@@ -8,7 +8,7 @@ export function hasPermi(app: App<Element>) {
     const { wsCache } = useCache()
     const { value } = binding
     const all_permission = '*:*:*'
-    const permissions = wsCache.get('user').permissions
+    const permissions = wsCache.get(CACHE_KEY.USER).permissions
 
     if (value && value instanceof Array && value.length > 0) {
       const permissionFlag = value

+ 2 - 2
yudao-ui-admin-vue3/src/directives/permission/hasRole.ts

@@ -1,5 +1,5 @@
 import type { App } from 'vue'
-import { useCache } from '@/hooks/web/useCache'
+import { CACHE_KEY, useCache } from '@/hooks/web/useCache'
 import { useI18n } from '@/hooks/web/useI18n'
 const { t } = useI18n() // 国际化
 
@@ -8,7 +8,7 @@ export function hasRole(app: App<Element>) {
     const { wsCache } = useCache()
     const { value } = binding
     const super_admin = 'admin'
-    const roles = wsCache.get('user').roles
+    const roles = wsCache.get(CACHE_KEY.USER).roles
 
     if (value && value instanceof Array && value.length > 0) {
       const roleFlag = value

+ 10 - 0
yudao-ui-admin-vue3/src/hooks/web/useCache.ts

@@ -6,6 +6,16 @@ import WebStorageCache from 'web-storage-cache'
 
 type CacheType = 'localStorage' | 'sessionStorage'
 
+export const CACHE_KEY = {
+  IS_DARK: 'isDark',
+  USER: 'user',
+  LANG: 'lang',
+  THEME: 'theme',
+  LAYOUT: 'layout',
+  ROLE_ROUTERS: 'roleRouters',
+  DICT_CACHE: 'dictCache'
+}
+
 export const useCache = (type: CacheType = 'localStorage') => {
   const wsCache: WebStorageCache = new WebStorageCache({
     storage: type

+ 3 - 1
yudao-ui-admin-vue3/src/hooks/web/useVxeCrudSchemas.ts

@@ -208,7 +208,8 @@ const filterTableSchema = (crudSchema: VxeCrudSchema): VxeGridPropTypes.Columns
       const tableSchemaItem = {
         ...schemaItem.table,
         field: schemaItem.field,
-        title: schemaItem.table?.title || schemaItem.title
+        title: schemaItem.table?.title || schemaItem.title,
+        minWidth: '80px'
       }
       tableSchemaItem.showOverflow = 'tooltip'
       if (schemaItem?.formatter) {
@@ -231,6 +232,7 @@ const filterTableSchema = (crudSchema: VxeCrudSchema): VxeGridPropTypes.Columns
     const tableSchemaItem = {
       title: crudSchema.actionTitle ? crudSchema.actionTitle : t('table.action'),
       field: 'actionbtns',
+      fixed: 'right' as unknown as VxeColumnPropTypes.Fixed,
       width: crudSchema.actionWidth ? crudSchema.actionWidth : '200px',
       slots: {
         default: 'actionbtns_default'

+ 6 - 4
yudao-ui-admin-vue3/src/hooks/web/useVxeGrid.ts

@@ -11,10 +11,12 @@ const message = useMessage() // 消息弹窗
 
 interface UseVxeGridConfig<T = any> {
   allSchemas: VxeAllSchemas
+  height?: number // 高度 默认730
   topActionSlots?: boolean // 是否开启表格内顶部操作栏插槽
   treeConfig?: VxeTablePropTypes.TreeConfig // 树形表单配置
   isList?: boolean // 是否不带分页的list
   getListApi: (option: any) => Promise<T> // 获取列表接口
+  getAllListApi?: (option: any) => Promise<T> // 获取全部数据接口 用于VXE导出
   deleteApi?: (option: any) => Promise<T> // 删除接口
   exportListApi?: (option: any) => Promise<T> // 导出接口
   exportName?: string // 导出文件夹名称
@@ -47,7 +49,7 @@ export const useVxeGrid = <T = any>(config?: UseVxeGridConfig<T>) => {
   const gridOptions = reactive<VxeGridProps<any>>({
     loading: true,
     size: currentSize as any,
-    height: 730, // 1080高度
+    height: config?.height ? config.height : 730,
     rowConfig: {
       isCurrent: true, // 当鼠标点击行时,是否要高亮当前行
       isHover: true // 当鼠标移到行时,是否要高亮当前行
@@ -99,8 +101,8 @@ export const useVxeGrid = <T = any>(config?: UseVxeGridConfig<T>) => {
         queryAll: ({ form }) => {
           const queryParams = Object.assign({}, JSON.parse(JSON.stringify(form)))
           return new Promise(async (resolve) => {
-            if (config?.exportListApi) {
-              resolve(await config?.exportListApi(queryParams))
+            if (config?.getAllListApi) {
+              resolve(await config?.getAllListApi(queryParams))
             } else {
               resolve(await config?.getListApi(queryParams))
             }
@@ -113,7 +115,7 @@ export const useVxeGrid = <T = any>(config?: UseVxeGridConfig<T>) => {
       // 默认选中类型
       type: 'csv',
       // 自定义数据量列表
-      modes: ['current', 'all'],
+      modes: config?.getAllListApi ? ['current', 'all'] : ['current'],
       columns: config?.allSchemas.printSchema
     }
   })

+ 1 - 1
yudao-ui-admin-vue3/src/layout/Layout.vue

@@ -2,7 +2,7 @@
 import { computed, defineComponent, unref } from 'vue'
 import { useAppStore } from '@/store/modules/app'
 import { Backtop } from '@/components/Backtop'
-import { Setting } from '@/components/Setting'
+import { Setting } from '@/layout/components/Setting'
 import { useRenderLayout } from './components/useRenderLayout'
 import { useDesign } from '@/hooks/web/useDesign'
 

+ 1 - 1
yudao-ui-admin-vue3/src/layout/components/AppView.vue

@@ -1,7 +1,7 @@
 <script setup lang="ts">
 import { useTagsViewStore } from '@/store/modules/tagsView'
 import { useAppStore } from '@/store/modules/app'
-import { Footer } from '@/components/Footer'
+import { Footer } from '@/layout/components/Footer'
 import { computed } from 'vue'
 
 const appStore = useAppStore()

+ 0 - 0
yudao-ui-admin-vue3/src/components/Breadcrumb/index.ts → yudao-ui-admin-vue3/src/layout/components/Breadcrumb/index.ts


+ 0 - 0
yudao-ui-admin-vue3/src/components/Breadcrumb/src/Breadcrumb.vue → yudao-ui-admin-vue3/src/layout/components/Breadcrumb/src/Breadcrumb.vue


+ 0 - 0
yudao-ui-admin-vue3/src/components/Breadcrumb/src/helper.ts → yudao-ui-admin-vue3/src/layout/components/Breadcrumb/src/helper.ts


+ 0 - 0
yudao-ui-admin-vue3/src/components/Collapse/index.ts → yudao-ui-admin-vue3/src/layout/components/Collapse/index.ts


+ 0 - 0
yudao-ui-admin-vue3/src/components/Collapse/src/Collapse.vue → yudao-ui-admin-vue3/src/layout/components/Collapse/src/Collapse.vue


+ 0 - 0
yudao-ui-admin-vue3/src/components/ContextMenu/index.ts → yudao-ui-admin-vue3/src/layout/components/ContextMenu/index.ts


+ 0 - 0
yudao-ui-admin-vue3/src/components/ContextMenu/src/ContextMenu.vue → yudao-ui-admin-vue3/src/layout/components/ContextMenu/src/ContextMenu.vue


+ 0 - 0
yudao-ui-admin-vue3/src/components/Footer/index.ts → yudao-ui-admin-vue3/src/layout/components/Footer/index.ts


+ 0 - 0
yudao-ui-admin-vue3/src/components/Footer/src/Footer.vue → yudao-ui-admin-vue3/src/layout/components/Footer/src/Footer.vue


+ 0 - 0
yudao-ui-admin-vue3/src/components/LocaleDropdown/index.ts → yudao-ui-admin-vue3/src/layout/components/LocaleDropdown/index.ts


+ 0 - 0
yudao-ui-admin-vue3/src/components/LocaleDropdown/src/LocaleDropdown.vue → yudao-ui-admin-vue3/src/layout/components/LocaleDropdown/src/LocaleDropdown.vue


+ 0 - 0
yudao-ui-admin-vue3/src/components/Logo/index.ts → yudao-ui-admin-vue3/src/layout/components/Logo/index.ts


+ 0 - 0
yudao-ui-admin-vue3/src/components/Logo/src/Logo.vue → yudao-ui-admin-vue3/src/layout/components/Logo/src/Logo.vue


+ 0 - 0
yudao-ui-admin-vue3/src/components/Menu/index.ts → yudao-ui-admin-vue3/src/layout/components/Menu/index.ts


+ 0 - 0
yudao-ui-admin-vue3/src/components/Menu/src/Menu.vue → yudao-ui-admin-vue3/src/layout/components/Menu/src/Menu.vue


+ 0 - 0
yudao-ui-admin-vue3/src/components/Menu/src/components/useRenderMenuItem.tsx → yudao-ui-admin-vue3/src/layout/components/Menu/src/components/useRenderMenuItem.tsx


+ 0 - 0
yudao-ui-admin-vue3/src/components/Menu/src/components/useRenderMenuTitle.tsx → yudao-ui-admin-vue3/src/layout/components/Menu/src/components/useRenderMenuTitle.tsx


+ 0 - 0
yudao-ui-admin-vue3/src/components/Menu/src/helper.ts → yudao-ui-admin-vue3/src/layout/components/Menu/src/helper.ts


+ 0 - 0
yudao-ui-admin-vue3/src/components/Screenfull/index.ts → yudao-ui-admin-vue3/src/layout/components/Screenfull/index.ts


+ 0 - 0
yudao-ui-admin-vue3/src/components/Screenfull/src/Screenfull.vue → yudao-ui-admin-vue3/src/layout/components/Screenfull/src/Screenfull.vue


+ 0 - 0
yudao-ui-admin-vue3/src/components/Setting/index.ts → yudao-ui-admin-vue3/src/layout/components/Setting/index.ts


+ 5 - 5
yudao-ui-admin-vue3/src/components/Setting/src/Setting.vue → yudao-ui-admin-vue3/src/layout/components/Setting/src/Setting.vue

@@ -3,13 +3,13 @@ import { ElDrawer, ElDivider, ElMessage } from 'element-plus'
 import { ref, unref, computed, watch } from 'vue'
 import { useCssVar, useClipboard } from '@vueuse/core'
 import { useI18n } from '@/hooks/web/useI18n'
-import { useCache } from '@/hooks/web/useCache'
+import { CACHE_KEY, useCache } from '@/hooks/web/useCache'
 import { useDesign } from '@/hooks/web/useDesign'
 
 import { trim, setCssVar } from '@/utils'
 import { colorIsDark, lighten, hexToRGB } from '@/utils/color'
 import { useAppStore } from '@/store/modules/app'
-import { ThemeSwitch } from '@/components/ThemeSwitch'
+import { ThemeSwitch } from '@/layout/components/ThemeSwitch'
 import ColorRadioPicker from './components/ColorRadioPicker.vue'
 import InterfaceDisplay from './components/InterfaceDisplay.vue'
 import LayoutRadioPicker from './components/LayoutRadioPicker.vue'
@@ -188,9 +188,9 @@ const copyConfig = async () => {
 // 清空缓存
 const clear = () => {
   const { wsCache } = useCache()
-  wsCache.delete('layout')
-  wsCache.delete('theme')
-  wsCache.delete('isDark')
+  wsCache.delete(CACHE_KEY.LAYOUT)
+  wsCache.delete(CACHE_KEY.THEME)
+  wsCache.delete(CACHE_KEY.IS_DARK)
   window.location.reload()
 }
 </script>

+ 0 - 0
yudao-ui-admin-vue3/src/components/Setting/src/components/ColorRadioPicker.vue → yudao-ui-admin-vue3/src/layout/components/Setting/src/components/ColorRadioPicker.vue


+ 0 - 0
yudao-ui-admin-vue3/src/components/Setting/src/components/InterfaceDisplay.vue → yudao-ui-admin-vue3/src/layout/components/Setting/src/components/InterfaceDisplay.vue


+ 0 - 0
yudao-ui-admin-vue3/src/components/Setting/src/components/LayoutRadioPicker.vue → yudao-ui-admin-vue3/src/layout/components/Setting/src/components/LayoutRadioPicker.vue


+ 0 - 0
yudao-ui-admin-vue3/src/components/SizeDropdown/index.ts → yudao-ui-admin-vue3/src/layout/components/SizeDropdown/index.ts


+ 0 - 0
yudao-ui-admin-vue3/src/components/SizeDropdown/src/SizeDropdown.vue → yudao-ui-admin-vue3/src/layout/components/SizeDropdown/src/SizeDropdown.vue


+ 0 - 0
yudao-ui-admin-vue3/src/components/TabMenu/index.ts → yudao-ui-admin-vue3/src/layout/components/TabMenu/index.ts


+ 1 - 1
yudao-ui-admin-vue3/src/components/TabMenu/src/TabMenu.vue → yudao-ui-admin-vue3/src/layout/components/TabMenu/src/TabMenu.vue

@@ -5,7 +5,7 @@ import { computed, unref, defineComponent, watch, ref, onMounted } from 'vue'
 import { useI18n } from '@/hooks/web/useI18n'
 import { ElScrollbar } from 'element-plus'
 import { Icon } from '@/components/Icon'
-import { Menu } from '@/components/Menu'
+import { Menu } from '@/layout/components/Menu'
 import { useRouter } from 'vue-router'
 import { pathResolve } from '@/utils/routerHelper'
 import { cloneDeep } from 'lodash-es'

+ 1 - 1
yudao-ui-admin-vue3/src/components/TabMenu/src/helper.ts → yudao-ui-admin-vue3/src/layout/components/TabMenu/src/helper.ts

@@ -1,4 +1,4 @@
-import { getAllParentPath } from '@/components/Menu/src/helper'
+import { getAllParentPath } from '@/layout/components/Menu/src/helper'
 import type { RouteMeta } from 'vue-router'
 import { isUrl } from '@/utils/is'
 import { cloneDeep } from 'lodash-es'

+ 0 - 0
yudao-ui-admin-vue3/src/components/TagsView/index.ts → yudao-ui-admin-vue3/src/layout/components/TagsView/index.ts


+ 3 - 4
yudao-ui-admin-vue3/src/components/TagsView/src/TagsView.vue → yudao-ui-admin-vue3/src/layout/components/TagsView/src/TagsView.vue

@@ -7,7 +7,7 @@ import { useTagsViewStore } from '@/store/modules/tagsView'
 import { useAppStore } from '@/store/modules/app'
 import { useI18n } from '@/hooks/web/useI18n'
 import { filterAffixTags } from './helper'
-import { ContextMenu, ContextMenuExpose } from '@/components/ContextMenu'
+import { ContextMenu, ContextMenuExpose } from '@/layout/components/ContextMenu'
 import { useDesign } from '@/hooks/web/useDesign'
 import { useTemplateRefsList } from '@vueuse/core'
 import { ElScrollbar } from 'element-plus'
@@ -114,8 +114,8 @@ const toLastView = () => {
       addTags()
       return
     }
-    // You can set another route
-    push(permissionStore.getAddRouters[0].path)
+    // TODO: You can set another route
+    push('/')
   }
 }
 
@@ -128,7 +128,6 @@ const moveToCurrentTag = async () => {
       if (v.fullPath !== unref(currentRoute).fullPath) {
         tagsViewStore.updateVisitedView(unref(currentRoute))
       }
-
       break
     }
   }

+ 0 - 0
yudao-ui-admin-vue3/src/components/TagsView/src/helper.ts → yudao-ui-admin-vue3/src/layout/components/TagsView/src/helper.ts


+ 0 - 0
yudao-ui-admin-vue3/src/components/ThemeSwitch/index.ts → yudao-ui-admin-vue3/src/layout/components/ThemeSwitch/index.ts


+ 0 - 0
yudao-ui-admin-vue3/src/components/ThemeSwitch/src/ThemeSwitch.vue → yudao-ui-admin-vue3/src/layout/components/ThemeSwitch/src/ThemeSwitch.vue


+ 6 - 6
yudao-ui-admin-vue3/src/layout/components/ToolHeader.vue

@@ -1,11 +1,11 @@
 <script lang="tsx">
 import { defineComponent, computed } from 'vue'
-import { Collapse } from '@/components/Collapse'
-import { LocaleDropdown } from '@/components/LocaleDropdown'
-import { SizeDropdown } from '@/components/SizeDropdown'
-import { UserInfo } from '@/components/UserInfo'
-import { Screenfull } from '@/components/Screenfull'
-import { Breadcrumb } from '@/components/Breadcrumb'
+import { Collapse } from '@/layout/components/Collapse'
+import { LocaleDropdown } from '@/layout/components/LocaleDropdown'
+import { SizeDropdown } from '@/layout/components/SizeDropdown'
+import { UserInfo } from '@/layout/components/UserInfo'
+import { Screenfull } from '@/layout/components/Screenfull'
+import { Breadcrumb } from '@/layout/components/Breadcrumb'
 import { useAppStore } from '@/store/modules/app'
 import { useDesign } from '@/hooks/web/useDesign'
 

+ 0 - 0
yudao-ui-admin-vue3/src/components/UserInfo/index.ts → yudao-ui-admin-vue3/src/layout/components/UserInfo/index.ts


+ 2 - 2
yudao-ui-admin-vue3/src/components/UserInfo/src/UserInfo.vue → yudao-ui-admin-vue3/src/layout/components/UserInfo/src/UserInfo.vue

@@ -1,7 +1,7 @@
 <script setup lang="ts">
 import { ElDropdown, ElDropdownMenu, ElDropdownItem, ElMessageBox } from 'element-plus'
 import { useI18n } from '@/hooks/web/useI18n'
-import { useCache } from '@/hooks/web/useCache'
+import { CACHE_KEY, useCache } from '@/hooks/web/useCache'
 import { useRouter } from 'vue-router'
 import { useDesign } from '@/hooks/web/useDesign'
 import avatarImg from '@/assets/imgs/avatar.gif'
@@ -22,7 +22,7 @@ const { getPrefixCls } = useDesign()
 
 const prefixCls = getPrefixCls('user-info')
 
-const user = wsCache.get('user')
+const user = wsCache.get(CACHE_KEY.USER)
 
 const avatar = user.user.avatar ? user.user.avatar : avatarImg
 

+ 4 - 4
yudao-ui-admin-vue3/src/layout/components/useRenderLayout.tsx

@@ -1,9 +1,9 @@
 import { computed } from 'vue'
 import { useAppStore } from '@/store/modules/app'
-import { Menu } from '@/components/Menu'
-import { TabMenu } from '@/components/TabMenu'
-import { TagsView } from '@/components/TagsView'
-import { Logo } from '@/components/Logo'
+import { Menu } from '@/layout/components/Menu'
+import { TabMenu } from '@/layout/components/TabMenu'
+import { TagsView } from '@/layout/components/TagsView'
+import { Logo } from '@/layout/components/Logo'
 import AppView from './AppView.vue'
 import ToolHeader from './ToolHeader.vue'
 import { ElScrollbar } from 'element-plus'

+ 8 - 8
yudao-ui-admin-vue3/src/router/index.ts

@@ -1,18 +1,18 @@
 import type { App } from 'vue'
-import { getAccessToken } from '@/utils/auth'
 import type { RouteRecordRaw } from 'vue-router'
+import { createRouter, createWebHashHistory } from 'vue-router'
 import remainingRouter from './modules/remaining'
+import { isRelogin } from '@/config/axios/service'
+import { getAccessToken } from '@/utils/auth'
 import { useTitle } from '@/hooks/web/useTitle'
 import { useNProgress } from '@/hooks/web/useNProgress'
+import { CACHE_KEY, useCache } from '@/hooks/web/useCache'
 import { usePageLoading } from '@/hooks/web/usePageLoading'
-import { createRouter, createWebHashHistory } from 'vue-router'
-import { usePermissionStoreWithOut } from '@/store/modules/permission'
 import { useDictStoreWithOut } from '@/store/modules/dict'
 import { useUserStoreWithOut } from '@/store/modules/user'
-import { listSimpleDictDataApi } from '@/api/system/dict/dict.data'
-import { isRelogin } from '@/config/axios/service'
+import { usePermissionStoreWithOut } from '@/store/modules/permission'
 import { getInfoApi } from '@/api/login'
-import { useCache } from '@/hooks/web/useCache'
+import { listSimpleDictDataApi } from '@/api/system/dict/dict.data'
 
 const { wsCache } = useCache('sessionStorage')
 
@@ -50,12 +50,12 @@ router.beforeEach(async (to, from, next) => {
       const dictStore = useDictStoreWithOut()
       const userStore = useUserStoreWithOut()
       const permissionStore = usePermissionStoreWithOut()
-      const dictMap = wsCache.get('dictCache')
+      const dictMap = wsCache.get(CACHE_KEY.DICT_CACHE)
       if (!dictMap) {
         const res = await listSimpleDictDataApi()
         dictStore.setDictMap(res)
       }
-      if (userStore.getRoles.length === 0) {
+      if (!userStore.getIsSetUser) {
         isRelogin.show = true
         const res = await getInfoApi()
         await userStore.setUserInfoAction(res)

+ 7 - 7
yudao-ui-admin-vue3/src/store/modules/app.ts

@@ -2,7 +2,7 @@ import { defineStore } from 'pinia'
 import { store } from '../index'
 import { setCssVar, humpToUnderline } from '@/utils'
 import { ElMessage } from 'element-plus'
-import { useCache } from '@/hooks/web/useCache'
+import { CACHE_KEY, useCache } from '@/hooks/web/useCache'
 import { ElementPlusSize } from '@/types/elementPlus'
 import { LayoutType } from '@/types/layout'
 import { ThemeTypes } from '@/types/theme'
@@ -61,10 +61,10 @@ export const useAppStore = defineStore('app', {
       greyMode: false, // 是否开始灰色模式,用于特殊悼念日
       fixedMenu: wsCache.get('fixedMenu') || false, // 是否固定菜单
 
-      layout: wsCache.get('layout') || 'classic', // layout布局
-      isDark: wsCache.get('isDark') || false, // 是否是暗黑模式
+      layout: wsCache.get(CACHE_KEY.LAYOUT) || 'classic', // layout布局
+      isDark: wsCache.get(CACHE_KEY.IS_DARK) || false, // 是否是暗黑模式
       currentSize: wsCache.get('default') || 'default', // 组件尺寸
-      theme: wsCache.get('theme') || {
+      theme: wsCache.get(CACHE_KEY.THEME) || {
         // 主题色
         elColorPrimary: '#409eff',
         // 左侧菜单边框颜色
@@ -223,7 +223,7 @@ export const useAppStore = defineStore('app', {
         return
       }
       this.layout = layout
-      wsCache.set('layout', this.layout)
+      wsCache.set(CACHE_KEY.LAYOUT, this.layout)
     },
     setTitle(title: string) {
       this.title = title
@@ -237,7 +237,7 @@ export const useAppStore = defineStore('app', {
         document.documentElement.classList.add('light')
         document.documentElement.classList.remove('dark')
       }
-      wsCache.set('isDark', this.isDark)
+      wsCache.set(CACHE_KEY.IS_DARK, this.isDark)
     },
     setCurrentSize(currentSize: ElementPlusSize) {
       this.currentSize = currentSize
@@ -248,7 +248,7 @@ export const useAppStore = defineStore('app', {
     },
     setTheme(theme: ThemeTypes) {
       this.theme = Object.assign(this.theme, theme)
-      wsCache.set('theme', this.theme)
+      wsCache.set(CACHE_KEY.THEME, this.theme)
     },
     setCssVarTheme() {
       for (const key in this.theme) {

+ 3 - 3
yudao-ui-admin-vue3/src/store/modules/dict.ts

@@ -1,7 +1,7 @@
 import { defineStore } from 'pinia'
 import { store } from '../index'
 import { DictDataVO } from '@/api/system/dict/types'
-import { useCache } from '@/hooks/web/useCache'
+import { CACHE_KEY, useCache } from '@/hooks/web/useCache'
 const { wsCache } = useCache('sessionStorage')
 
 export interface DictValueType {
@@ -24,7 +24,7 @@ export const useDictStore = defineStore('dict', {
   }),
   getters: {
     getDictMap(): Recordable {
-      const dictMap = wsCache.get('dictCache')
+      const dictMap = wsCache.get(CACHE_KEY.DICT_CACHE)
       return dictMap ? dictMap : this.dictMap
     },
     getHasDictData(): boolean {
@@ -54,7 +54,7 @@ export const useDictStore = defineStore('dict', {
         })
       })
       this.dictMap = dictDataMap
-      wsCache.set('dictCache', dictDataMap, { exp: 60 }) // 60 秒 过期
+      wsCache.set(CACHE_KEY.DICT_CACHE, dictDataMap, { exp: 60 }) // 60 秒 过期
     }
   }
 })

+ 4 - 4
yudao-ui-admin-vue3/src/store/modules/locale.ts

@@ -2,7 +2,7 @@ import { defineStore } from 'pinia'
 import { store } from '../index'
 import zhCn from 'element-plus/es/locale/lang/zh-cn'
 import en from 'element-plus/es/locale/lang/en'
-import { useCache } from '@/hooks/web/useCache'
+import { CACHE_KEY, useCache } from '@/hooks/web/useCache'
 import { LocaleDropdownType } from '@/types/localeDropdown'
 
 const { wsCache } = useCache()
@@ -20,8 +20,8 @@ export const useLocaleStore = defineStore('locales', {
   state: (): LocaleState => {
     return {
       currentLocale: {
-        lang: wsCache.get('lang') || 'zh-CN',
-        elLocale: elLocaleMap[wsCache.get('lang') || 'zh-CN']
+        lang: wsCache.get(CACHE_KEY.LANG) || 'zh-CN',
+        elLocale: elLocaleMap[wsCache.get(CACHE_KEY.LANG) || 'zh-CN']
       },
       // 多语言
       localeMap: [
@@ -49,7 +49,7 @@ export const useLocaleStore = defineStore('locales', {
       // this.locale = Object.assign(this.locale, localeMap)
       this.currentLocale.lang = localeMap?.lang
       this.currentLocale.elLocale = elLocaleMap[localeMap?.lang]
-      wsCache.set('lang', localeMap?.lang)
+      wsCache.set(CACHE_KEY.LANG, localeMap?.lang)
     }
   }
 })

+ 4 - 4
yudao-ui-admin-vue3/src/store/modules/permission.ts

@@ -4,7 +4,7 @@ import { cloneDeep } from 'lodash-es'
 import remainingRouter from '@/router/modules/remaining'
 import { generateRoute, flatMultiLevelRoutes } from '@/utils/routerHelper'
 import { getAsyncRoutesApi } from '@/api/login'
-import { useCache } from '@/hooks/web/useCache'
+import { CACHE_KEY, useCache } from '@/hooks/web/useCache'
 
 const { wsCache } = useCache()
 
@@ -35,11 +35,11 @@ export const usePermissionStore = defineStore('permission', {
     async generateRoutes(): Promise<unknown> {
       return new Promise<void>(async (resolve) => {
         let res: AppCustomRouteRecordRaw[]
-        if (wsCache.get('roleRouters')) {
-          res = wsCache.get('roleRouters') as AppCustomRouteRecordRaw[]
+        if (wsCache.get(CACHE_KEY.ROLE_ROUTERS)) {
+          res = wsCache.get(CACHE_KEY.ROLE_ROUTERS) as AppCustomRouteRecordRaw[]
         } else {
           res = await getAsyncRoutesApi()
-          wsCache.set('roleRouters', res)
+          wsCache.set(CACHE_KEY.ROLE_ROUTERS, res)
         }
         const routerMap: AppRouteRecordRaw[] = generateRoute(res as AppCustomRouteRecordRaw[])
         // 动态路由,404一定要放到最后面

+ 9 - 2
yudao-ui-admin-vue3/src/store/modules/user.ts

@@ -1,7 +1,7 @@
 import { store } from '../index'
 import { defineStore } from 'pinia'
 import { getAccessToken, removeToken } from '@/utils/auth'
-import { useCache } from '@/hooks/web/useCache'
+import { CACHE_KEY, useCache } from '@/hooks/web/useCache'
 
 const { wsCache } = useCache()
 
@@ -13,6 +13,7 @@ interface UserVO {
 interface UserInfoVO {
   permissions: string[]
   roles: string[]
+  isSetUser: boolean
   user: UserVO
 }
 
@@ -20,6 +21,7 @@ export const useUserStore = defineStore('admin-user', {
   state: (): UserInfoVO => ({
     permissions: [],
     roles: [],
+    isSetUser: false,
     user: {
       id: 0,
       avatar: '',
@@ -33,6 +35,9 @@ export const useUserStore = defineStore('admin-user', {
     getRoles(): string[] {
       return this.roles
     },
+    getIsSetUser(): boolean {
+      return this.isSetUser
+    },
     getUser(): UserVO {
       return this.user
     }
@@ -46,7 +51,8 @@ export const useUserStore = defineStore('admin-user', {
       this.permissions = userInfo.permissions
       this.roles = userInfo.roles
       this.user = userInfo.user
-      wsCache.set('user', userInfo)
+      this.isSetUser = true
+      wsCache.set(CACHE_KEY.USER, userInfo)
     },
     loginOut() {
       removeToken()
@@ -56,6 +62,7 @@ export const useUserStore = defineStore('admin-user', {
     resetState() {
       this.permissions = []
       this.roles = []
+      this.isSetUser = false
       this.user = {
         id: 0,
         avatar: '',

+ 2 - 0
yudao-ui-admin-vue3/src/types/components.d.ts

@@ -21,6 +21,8 @@ export type ComponentName =
   | 'TreeSelect'
   | 'InputPassword'
   | 'Editor'
+  | 'UploadImg'
+  | 'UploadFile'
 
 export type ColProps = {
   span?: number

+ 25 - 23
yudao-ui-admin-vue3/src/utils/auth.ts

@@ -1,31 +1,33 @@
-import Cookies from 'js-cookie'
+import { useCache } from '@/hooks/web/useCache'
 import { TokenType } from '@/api/login/types'
 import { decrypt, encrypt } from '@/utils/jsencrypt'
 
+const { wsCache } = useCache()
+
 const AccessTokenKey = 'ACCESS_TOKEN'
 const RefreshTokenKey = 'REFRESH_TOKEN'
 
 // 获取token
 export const getAccessToken = () => {
   // 此处与TokenKey相同,此写法解决初始化时Cookies中不存在TokenKey报错
-  return Cookies.get(AccessTokenKey) ? Cookies.get(AccessTokenKey) : Cookies.get('ACCESS_TOKEN')
+  return wsCache.get(AccessTokenKey) ? wsCache.get(AccessTokenKey) : wsCache.get('ACCESS_TOKEN')
 }
 
 // 刷新token
 export const getRefreshToken = () => {
-  return Cookies.get(RefreshTokenKey)
+  return wsCache.get(RefreshTokenKey)
 }
 
 // 设置token
 export const setToken = (token: TokenType) => {
-  Cookies.set(RefreshTokenKey, token.refreshToken, token.expiresTime)
-  Cookies.set(AccessTokenKey, token.accessToken)
+  wsCache.set(RefreshTokenKey, token.refreshToken, { exp: token.expiresTime })
+  wsCache.set(AccessTokenKey, token.accessToken)
 }
 
 // 删除token
 export const removeToken = () => {
-  Cookies.remove(AccessTokenKey)
-  Cookies.remove(RefreshTokenKey)
+  wsCache.delete(AccessTokenKey)
+  wsCache.delete(RefreshTokenKey)
 }
 
 /** 格式化token(jwt格式) */
@@ -39,40 +41,40 @@ const PasswordKey = 'PASSWORD'
 const RememberMeKey = 'REMEMBER_ME'
 
 export const getUsername = () => {
-  return Cookies.get(UsernameKey)
+  return wsCache.get(UsernameKey)
 }
 
 export const setUsername = (username: string) => {
-  Cookies.set(UsernameKey, username)
+  wsCache.set(UsernameKey, username, { exp: 30 * 24 * 60 * 60 })
 }
 
 export const removeUsername = () => {
-  Cookies.remove(UsernameKey)
+  wsCache.delete(UsernameKey)
 }
 
 export const getPassword = () => {
-  const password = Cookies.get(PasswordKey)
+  const password = wsCache.get(PasswordKey)
   return password ? decrypt(password) : undefined
 }
 
 export const setPassword = (password: string) => {
-  Cookies.set(PasswordKey, encrypt(password))
+  wsCache.set(PasswordKey, encrypt(password), { exp: 30 * 24 * 60 * 60 })
 }
 
 export const removePassword = () => {
-  Cookies.remove(PasswordKey)
+  wsCache.delete(PasswordKey)
 }
 
 export const getRememberMe = () => {
-  return Cookies.get(RememberMeKey) === 'true'
+  return wsCache.get(RememberMeKey) === true
 }
 
-export const setRememberMe = (rememberMe: string) => {
-  Cookies.set(RememberMeKey, rememberMe)
+export const setRememberMe = (rememberMe: boolean) => {
+  wsCache.set(RememberMeKey, rememberMe, { exp: 30 * 24 * 60 * 60 })
 }
 
 export const removeRememberMe = () => {
-  Cookies.remove(RememberMeKey)
+  wsCache.delete(RememberMeKey)
 }
 
 // ========== 租户相关 ==========
@@ -81,25 +83,25 @@ const TenantIdKey = 'TENANT_ID'
 const TenantNameKey = 'TENANT_NAME'
 
 export const getTenantName = () => {
-  return Cookies.get(TenantNameKey)
+  return wsCache.get(TenantNameKey)
 }
 
 export const setTenantName = (username: string) => {
-  Cookies.set(TenantNameKey, username)
+  wsCache.set(TenantNameKey, username, { exp: 30 * 24 * 60 * 60 })
 }
 
 export const removeTenantName = () => {
-  Cookies.remove(TenantNameKey)
+  wsCache.delete(TenantNameKey)
 }
 
 export const getTenantId = () => {
-  return Cookies.get(TenantIdKey)
+  return wsCache.get(TenantIdKey)
 }
 
 export const setTenantId = (username: string) => {
-  Cookies.set(TenantIdKey, username)
+  wsCache.set(TenantIdKey, username)
 }
 
 export const removeTenantId = () => {
-  Cookies.remove(TenantIdKey)
+  wsCache.delete(TenantIdKey)
 }

+ 3 - 3
yudao-ui-admin-vue3/src/utils/routerHelper.ts

@@ -18,7 +18,7 @@ export const getParentLayout = () => {
 }
 
 // 按照路由中meta下的rank等级升序来排序路由
-export function ascending(arr: any[]) {
+export const ascending = (arr: any[]) => {
   arr.forEach((v) => {
     if (v?.meta?.rank === null) v.meta.rank = undefined
     if (v?.meta?.rank === 0) {
@@ -109,7 +109,7 @@ export const generateRoute = (routes: AppCustomRouteRecordRaw[]): AppRouteRecord
         data.children = generateRoute(route.children)
       }
     }
-    res.push(data)
+    res.push(data as AppRouteRecordRaw)
   }
   return res
 }
@@ -203,7 +203,7 @@ const addToChildren = (
     }
   }
 }
-function toCamelCase(str: string, upperCaseFirst: boolean) {
+const toCamelCase = (str: string, upperCaseFirst: boolean) => {
   str = (str || '').toLowerCase().replace(/-(.)/g, function (group1: string) {
     return group1.toUpperCase()
   })

+ 9 - 0
yudao-ui-admin-vue3/src/utils/tree.ts

@@ -8,6 +8,11 @@ const DEFAULT_CONFIG: TreeHelperConfig = {
   children: 'children',
   pid: 'pid'
 }
+export const defaultProps = {
+  children: 'children',
+  label: 'name',
+  value: 'id'
+}
 
 const getConfig = (config: Partial<TreeHelperConfig>) => Object.assign({}, DEFAULT_CONFIG, config)
 
@@ -214,6 +219,10 @@ export const eachTree = (treeDatas: any[], callBack: Fn, parentNode = {}) => {
  * @param {*} children 孩子节点字段 默认 'children'
  */
 export const handleTree = (data: any[], id?: string, parentId?: string, children?: string) => {
+  if (!Array.isArray(data)) {
+    console.warn('data must be an array')
+    return []
+  }
   const config = {
     id: id || 'id',
     parentId: parentId || 'parentId',

+ 1 - 1
yudao-ui-admin-vue3/src/views/Error/403.vue

@@ -1,5 +1,5 @@
 <template>
-  <Error type="403" @error-click="errorClick" />
+  <Error type="403" @error-click="errorClick()" />
 </template>
 <script setup lang="ts">
 import { Error } from '@/components/Error'

+ 1 - 1
yudao-ui-admin-vue3/src/views/Error/404.vue

@@ -1,5 +1,5 @@
 <template>
-  <Error @error-click="errorClick" />
+  <Error @error-click="errorClick()" />
 </template>
 <script setup lang="ts">
 import { Error } from '@/components/Error'

+ 1 - 1
yudao-ui-admin-vue3/src/views/Error/500.vue

@@ -1,5 +1,5 @@
 <template>
-  <Error type="500" @error-click="errorClick" />
+  <Error type="500" @error-click="errorClick()" />
 </template>
 <script setup lang="ts">
 import { Error } from '@/components/Error'

+ 5 - 4
yudao-ui-admin-vue3/src/views/Home/Index.vue

@@ -167,20 +167,21 @@ import { EChartsOption } from 'echarts'
 import { ElRow, ElCol, ElSkeleton, ElCard, ElDivider, ElLink } from 'element-plus'
 import { formatTime } from '@/utils'
 import { useI18n } from '@/hooks/web/useI18n'
-import { useCache } from '@/hooks/web/useCache'
+import { useUserStore } from '@/store/modules/user'
 import { useWatermark } from '@/hooks/web/useWatermark'
 import { Echart } from '@/components/Echart'
 import { CountTo } from '@/components/CountTo'
 import { Highlight } from '@/components/Highlight'
+import avatarImg from '@/assets/imgs/avatar.gif'
 import type { WorkplaceTotal, Project, Notice, Shortcut } from './types'
 import { pieOptions, barOptions } from './echarts-data'
 
 const { t } = useI18n()
-const { wsCache } = useCache()
+const userStore = useUserStore()
 const { setWatermark } = useWatermark()
 const loading = ref(true)
-const avatar = wsCache.get('user').user.avatar
-const username = wsCache.get('user').user.nickname
+const avatar = userStore.getUser.avatar ? userStore.getUser.avatar : avatarImg
+const username = userStore.getUser.nickname
 const pieOptionsData = reactive<EChartsOption>(pieOptions) as EChartsOption
 // 获取统计数
 let totalSate = reactive<WorkplaceTotal>({

+ 2 - 2
yudao-ui-admin-vue3/src/views/Login/Login.vue

@@ -63,8 +63,8 @@ import { underlineToHump } from '@/utils'
 import { useI18n } from '@/hooks/web/useI18n'
 import { useDesign } from '@/hooks/web/useDesign'
 import { useAppStore } from '@/store/modules/app'
-import { ThemeSwitch } from '@/components/ThemeSwitch'
-import { LocaleDropdown } from '@/components/LocaleDropdown'
+import { ThemeSwitch } from '@/layout/components/ThemeSwitch'
+import { LocaleDropdown } from '@/layout/components/LocaleDropdown'
 import { LoginForm, MobileForm, RegisterForm, QrCodeForm } from './components'
 
 const { t } = useI18n()

+ 92 - 74
yudao-ui-admin-vue3/src/views/Login/components/LoginForm.vue

@@ -16,7 +16,7 @@
         </el-form-item>
       </el-col>
       <el-col :span="24" style="padding-left: 10px; padding-right: 10px">
-        <el-form-item prop="tenantName">
+        <el-form-item prop="tenantName" v-if="loginData.tenantEnable === 'true'">
           <el-input
             type="text"
             v-model="loginData.loginForm.tenantName"
@@ -65,9 +65,13 @@
       </el-col>
       <el-col :span="24" style="padding-left: 10px; padding-right: 10px">
         <el-form-item>
-          <el-button :loading="loginLoading" type="primary" class="w-[100%]" @click="getCode()">
-            {{ t('login.login') }}
-          </el-button>
+          <XButton
+            :loading="loginLoading"
+            type="primary"
+            class="w-[100%]"
+            :title="t('login.login')"
+            @click="getCode()"
+          />
         </el-form-item>
       </el-col>
       <Verify
@@ -81,19 +85,25 @@
         <el-form-item>
           <el-row justify="space-between" style="width: 100%" :gutter="5">
             <el-col :span="8">
-              <el-button class="w-[100%]" @click="setLoginState(LoginStateEnum.MOBILE)">
-                {{ t('login.btnMobile') }}
-              </el-button>
+              <XButton
+                class="w-[100%]"
+                :title="t('login.btnMobile')"
+                @click="setLoginState(LoginStateEnum.MOBILE)"
+              />
             </el-col>
             <el-col :span="8">
-              <el-button class="w-[100%]" @click="setLoginState(LoginStateEnum.QR_CODE)">
-                {{ t('login.btnQRCode') }}
-              </el-button>
+              <XButton
+                class="w-[100%]"
+                :title="t('login.btnQRCode')"
+                @click="setLoginState(LoginStateEnum.QR_CODE)"
+              />
             </el-col>
             <el-col :span="8">
-              <el-button class="w-[100%]" @click="setLoginState(LoginStateEnum.REGISTER)">
-                {{ t('login.btnRegister') }}
-              </el-button>
+              <XButton
+                class="w-[100%]"
+                :title="t('login.btnRegister')"
+                @click="setLoginState(LoginStateEnum.REGISTER)"
+              />
             </el-col>
           </el-row>
         </el-form-item>
@@ -103,32 +113,13 @@
         <el-form-item>
           <div class="flex justify-between w-[100%]">
             <Icon
-              icon="ant-design:github-filled"
-              :size="30"
-              class="cursor-pointer anticon"
-              color="#999"
-              @click="doSocialLogin('github')"
-            />
-            <Icon
-              icon="ant-design:wechat-filled"
+              v-for="(item, key) in socialList"
+              :key="key"
+              :icon="item.icon"
               :size="30"
               class="cursor-pointer anticon"
               color="#999"
-              @click="doSocialLogin('wechat')"
-            />
-            <Icon
-              icon="ant-design:alipay-circle-filled"
-              :size="30"
-              color="#999"
-              class="cursor-pointer anticon"
-              @click="doSocialLogin('alipay')"
-            />
-            <Icon
-              icon="ant-design:dingtalk-circle-filled"
-              :size="30"
-              color="#999"
-              class="cursor-pointer anticon"
-              @click="doSocialLogin('dingtalk')"
+              @click="doSocialLogin(item.type)"
             />
           </div>
         </el-form-item>
@@ -150,21 +141,24 @@ import {
   ElDivider,
   ElLoading
 } from 'element-plus'
-import Cookies from 'js-cookie'
 import { useRouter } from 'vue-router'
 import type { RouteLocationNormalizedLoaded } from 'vue-router'
 import { useI18n } from '@/hooks/web/useI18n'
 import { useIcon } from '@/hooks/web/useIcon'
+import { useMessage } from '@/hooks/web/useMessage'
 import { required } from '@/utils/formRules'
-import { setToken, setTenantId } from '@/utils/auth'
-import { decrypt, encrypt } from '@/utils/jsencrypt'
-import { Icon } from '@/components/Icon'
+import * as authUtil from '@/utils/auth'
+import { decrypt } from '@/utils/jsencrypt'
 import { Verify } from '@/components/Verifition'
 import { usePermissionStore } from '@/store/modules/permission'
 import * as LoginApi from '@/api/login'
 import { LoginStateEnum, useLoginState, useFormValid } from './useLogin'
 
 const { t } = useI18n()
+const message = useMessage()
+const iconHouse = useIcon({ icon: 'ep:house' })
+const iconAvatar = useIcon({ icon: 'ep:avatar' })
+const iconLock = useIcon({ icon: 'ep:lock' })
 const formLogin = ref()
 const { validForm } = useFormValid(formLogin)
 const { setLoginState, getLoginState } = useLoginState()
@@ -172,9 +166,6 @@ const { currentRoute, push } = useRouter()
 const permissionStore = usePermissionStore()
 const redirect = ref<string>('')
 const loginLoading = ref(false)
-const iconHouse = useIcon({ icon: 'ep:house' })
-const iconAvatar = useIcon({ icon: 'ep:avatar' })
-const iconLock = useIcon({ icon: 'ep:lock' })
 const verify = ref()
 const captchaType = ref('blockPuzzle') // blockPuzzle 滑块 clickWord 点击文字
 
@@ -194,16 +185,33 @@ const loginData = reactive({
     signIn: false
   },
   loginForm: {
-    tenantName: Cookies.get('tenantName') ? Cookies.get('tenantName') : '芋道源码',
-    username: Cookies.get('username') ? Cookies.get('username') : 'admin',
-    password: Cookies.get('password')
-      ? (decrypt(Cookies.get('password')) as unknown as string)
-      : 'admin123',
+    tenantName: '芋道源码',
+    username: 'admin',
+    password: 'admin123',
     captchaVerification: '',
     rememberMe: false
   }
 })
 
+const socialList = [
+  {
+    icon: 'ant-design:github-filled',
+    type: 0
+  },
+  {
+    icon: 'ant-design:wechat-filled',
+    type: 30
+  },
+  {
+    icon: 'ant-design:alipay-circle-filled',
+    type: 0
+  },
+  {
+    icon: 'ant-design:dingtalk-circle-filled',
+    type: 20
+  }
+]
+
 // 获取验证码
 const getCode = async () => {
   // 情况一,未开启:则直接登录
@@ -217,17 +225,19 @@ const getCode = async () => {
 }
 //获取租户ID
 const getTenantId = async () => {
-  const res = await LoginApi.getTenantIdByNameApi(loginData.loginForm.tenantName)
-  setTenantId(res)
+  if (loginData.tenantEnable === 'true') {
+    const res = await LoginApi.getTenantIdByNameApi(loginData.loginForm.tenantName)
+    authUtil.setTenantId(res)
+  }
 }
 // 记住我
 const getCookie = () => {
-  const username = Cookies.get('username')
-  const password = Cookies.get('password')
-    ? (decrypt(Cookies.get('password')) as unknown as string)
+  const username = authUtil.getUsername()
+  const password = authUtil.getPassword()
+    ? decrypt(authUtil.getPassword() as unknown as string)
     : undefined
-  const rememberMe = Cookies.get('rememberMe')
-  const tenantName = Cookies.get('tenantName')
+  const rememberMe = authUtil.getRememberMe()
+  const tenantName = authUtil.getTenantName()
   loginData.loginForm = {
     ...loginData.loginForm,
     username: username ? username : loginData.loginForm.username,
@@ -256,19 +266,17 @@ const handleLogin = async (params) => {
       background: 'rgba(0, 0, 0, 0.7)'
     })
     if (loginData.loginForm.rememberMe) {
-      Cookies.set('username', loginData.loginForm.username, { expires: 30 })
-      Cookies.set('password', encrypt(loginData.loginForm.password as unknown as string), {
-        expires: 30
-      })
-      Cookies.set('rememberMe', loginData.loginForm.rememberMe, { expires: 30 })
-      Cookies.set('tenantName', loginData.loginForm.tenantName, { expires: 30 })
+      authUtil.setUsername(loginData.loginForm.username)
+      authUtil.setPassword(loginData.loginForm.password)
+      authUtil.setRememberMe(loginData.loginForm.rememberMe)
+      authUtil.setTenantName(loginData.loginForm.tenantName)
     } else {
-      Cookies.remove('username')
-      Cookies.remove('password')
-      Cookies.remove('rememberMe')
-      Cookies.remove('tenantName')
+      authUtil.removeUsername()
+      authUtil.removePassword()
+      authUtil.removeRememberMe()
+      authUtil.removeTenantName()
     }
-    setToken(res)
+    authUtil.setToken(res)
     if (!redirect.value) {
       redirect.value = '/'
     }
@@ -284,14 +292,24 @@ const handleLogin = async (params) => {
 }
 
 // 社交登录
-const doSocialLogin = async (type: string) => {
-  loginLoading.value = true
-  // 计算 redirectUri
-  const redirectUri =
-    location.origin + '/social-login?type=' + type + '&redirect=' + (redirect.value || '/')
-  // 进行跳转
-  const res = await LoginApi.socialAuthRedirectApi(type, encodeURIComponent(redirectUri))
-  window.location.href = res
+const doSocialLogin = async (type: number) => {
+  if (type === 0) {
+    message.error('此方式未配置')
+  } else {
+    loginLoading.value = true
+    if (loginData.tenantEnable === 'true') {
+      await message.prompt('请输入租户名称', t('common.reminder')).then(async ({ value }) => {
+        const res = await LoginApi.getTenantIdByNameApi(value)
+        authUtil.setTenantId(res)
+      })
+    }
+    // 计算 redirectUri
+    const redirectUri =
+      location.origin + '/social-login?type=' + type + '&redirect=' + (redirect.value || '/')
+    // 进行跳转
+    const res = await LoginApi.socialAuthRedirectApi(type, encodeURIComponent(redirectUri))
+    window.location.href = res
+  }
 }
 watch(
   () => currentRoute.value,

+ 20 - 13
yudao-ui-admin-vue3/src/views/Login/components/MobileForm.vue

@@ -17,7 +17,7 @@
         </el-form-item>
       </el-col>
       <el-col :span="24" style="padding-left: 10px; padding-right: 10px">
-        <el-form-item prop="tenantName">
+        <el-form-item prop="tenantName" v-if="loginData.tenantEnable === 'true'">
           <el-input
             type="text"
             v-model="loginData.loginForm.tenantName"
@@ -69,16 +69,23 @@
       <!-- 登录按钮 / 返回按钮 -->
       <el-col :span="24" style="padding-left: 10px; padding-right: 10px">
         <el-form-item>
-          <el-button :loading="loginLoading" type="primary" class="w-[100%]" @click="signIn">
-            {{ t('login.login') }}
-          </el-button>
+          <XButton
+            :loading="loginLoading"
+            type="primary"
+            class="w-[100%]"
+            :title="t('login.login')"
+            @click="signIn()"
+          />
         </el-form-item>
       </el-col>
       <el-col :span="24" style="padding-left: 10px; padding-right: 10px">
         <el-form-item>
-          <el-button :loading="loginLoading" class="w-[100%]" @click="handleBackLogin">
-            {{ t('login.backLogin') }}
-          </el-button>
+          <XButton
+            :loading="loginLoading"
+            class="w-[100%]"
+            :title="t('login.backLogin')"
+            @click="handleBackLogin()"
+          />
         </el-form-item>
       </el-col>
     </el-row>
@@ -91,17 +98,15 @@ import { useRouter } from 'vue-router'
 import type { RouteLocationNormalizedLoaded } from 'vue-router'
 import { useI18n } from '@/hooks/web/useI18n'
 import { useIcon } from '@/hooks/web/useIcon'
-import { useCache } from '@/hooks/web/useCache'
 import { useMessage } from '@/hooks/web/useMessage'
-import { setToken } from '@/utils/auth'
 import { required } from '@/utils/formRules'
+import { setTenantId, setToken } from '@/utils/auth'
 import { usePermissionStore } from '@/store/modules/permission'
 import { getTenantIdByNameApi, sendSmsCodeApi, smsLoginApi } from '@/api/login'
 import LoginFormTitle from './LoginFormTitle.vue'
 import { useLoginState, LoginStateEnum, useFormValid } from './useLogin'
 
 const { t } = useI18n()
-const { wsCache } = useCache()
 const message = useMessage()
 const permissionStore = usePermissionStore()
 const { currentRoute, push } = useRouter()
@@ -121,7 +126,7 @@ const rules = {
 }
 const loginData = reactive({
   codeImg: '',
-  tenantEnable: true,
+  tenantEnable: import.meta.env.VITE_APP_TENANT_ENABLE,
   token: '',
   loading: {
     signIn: false
@@ -171,8 +176,10 @@ watch(
 )
 // 获取租户 ID
 const getTenantId = async () => {
-  const res = await getTenantIdByNameApi(loginData.loginForm.tenantName)
-  wsCache.set('tenantId', res)
+  if (loginData.tenantEnable === 'true') {
+    const res = await getTenantIdByNameApi(loginData.loginForm.tenantName)
+    setTenantId(res)
+  }
 }
 // 登录
 const signIn = async () => {

+ 4 - 6
yudao-ui-admin-vue3/src/views/Login/components/QrCodeForm.vue

@@ -11,9 +11,7 @@
     <el-divider class="enter-x">{{ t('login.qrcode') }}</el-divider>
     <el-col :span="24" style="padding-left: 10px; padding-right: 10px">
       <div class="w-[100%] mt-15px">
-        <el-button class="w-[100%]" @click="handleBackLogin">
-          {{ t('sys.login.backSignIn') }}
-        </el-button>
+        <XButton class="w-[100%]" :title="t('login.backLogin')" @click="handleBackLogin()" />
       </div>
     </el-col>
   </el-row>
@@ -21,11 +19,11 @@
 <script setup lang="ts">
 import { computed, unref } from 'vue'
 import { ElRow, ElCol, ElCard, ElDivider } from 'element-plus'
-import { useI18n } from '@/hooks/web/useI18n'
-import { useLoginState, LoginStateEnum } from './useLogin'
-import LoginFormTitle from './LoginFormTitle.vue'
 import { Qrcode } from '@/components/Qrcode'
 import logoImg from '@/assets/imgs/logo.png'
+import { useI18n } from '@/hooks/web/useI18n'
+import LoginFormTitle from './LoginFormTitle.vue'
+import { useLoginState, LoginStateEnum } from './useLogin'
 
 const { t } = useI18n()
 const { handleBackLogin, getLoginState } = useLoginState()

+ 9 - 7
yudao-ui-admin-vue3/src/views/Login/components/RegisterForm.vue

@@ -21,14 +21,16 @@
 
     <template #register>
       <div class="w-[100%]">
-        <el-button type="primary" class="w-[100%]" :loading="loading" @click="loginRegister">
-          {{ t('login.register') }}
-        </el-button>
+        <XButton
+          :loading="loading"
+          type="primary"
+          class="w-[100%]"
+          :title="t('login.register')"
+          @click="loginRegister()"
+        />
       </div>
       <div class="w-[100%] mt-15px">
-        <el-button class="w-[100%]" @click="handleBackLogin">
-          {{ t('login.hasUser') }}
-        </el-button>
+        <XButton class="w-[100%]" :title="t('login.hasUser')" @click="handleBackLogin()" />
       </div>
     </template>
   </Form>
@@ -40,8 +42,8 @@ import { Form } from '@/components/Form'
 import { useI18n } from '@/hooks/web/useI18n'
 import { useForm } from '@/hooks/web/useForm'
 import { useValidator } from '@/hooks/web/useValidator'
-import { useLoginState, LoginStateEnum } from './useLogin'
 import LoginFormTitle from './LoginFormTitle.vue'
+import { useLoginState, LoginStateEnum } from './useLogin'
 import { FormSchema } from '@/types/form'
 
 const { t } = useI18n()

+ 2 - 5
yudao-ui-admin-vue3/src/views/Profile/components/BasicInfo.vue

@@ -1,7 +1,7 @@
 <template>
   <Form ref="formRef" :rules="rules" :schema="schema" :labelWidth="80">
-    <template #sex>
-      <el-radio-group v-model="sexVlue">
+    <template #sex="form">
+      <el-radio-group v-model="form['sex']">
         <el-radio :label="1">{{ t('profile.user.man') }}</el-radio>
         <el-radio :label="2">{{ t('profile.user.woman') }}</el-radio>
       </el-radio-group>
@@ -67,7 +67,6 @@ const schema = reactive<FormSchema[]>([
     value: 0
   }
 ])
-const sexVlue = ref<number>()
 const formRef = ref<FormExpose>() // 表单 Ref
 const submit = () => {
   const elForm = unref(formRef)?.getElFormRef()
@@ -75,7 +74,6 @@ const submit = () => {
   elForm.validate(async (valid) => {
     if (valid) {
       const data = unref(formRef)?.formModel as UserProfileUpdateReqVO
-      data.sex = sexVlue.value as unknown as number
       await updateUserProfileApi(data)
       ElMessage.success(t('common.updateSuccess'))
       await init()
@@ -84,7 +82,6 @@ const submit = () => {
 }
 const init = async () => {
   const res = await getUserProfileApi()
-  sexVlue.value = res.sex
   unref(formRef)?.setValues(res)
 }
 onMounted(async () => {

+ 3 - 3
yudao-ui-admin-vue3/src/views/bpm/form/index.vue

@@ -73,12 +73,12 @@ const submitForm = async () => {
 }
 
 // ========== 详情相关 ==========
-const detailRef = ref() // 详情 Ref
+const detailData = ref() // 详情 Ref
 
 // 详情操作
 const handleDetail = async (row: FormVO) => {
   // 设置数据
-  detailRef.value = row
+  detailData.value = row
   setDialogTile('detail')
 }
 
@@ -148,7 +148,7 @@ getList()
     <Descriptions
       v-if="actionType === 'detail'"
       :schema="allSchemas.detailSchema"
-      :data="detailRef"
+      :data="detailData"
     />
     <!-- 操作按钮 -->
     <template #footer>

برخی فایل ها در این مقایسه diff نمایش داده نمی شوند زیرا تعداد فایل ها بسیار زیاد است