ソースを参照

Merge branch 'ts'

Hixon 1 年間 前
コミット
505458eb81
38 ファイル変更344 行追加288 行削除
  1. 20 0
      LICENSE
  2. 2 2
      package.json
  3. 1 0
      src/api/monitor/operlog/types.ts
  4. 1 1
      src/api/system/config/index.ts
  5. 1 1
      src/api/system/user/index.ts
  6. 24 30
      src/assets/styles/sidebar.scss
  7. 18 30
      src/components/DictTag/index.vue
  8. 16 24
      src/components/Editor/index.vue
  9. 119 107
      src/components/HeaderSearch/index.vue
  10. 4 0
      src/components/IconSelect/index.vue
  11. 29 24
      src/components/RightToolbar/index.vue
  12. 13 1
      src/components/TopNav/index.vue
  13. 4 0
      src/enums/SideThemeEnum.ts
  14. 2 2
      src/layout/components/Navbar.vue
  15. 16 3
      src/layout/components/Settings/index.vue
  16. 8 5
      src/layout/components/Sidebar/SidebarItem.vue
  17. 3 3
      src/layout/components/Sidebar/index.vue
  18. 1 1
      src/layout/components/TagsView/index.vue
  19. 0 0
      src/layout/components/TopBar/search.vue
  20. 1 1
      src/settings.ts
  21. 7 7
      src/store/modules/settings.ts
  22. 1 0
      src/types/global.d.ts
  23. 5 5
      src/views/demo/demo/index.vue
  24. 1 1
      src/views/demo/tree/index.vue
  25. 5 0
      src/views/login.vue
  26. 5 1
      src/views/monitor/operlog/index.vue
  27. 3 7
      src/views/system/client/index.vue
  28. 1 1
      src/views/system/dept/index.vue
  29. 2 3
      src/views/system/dict/data.vue
  30. 2 2
      src/views/system/menu/index.vue
  31. 1 1
      src/views/system/oss/index.vue
  32. 4 4
      src/views/system/tenant/index.vue
  33. 1 1
      src/views/system/tenantPackage/index.vue
  34. 11 8
      src/views/system/user/index.vue
  35. 5 5
      src/views/system/user/profile/index.vue
  36. 2 2
      vite/plugins/index.ts
  37. 5 0
      vite/plugins/setup-extend.ts
  38. 0 5
      vite/plugins/vue-setup-extend.ts

+ 20 - 0
LICENSE

@@ -0,0 +1,20 @@
+The MIT License (MIT)
+
+Copyright (c) 2019 RuoYi-Vue-Plus
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

+ 2 - 2
package.json

@@ -18,7 +18,7 @@
   },
   "dependencies": {
     "@element-plus/icons-vue": "2.1.0",
-    "@vueup/vue-quill": "1.1.0",
+    "@vueup/vue-quill": "1.2.0",
     "@vueuse/core": "9.5.0",
     "animate.css": "4.1.1",
     "await-to-js": "^3.0.0",
@@ -76,7 +76,7 @@
     "vite": "4.3.1",
     "vite-plugin-compression": "0.5.1",
     "vite-plugin-svg-icons": "2.0.1",
-    "vite-plugin-vue-setup-extend": "^0.4.0",
+    "unplugin-vue-setup-extend-plus": "0.4.9",
     "vitest": "^0.29.7",
     "vue-eslint-parser": "9.1.0",
     "vue-tsc": "0.35.0"

+ 1 - 0
src/api/monitor/operlog/types.ts

@@ -1,4 +1,5 @@
 export interface OperLogQuery extends PageQuery {
+  operIp: string;
   title: string;
   operName: string;
   businessType: string;

+ 1 - 1
src/api/system/config/index.ts

@@ -20,7 +20,7 @@ export function getConfig(configId: string | number): AxiosPromise<ConfigVO> {
 }
 
 // 根据参数键名查询参数值
-export function getConfigKey(configKey: string): AxiosPromise<ConfigVO> {
+export function getConfigKey(configKey: string): AxiosPromise<String> {
   return request({
     url: '/system/config/configKey/' + configKey,
     method: 'get'

+ 1 - 1
src/api/system/user/index.ts

@@ -136,7 +136,7 @@ export const updateUserPwd = (oldPassword: string, newPassword: string) => {
     headers: {
       isEncrypt: true
     },
-    params: data
+    data: data
   });
 };
 

+ 24 - 30
src/assets/styles/sidebar.scss

@@ -81,10 +81,16 @@
     }
 
     // menu hover
+    .theme-dark .sub-menu-title-noDropdown,
+    .theme-dark .el-sub-menu__title {
+      &:hover {
+        background-color: $base-sub-menu-title-hover !important;
+      }
+    }
     .sub-menu-title-noDropdown,
     .el-sub-menu__title {
       &:hover {
-        background-color: $base-sub-menu-title-hover !important;
+        background-color: rgba(0, 0, 0, 0.05) !important;
       }
     }
 
@@ -95,12 +101,12 @@
     & .nest-menu .el-sub-menu > .el-sub-menu__title,
     & .el-sub-menu .el-menu-item {
       min-width: $base-sidebar-width !important;
-
       &:hover {
-        background-color: rgba(0, 0, 0, 0.06) !important;
+        background-color: rgba(0, 0, 0, 0.1) !important;
       }
     }
 
+
     & .theme-dark .nest-menu .el-sub-menu > .el-sub-menu__title,
     & .theme-dark .el-sub-menu .el-menu-item {
       background-color: $base-sub-menu-background !important;
@@ -109,6 +115,21 @@
         background-color: $base-sub-menu-hover !important;
       }
     }
+
+    & .theme-dark .nest-menu .el-sub-menu > .el-sub-menu__title,
+    & .theme-dark .el-menu-item {
+      &:hover {
+        // you can use $sub-menuHover
+        background-color: $base-menu-hover !important;
+      }
+    }
+    & .nest-menu .el-sub-menu > .el-sub-menu__title,
+    & .el-menu-item {
+      &:hover {
+        // you can use $sub-menuHover
+        background-color: rgba(0, 0, 0, 0.04) !important;
+      }
+    }
   }
 
   .hideSidebar {
@@ -206,31 +227,4 @@
       margin-right: 16px;
     }
   }
-
-  .nest-menu .el-sub-menu > .el-sub-menu__title,
-  .el-menu-item {
-    &:hover {
-      // you can use $sub-menuHover
-      background-color: $base-menu-hover !important;
-    }
-  }
-
-  // the scroll bar appears when the sub-menu is too long
-  > .el-menu--popup {
-    max-height: 100vh;
-    overflow-y: auto;
-
-    &::-webkit-scrollbar-track-piece {
-      background: #d3dce6;
-    }
-
-    &::-webkit-scrollbar {
-      width: 6px;
-    }
-
-    &::-webkit-scrollbar-thumb {
-      background: #99a9bf;
-      border-radius: 20px;
-    }
-  }
 }

+ 18 - 30
src/components/DictTag/index.vue

@@ -2,7 +2,8 @@
   <div>
     <template v-for="(item, index) in options">
       <template v-if="values.includes(item.value)">
-        <span v-if="item.elTagType == 'default' || item.elTagType == ''" :key="item.value" :index="index" :class="item.elTagClass">
+        <span v-if="(item.elTagType == 'default' || item.elTagType == '') && (item.elTagClass == '' || item.elTagClass == null)"
+              :key="item.value" :index="index" :class="item.elTagClass">
           {{ item.label + " " }}
         </span>
         <el-tag
@@ -37,46 +38,33 @@ const props = defineProps({
   value: [Number, String, Array] as PropType<number | string | Array<number | string>>,
   // 当未找到匹配的数据时,显示value
   showValue: propTypes.bool.def(true),
+  separator: propTypes.string.def(","),
 });
-
 const values = computed(() => {
-  if (props.value !== null && typeof props.value !== "undefined") {
-    return Array.isArray(props.value) ? props.value : [String(props.value)];
-  } else {
-    return [];
-  }
+  if (props.value === '' || props.value === null || typeof props.value === "undefined") return []
+  return Array.isArray(props.value) ? props.value.map(item => '' + item) : String(props.value).split(props.separator);
 });
 
 const unmatch = computed(() => {
-  if (props.value !== null && typeof props.value !== "undefined") {
-    // 传入值为非数组
-    if (!Array.isArray(props.value)) {
-      if (props.options.some((v) => v.value == props.value)) {
-        return false;
-      }
-      return true;
+  if (props.options?.length == 0 || props.value === '' || props.value === null || typeof props.value === "undefined") return false
+  // 传入值为非数组
+  values.value.forEach(item => {
+    if (!props.options.some(v => v.value === item)) {
+      return true // 如果有未匹配项,将标志设置为true
     }
-    return true;
-  }
-  // 没有value不显示
-  return false;
+  })
+  return false // 返回标志的值
 });
 
 const unmatchArray = computed(() => {
 // 记录未匹配的项
   const itemUnmatchArray: Array<string | number> = [];
-  if (props.value !== null && typeof props.value !== "undefined") {
-    // 传入值为非数组
-    if (!Array.isArray(props.value)) {
-      itemUnmatchArray.push(props.value);
-    } else {
-      // 传入值为Array
-      props.value.forEach((item) => {
-        if (!props.options.some((v) => v.value == item)) {
-          itemUnmatchArray.push(item);
-        }
-      });
-    }
+  if (props.value !== '' && props.value !== null && typeof props.value !== "undefined") {
+    values.value.forEach(item => {
+      if (!props.options.some(v => v.value === item)) {
+        itemUnmatchArray.push(item);
+      }
+    })
   }
   // 没有value不显示
   return handleArray(itemUnmatchArray);

+ 16 - 24
src/components/Editor/index.vue

@@ -9,13 +9,13 @@
       name="file"
       :show-file-list="false"
       :headers="upload.headers"
-      style="display: none"
+      ref="uploadRef"
       v-if="type === 'url'"
     >
     </el-upload>
     <div class="editor">
       <quill-editor
-        ref="myQuillEditor"
+        ref="quillEditorRef"
         v-model:content="content"
         contentType="html"
         @textChange="(e: any) => $emit('update:modelValue', content)"
@@ -53,7 +53,7 @@ const upload = reactive<UploadOption>({
   headers: globalHeaders(),
   url: import.meta.env.VITE_APP_BASE_API + '/resource/oss/upload'
 })
-const myQuillEditor = ref();
+const quillEditorRef = ref();
 
 const options = ref({
   theme: "snow",
@@ -86,7 +86,7 @@ const options = ref({
       },
     }
   },
-  placeholder: '请输入内容',
+  placeholder: "请输入内容",
   readOnly: props.readOnly,
 });
 
@@ -110,10 +110,10 @@ watch(() => props.modelValue, (v) => {
 
 // 图片上传成功返回图片地址
 const handleUploadSuccess = (res: any) => {
-  // 获取富文本实例
-  let quill = toRaw(myQuillEditor.value).getQuill();
   // 如果上传成功
   if (res.code === 200) {
+    // 获取富文本实例
+    let quill = toRaw(quillEditorRef.value).getQuill();
     // 获取光标位置
     let length = quill.selection.savedRange.index;
     // 插入图片,res为服务器返回的图片链接地址
@@ -129,6 +129,13 @@ const handleUploadSuccess = (res: any) => {
 
 // 图片上传前拦截
 const handleBeforeUpload = (file: any) => {
+  const type = ["image/jpeg", "image/jpg", "image/png", "image/svg"];
+  const isJPG = type.includes(file.type);
+  //检验文件格式
+  if (!isJPG) {
+    proxy?.$modal.msgError(`图片格式错误!`);
+    return false;
+  }
   // 校检文件大小
   if (props.fileSize) {
     const isLt = file.size / 1024 / 1024 < props.fileSize;
@@ -149,95 +156,80 @@ const handleUploadError = (err: any) => {
 </script>
 
 <style>
+.editor-img-uploader {
+  display: none;
+}
 .editor,
 .ql-toolbar {
   white-space: pre-wrap !important;
   line-height: normal !important;
 }
-
 .quill-img {
   display: none;
 }
-
 .ql-snow .ql-tooltip[data-mode="link"]::before {
   content: "请输入链接地址:";
 }
-
 .ql-snow .ql-tooltip.ql-editing a.ql-action::after {
   border-right: 0;
   content: "保存";
   padding-right: 0;
 }
-
 .ql-snow .ql-tooltip[data-mode="video"]::before {
   content: "请输入视频地址:";
 }
-
 .ql-snow .ql-picker.ql-size .ql-picker-label::before,
 .ql-snow .ql-picker.ql-size .ql-picker-item::before {
   content: "14px";
 }
-
 .ql-snow .ql-picker.ql-size .ql-picker-label[data-value="small"]::before,
 .ql-snow .ql-picker.ql-size .ql-picker-item[data-value="small"]::before {
   content: "10px";
 }
-
 .ql-snow .ql-picker.ql-size .ql-picker-label[data-value="large"]::before,
 .ql-snow .ql-picker.ql-size .ql-picker-item[data-value="large"]::before {
   content: "18px";
 }
-
 .ql-snow .ql-picker.ql-size .ql-picker-label[data-value="huge"]::before,
 .ql-snow .ql-picker.ql-size .ql-picker-item[data-value="huge"]::before {
   content: "32px";
 }
-
 .ql-snow .ql-picker.ql-header .ql-picker-label::before,
 .ql-snow .ql-picker.ql-header .ql-picker-item::before {
   content: "文本";
 }
-
 .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="1"]::before,
 .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="1"]::before {
   content: "标题1";
 }
-
 .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="2"]::before,
 .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="2"]::before {
   content: "标题2";
 }
-
 .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="3"]::before,
 .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="3"]::before {
   content: "标题3";
 }
-
 .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="4"]::before,
 .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="4"]::before {
   content: "标题4";
 }
-
 .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="5"]::before,
 .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="5"]::before {
   content: "标题5";
 }
-
 .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="6"]::before,
 .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="6"]::before {
   content: "标题6";
 }
-
 .ql-snow .ql-picker.ql-font .ql-picker-label::before,
 .ql-snow .ql-picker.ql-font .ql-picker-item::before {
   content: "标准字体";
 }
-
 .ql-snow .ql-picker.ql-font .ql-picker-label[data-value="serif"]::before,
 .ql-snow .ql-picker.ql-font .ql-picker-item[data-value="serif"]::before {
   content: "衬线字体";
 }
-
 .ql-snow .ql-picker.ql-font .ql-picker-label[data-value="monospace"]::before,
 .ql-snow .ql-picker.ql-font .ql-picker-item[data-value="monospace"]::before {
   content: "等宽字体";

+ 119 - 107
src/components/HeaderSearch/index.vue

@@ -1,6 +1,6 @@
 <template>
   <div :class="{ 'show': show }" class="header-search">
-    <svg-icon class-name="search-icon" icon-class="search" @click.stop="click" />
+    <svg-icon class-name="search-icon" icon-class="search" @click.stop="click"/>
     <el-select
       ref="headerSearchSelectRef"
       v-model="search"
@@ -12,21 +12,22 @@
       class="header-search-select"
       @change="change"
     >
-      <el-option v-for="option in options" :key="option.item.path" :value="option.item" :label="option.item.title.join(' > ')" />
+      <el-option v-for="option in options" :key="option.item.path" :value="option.item"
+                 :label="option.item.title.join(' > ')"/>
     </el-select>
   </div>
 </template>
 
 <script setup lang="ts" name="HeaderSearch">
 import Fuse from 'fuse.js';
-import { getNormalPath } from '@/utils/ruoyi';
-import { isHttp } from '@/utils/validate';
+import {getNormalPath} from '@/utils/ruoyi';
+import {isHttp} from '@/utils/validate';
 import usePermissionStore from '@/store/modules/permission';
-import { RouteOption } from 'vue-router';
+import {RouteOption} from 'vue-router';
 
 type Router = Array<{
-    path: string;
-    title: string[];
+  path: string;
+  title: string[];
 }>
 
 const search = ref('');
@@ -39,88 +40,99 @@ const router = useRouter();
 const routes = computed(() => usePermissionStore().routes);
 
 const click = () => {
-    show.value = !show.value
-    if (show.value) {
-        headerSearchSelectRef.value && headerSearchSelectRef.value.focus()
-    }
+  show.value = !show.value
+  if (show.value) {
+    headerSearchSelectRef.value && headerSearchSelectRef.value.focus()
+  }
 };
 const close = () => {
-    headerSearchSelectRef.value && headerSearchSelectRef.value.blur()
-    options.value = []
-    show.value = false
+  headerSearchSelectRef.value && headerSearchSelectRef.value.blur()
+  options.value = []
+  show.value = false
 }
 const change = (val: any) => {
-    const path = val.path;
-    if (isHttp(path)) {
-        // http(s):// 路径新窗口打开
-        const pindex = path.indexOf("http");
-        window.open(path.substr(pindex, path.length), "_blank");
+  const path = val.path;
+  const query = val.query;
+  if (isHttp(path)) {
+    // http(s):// 路径新窗口打开
+    const pindex = path.indexOf("http");
+    window.open(path.substr(pindex, path.length), "_blank");
+  } else {
+    if (query) {
+      router.push({ path: path, query: JSON.parse(query) });
     } else {
-        router.push(path)
+      router.push(path)
     }
-    search.value = ''
-    options.value = []
-    nextTick(() => {
-        show.value = false
-    })
+  }
+  search.value = ''
+  options.value = []
+  nextTick(() => {
+    show.value = false
+  })
 }
 const initFuse = (list: Router) => {
-    fuse.value = new Fuse(list, {
-        shouldSort: true,
-        threshold: 0.4,
-        location: 0,
-        distance: 100,
-        minMatchCharLength: 1,
-        keys: [{
-            name: 'title',
-            weight: 0.7
-        }, {
-            name: 'path',
-            weight: 0.3
-        }]
-    })
+  fuse.value = new Fuse(list, {
+    shouldSort: true,
+    threshold: 0.4,
+    location: 0,
+    distance: 100,
+    minMatchCharLength: 1,
+    keys: [{
+      name: 'title',
+      weight: 0.7
+    }, {
+      name: 'path',
+      weight: 0.3
+    }]
+  })
 }
 // Filter out the routes that can be displayed in the sidebar
 // And generate the internationalized title
-const generateRoutes = (routes: RouteOption[], basePath = '', prefixTitle: string[] = []) => {
-    let res: Router = []
-    routes.forEach(r => {
-        // skip hidden router
-        if (!r.hidden) {
-            const p = r.path.length > 0 && r.path[0] === '/' ? r.path : '/' + r.path;
-            const data = {
-                path: !isHttp(r.path) ? getNormalPath(basePath + p) : r.path,
-                title: [...prefixTitle]
-            }
-            if (r.meta && r.meta.title) {
-                data.title = [...data.title, r.meta.title];
-                if (r.redirect !== 'noRedirect') {
-                    // only push the routes with title
-                    // special case: need to exclude parent router without redirect
-                    res.push(data);
-                }
-            }
-            // recursive child routes
-            if (r.children) {
-                const tempRoutes = generateRoutes(r.children, data.path, data.title);
-                if (tempRoutes.length >= 1) {
-                    res = [...res, ...tempRoutes];
-                }
-            }
+const generateRoutes = (routes: RouteOption[], basePath = '', prefixTitle: string[] = [], query: any = {}) => {
+  let res: Router = []
+  routes.forEach(r => {
+    // skip hidden router
+    if (!r.hidden) {
+      const p = r.path.length > 0 && r.path[0] === '/' ? r.path : '/' + r.path;
+      const data = {
+        path: !isHttp(r.path) ? getNormalPath(basePath + p) : r.path,
+        title: [...prefixTitle],
+        query: ''
+      }
+      if (r.meta && r.meta.title) {
+        data.title = [...data.title, r.meta.title];
+        if (r.redirect !== 'noRedirect') {
+          // only push the routes with title
+          // special case: need to exclude parent router without redirect
+          res.push(data);
         }
-    })
-    return res;
+      }
+
+      if (r.query) {
+        data.query = r.query
+      }
+
+      // recursive child routes
+      if (r.children) {
+        const tempRoutes = generateRoutes(r.children, data.path, data.title, data.query);
+        if (tempRoutes.length >= 1) {
+          res = [...res, ...tempRoutes];
+        }
+      }
+    }
+  })
+  return res;
 }
 const querySearch = (query: string) => {
-    if (query !== '') {
-        options.value = fuse.value.search(query)
-    } else {
-        options.value = []
-    }
+  if (query !== '') {
+    options.value = fuse.value.search(query)
+  } else {
+    options.value = []
+  }
 }
 
 onMounted(() => {
-    searchPool.value = generateRoutes(routes.value);
+  searchPool.value = generateRoutes(routes.value);
 })
 
 // watchEffect(() => {
@@ -128,54 +140,54 @@ onMounted(() => {
 // })
 
 watch(show, (value) => {
-    if (value) {
-        document.body.addEventListener('click', close)
-    } else {
-        document.body.removeEventListener('click', close)
-    }
+  if (value) {
+    document.body.addEventListener('click', close)
+  } else {
+    document.body.removeEventListener('click', close)
+  }
 })
 
 watch(searchPool, (list) => {
-    initFuse(list)
+  initFuse(list)
 })
 </script>
 
 <style lang="scss" scoped>
 .header-search {
-    font-size: 0 !important;
+  font-size: 0 !important;
 
-    .search-icon {
-        cursor: pointer;
-        font-size: 18px;
-        vertical-align: middle;
-    }
+  .search-icon {
+    cursor: pointer;
+    font-size: 18px;
+    vertical-align: middle;
+  }
 
-    .header-search-select {
-        font-size: 18px;
-        transition: width 0.2s;
-        width: 0;
-        overflow: hidden;
-        background: transparent;
-        border-radius: 0;
-        display: inline-block;
-        vertical-align: middle;
+  .header-search-select {
+    font-size: 18px;
+    transition: width 0.2s;
+    width: 0;
+    overflow: hidden;
+    background: transparent;
+    border-radius: 0;
+    display: inline-block;
+    vertical-align: middle;
 
-        :deep(.el-input__inner) {
-            border-radius: 0;
-            border: 0;
-            padding-left: 0;
-            padding-right: 0;
-            box-shadow: none !important;
-            border-bottom: 1px solid #d9d9d9;
-            vertical-align: middle;
-        }
+    :deep(.el-input__inner) {
+      border-radius: 0;
+      border: 0;
+      padding-left: 0;
+      padding-right: 0;
+      box-shadow: none !important;
+      border-bottom: 1px solid #d9d9d9;
+      vertical-align: middle;
     }
+  }
 
-    &.show {
-        .header-search-select {
-            width: 210px;
-            margin-left: 10px;
-        }
+  &.show {
+    .header-search-select {
+      width: 210px;
+      margin-left: 10px;
     }
+  }
 }
 </style>

+ 4 - 0
src/components/IconSelect/index.vue

@@ -68,6 +68,10 @@ const selectedIcon = (iconName: string) => {
 </script>
 
 <style scoped lang="scss">
+.el-scrollbar {
+  max-height: calc(50vh - 100px)!important;
+  overflow-y: auto;
+}
 .el-divider--horizontal {
   margin: 10px auto !important;
 }

+ 29 - 24
src/components/RightToolbar/index.vue

@@ -7,13 +7,23 @@
       <el-tooltip class="item" effect="dark" content="刷新" placement="top">
         <el-button circle icon="Refresh" @click="refresh()" />
       </el-tooltip>
-      <el-tooltip class="item" effect="dark" content="显隐列" placement="top" v-if="columns">
-        <el-button circle icon="Menu" @click="showColumn()" />
+      <el-tooltip class="item" effect="dark" content="显示/隐藏列" placement="top" v-if="columns">
+        <el-popover placement="bottom" trigger="click">
+          <div class="tree-header">显示/隐藏列</div>
+          <el-tree
+            ref="columnRef"
+            :data="columns"
+            show-checkbox
+            @check="columnChange"
+            node-key="key"
+            :props="{ label: 'label', children: 'children' }"
+          ></el-tree>
+          <template #reference>
+            <el-button circle icon="Menu" />
+          </template>
+        </el-popover>
       </el-tooltip>
     </el-row>
-    <el-dialog :title="title" v-model="open" append-to-body>
-      <el-transfer :titles="['显示', '隐藏']" v-model="value" :data="columns" @change="dataChange"></el-transfer>
-    </el-dialog>
   </div>
 </template>
 
@@ -29,15 +39,9 @@ const props = defineProps({
     gutter: propTypes.number.def(10),
 })
 
+const columnRef = ref<ElTreeInstance>();
 const emits = defineEmits(['update:showSearch', 'queryTable']);
 
-// 显隐数据
-const value = ref<Array<number>>([]);
-// 弹出层标题
-const title = ref("显示/隐藏");
-// 是否显示弹出层
-const open = ref(false);
-
 const style = computed(() => {
     const ret: any = {};
     if (props.gutter) {
@@ -56,23 +60,19 @@ function refresh() {
     emits("queryTable");
 }
 
-// 右侧列表元素变化
-function dataChange(data: TransferKey[]) {
-    props.columns?.forEach((item) => {
-        item.visible = !data.includes(item.key);
-    })
-}
-
-// 打开显隐列dialog
-const showColumn = () => {
-    open.value = true;
+// 更改数据列的显示和隐藏
+function columnChange(...args: any[]) {
+  props.columns?.forEach((item) => {
+    item.visible = args[1].checkedKeys.includes(item.key);
+  })
 }
 
 // 显隐列初始默认隐藏列
 onMounted(() => {
     props.columns?.forEach((item) => {
-        if (!item.visible) {
-            value.value.push(item.key);
+        if (item.visible) {
+          columnRef.value?.setChecked(item.key, true, false);
+            // value.value.push(item.key);
         }
     })
 })
@@ -91,4 +91,9 @@ onMounted(() => {
 .my-el-transfer {
   text-align: center;
 }
+.tree-header{
+  width: 100%;
+  line-height: 24px;
+  text-align: center;
+}
 </style>

+ 13 - 1
src/components/TopNav/index.vue

@@ -2,7 +2,9 @@
   <el-menu :default-active="activeMenu" mode="horizontal" @select="handleSelect" :ellipsis="false">
     <template v-for="(item, index) in topMenus">
       <el-menu-item :style="{'--theme': theme}" :index="item.path" :key="index" v-if="index < visibleNumber"
-        ><svg-icon :icon-class="item.meta ? item.meta.icon : '' " /> {{ item.meta?.title }}</el-menu-item
+        ><svg-icon
+        v-if="item.meta && item.meta.icon && item.meta.icon !== '#'"
+        :icon-class="item.meta ? item.meta.icon : '' " /> {{ item.meta?.title }}</el-menu-item
       >
     </template>
 
@@ -180,4 +182,14 @@ onMounted(() => {
   padding: 0 5px !important;
   margin: 0 10px !important;
 }
+
+/* 背景色隐藏 */
+.topmenu-container.el-menu--horizontal>.el-menu-item:not(.is-disabled):focus, .topmenu-container.el-menu--horizontal>.el-menu-item:not(.is-disabled):hover, .topmenu-container.el-menu--horizontal>.el-submenu .el-submenu__title:hover {
+  background-color: #ffffff !important;
+}
+
+/* 图标右间距 */
+.topmenu-container .svg-icon {
+  margin-right: 4px;
+}
 </style>

+ 4 - 0
src/enums/SideThemeEnum.ts

@@ -0,0 +1,4 @@
+export enum SideThemeEnum {
+  DARK = 'theme-dark',
+  LIGHT = 'theme-light'
+}

+ 2 - 2
src/layout/components/Navbar.vue

@@ -58,7 +58,7 @@
               <router-link to="/user/profile" v-if="!dynamic">
                 <el-dropdown-item>{{ $t('navbar.personalCenter') }}</el-dropdown-item>
               </router-link>
-              <el-dropdown-item command="setLayout">
+              <el-dropdown-item command="setLayout" v-if="settingsStore.showSettings">
                 <span>{{ $t('navbar.layoutSetting') }}</span>
               </el-dropdown-item>
               <el-dropdown-item divided command="logout">
@@ -73,7 +73,7 @@
 </template>
 
 <script setup lang="ts">
-import SearchMenu from './topBar/search.vue';
+import SearchMenu from './TopBar/search.vue';
 import useAppStore from '@/store/modules/app';
 import useUserStore from '@/store/modules/user';
 import useSettingsStore from '@/store/modules/settings';

+ 16 - 3
src/layout/components/Settings/index.vue

@@ -3,7 +3,7 @@
     <h3 class="drawer-title">主题风格设置</h3>
 
     <div class="setting-drawer-block-checbox">
-      <div class="setting-drawer-block-checbox-item" @click="handleTheme('theme-dark')">
+      <div class="setting-drawer-block-checbox-item" @click="handleTheme(SideThemeEnum.DARK)">
         <img src="@/assets/images/dark.svg" alt="dark" />
         <div v-if="sideTheme === 'theme-dark'" class="setting-drawer-block-checbox-selectIcon" style="display: block;">
           <i aria-label="图标: check" class="anticon anticon-check">
@@ -15,7 +15,7 @@
           </i>
         </div>
       </div>
-      <div class="setting-drawer-block-checbox-item" @click="handleTheme('theme-light')">
+      <div class="setting-drawer-block-checbox-item" @click="handleTheme(SideThemeEnum.LIGHT)">
         <img src="@/assets/images/light.svg" alt="light" />
         <div v-if="sideTheme === 'theme-light'" class="setting-drawer-block-checbox-selectIcon" style="display: block;">
           <i aria-label="图标: check" class="anticon anticon-check">
@@ -95,6 +95,7 @@ import usePermissionStore from '@/store/modules/permission'
 import { handleThemeStyle } from '@/utils/theme'
 import { ComponentInternalInstance } from "vue";
 import { SettingTypeEnum } from "@/enums/SettingTypeEnum";
+import { SideThemeEnum } from "@/enums/SideThemeEnum";
 
 const { proxy } = getCurrentInstance() as ComponentInternalInstance;
 const appStore = useAppStore()
@@ -114,6 +115,13 @@ const isDark = useDark({
   valueDark: 'dark',
   valueLight: 'light',
 });
+watch(isDark, ()=> {
+  if (isDark.value) {
+    settingsStore.changeSetting({ key: SettingTypeEnum.SIDE_THEME, value: SideThemeEnum.DARK })
+  } else {
+    settingsStore.changeSetting({ key: SettingTypeEnum.SIDE_THEME, value: sideTheme.value })
+  }
+})
 const toggleDark = () => useToggle(isDark);
 
 /** 是否需要topNav */
@@ -166,8 +174,13 @@ const themeChange = (val: string | null) => {
     }
 }
 const handleTheme = (val: string) => {
-    settingsStore.changeSetting({ key: SettingTypeEnum.SIDE_THEME, value: val })
     sideTheme.value = val;
+    if (isDark.value && val === SideThemeEnum.LIGHT) {
+      // 暗黑模式颜色不变
+      settingsStore.changeSetting({ key: SettingTypeEnum.SIDE_THEME, value: SideThemeEnum.DARK })
+      return
+    }
+    settingsStore.changeSetting({ key: SettingTypeEnum.SIDE_THEME, value: val })
 }
 const saveSetting = () => {
     proxy?.$modal.loading("正在保存到本地,请稍候...");

+ 8 - 5
src/layout/components/Sidebar/SidebarItem.vue

@@ -1,6 +1,6 @@
 <template>
   <div v-if="!item.hidden">
-    <template v-if="hasOneShowingChild(item.children, item) && (!onlyOneChild.children || onlyOneChild.noShowingChildren) && !item.alwaysShow">
+    <template v-if="hasOneShowingChild(item, item.children) && (!onlyOneChild.children || onlyOneChild.noShowingChildren) && !item.alwaysShow">
       <app-link v-if="onlyOneChild.meta" :to="resolvePath(onlyOneChild.path, onlyOneChild.query)">
         <el-menu-item :index="resolvePath(onlyOneChild.path)" :class="{ 'submenu-title-noDropdown': !isNest }">
           <svg-icon :icon-class="onlyOneChild.meta.icon || (item.meta && item.meta.icon)" />
@@ -18,10 +18,10 @@
       </template>
 
       <sidebar-item
-        v-for="child in item.children"
+        v-for="(child) in item.children as RouteOption[]"
         :key="child.path"
         :is-nest="true"
-        :item="child as RouteOption"
+        :item="child"
         :base-path="resolvePath(child.path)"
         class="nest-menu"
       />
@@ -34,7 +34,6 @@ import { isExternal } from '@/utils/validate'
 import AppLink from './Link.vue'
 import { getNormalPath } from '@/utils/ruoyi'
 import { RouteOption } from "vue-router";
-import { PropType } from "vue";
 
 const props = defineProps({
     // route object
@@ -54,7 +53,7 @@ const props = defineProps({
 
 const onlyOneChild = ref<any>({});
 
-const hasOneShowingChild = (children:RouteOption[] = [], parent: RouteOption) => {
+const hasOneShowingChild = (parent: RouteOption, children?:RouteOption[]) => {
     if (!children) {
         children = [];
     }
@@ -76,9 +75,13 @@ const hasOneShowingChild = (children:RouteOption[] = [], parent: RouteOption) =>
     // Show parent if there are no child router to display
     if (showingChildren.length === 0) {
         onlyOneChild.value = { ...parent, path: '', noShowingChildren: true }
+        if (parent.name === '2222') {
+          console.log(onlyOneChild.value)
+        }
         return true
     }
 
+
     return false
 };
 

+ 3 - 3
src/layout/components/Sidebar/index.vue

@@ -4,7 +4,7 @@
     <el-scrollbar :class="sideTheme" wrap-class="scrollbar-wrapper">
       <transition :enter-active-class="proxy?.animate.menuSearchAnimate.enter" mode="out-in">
         <el-menu
-          :default-active="activeMenu as string"
+          :default-active="activeMenu"
           :collapse="isCollapse"
           :background-color="bgColor"
           :text-color="textColor"
@@ -27,7 +27,7 @@ import variables from '@/assets/styles/variables.module.scss'
 import useAppStore from '@/store/modules/app'
 import useSettingsStore from '@/store/modules/settings'
 import usePermissionStore from '@/store/modules/permission'
-import { ComponentInternalInstance } from "vue";
+import { RouteOption } from "vue-router";
 const { proxy } = getCurrentInstance() as ComponentInternalInstance;
 
 const route = useRoute();
@@ -35,7 +35,7 @@ const appStore = useAppStore()
 const settingsStore = useSettingsStore()
 const permissionStore = usePermissionStore()
 
-const sidebarRouters =  computed(() => permissionStore.sidebarRouters);
+const sidebarRouters =  computed<RouteOption[]>(() => permissionStore.sidebarRouters);
 const showLogo = computed(() => settingsStore.sidebarLogo);
 const sideTheme = computed(() => settingsStore.sideTheme);
 const theme = computed(() => settingsStore.theme);

+ 1 - 1
src/layout/components/TagsView/index.vue

@@ -278,7 +278,7 @@ onMounted(() => {
           height: 8px;
           border-radius: 50%;
           position: relative;
-          margin-right: 2px;
+          margin-right: 5px;
         }
       }
     }

+ 0 - 0
src/layout/components/topBar/search.vue → src/layout/components/TopBar/search.vue


+ 1 - 1
src/settings.ts

@@ -13,7 +13,7 @@ const setting: DefaultSettings = {
   /**
    * 是否系统布局配置
    */
-  showSettings: false,
+  showSettings: true,
 
   /**
    * 是否显示顶部导航

+ 7 - 7
src/store/modules/settings.ts

@@ -11,13 +11,13 @@ export const useSettingsStore = defineStore('setting', () => {
     title: ref<string>(''),
     theme: ref<string>(storageSetting.theme || defaultSettings.theme),
     sideTheme: ref<string>(storageSetting.sideTheme || defaultSettings.sideTheme),
-    showSettings: ref<boolean>(storageSetting.showSettings),
-    topNav: ref<boolean>(storageSetting.topNav || defaultSettings.topNav),
-    tagsView: ref<boolean>(storageSetting.tagsView || defaultSettings.tagsView),
-    fixedHeader: ref<boolean>(storageSetting.fixedHeader || defaultSettings.fixedHeader),
-    sidebarLogo: ref<boolean>(storageSetting.sidebarLogo || defaultSettings.sidebarLogo),
-    dynamicTitle: ref<boolean>(storageSetting.dynamicTitle || defaultSettings.dynamicTitle),
-    animationEnable: ref<boolean>(storageSetting.animationEnable || defaultSettings.animationEnable),
+    showSettings: ref<boolean>(storageSetting.showSettings || defaultSettings.showSettings),
+    topNav: ref<boolean>(storageSetting.topNav === undefined ? defaultSettings.topNav : storageSetting.topNav),
+    tagsView: ref<boolean>(storageSetting.tagsView === undefined ? defaultSettings.tagsView : storageSetting.tagsView),
+    fixedHeader: ref<boolean>(storageSetting.fixedHeader === undefined ? defaultSettings.fixedHeader : storageSetting.fixedHeader),
+    sidebarLogo: ref<boolean>(storageSetting.sidebarLogo === undefined ? defaultSettings.sidebarLogo : storageSetting.sidebarLogo),
+    dynamicTitle: ref<boolean>(storageSetting.dynamicTitle === undefined ? defaultSettings.dynamicTitle : storageSetting.dynamicTitle),
+    animationEnable: ref<boolean>(storageSetting.animationEnable === undefined ? defaultSettings.animationEnable : storageSetting.animationEnable),
     dark: ref<boolean>(storageSetting.dark || defaultSettings.dark)
   };
 

+ 1 - 0
src/types/global.d.ts

@@ -13,6 +13,7 @@ declare global {
     key: number;
     label: string;
     visible: boolean;
+    children: Array<FieldOption>;
   }
 
   /**

+ 5 - 5
src/views/demo/demo/index.vue

@@ -5,19 +5,19 @@
         <el-card shadow="hover">
           <el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px">
             <el-form-item label="部门id" prop="deptId">
-              <el-input v-model="queryParams.deptId" placeholder="请输入部门id" clearable @keyup.enter="handleQuery" />
+              <el-input v-model="queryParams.deptId" placeholder="请输入部门id" clearable style="width: 240px" @keyup.enter="handleQuery" />
             </el-form-item>
             <el-form-item label="用户id" prop="userId">
-              <el-input v-model="queryParams.userId" placeholder="请输入用户id" clearable @keyup.enter="handleQuery" />
+              <el-input v-model="queryParams.userId" placeholder="请输入用户id" clearable style="width: 240px" @keyup.enter="handleQuery" />
             </el-form-item>
             <el-form-item label="排序号" prop="orderNum">
-              <el-input v-model="queryParams.orderNum" placeholder="请输入排序号" clearable @keyup.enter="handleQuery" />
+              <el-input v-model="queryParams.orderNum" placeholder="请输入排序号" clearable style="width: 240px" @keyup.enter="handleQuery" />
             </el-form-item>
             <el-form-item label="key键" prop="testKey">
-              <el-input v-model="queryParams.testKey" placeholder="请输入key键" clearable @keyup.enter="handleQuery" />
+              <el-input v-model="queryParams.testKey" placeholder="请输入key键" clearable style="width: 240px" @keyup.enter="handleQuery" />
             </el-form-item>
             <el-form-item label="值" prop="value">
-              <el-input v-model="queryParams.value" placeholder="请输入值" clearable @keyup.enter="handleQuery" />
+              <el-input v-model="queryParams.value" placeholder="请输入值" clearable style="width: 240px" @keyup.enter="handleQuery" />
             </el-form-item>
             <el-form-item>
               <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>

+ 1 - 1
src/views/demo/tree/index.vue

@@ -5,7 +5,7 @@
         <el-card shadow="hover">
           <el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px">
             <el-form-item label="树节点名" prop="treeName">
-              <el-input v-model="queryParams.treeName" placeholder="请输入树节点名" clearable @keyup.enter="handleQuery" />
+              <el-input v-model="queryParams.treeName" placeholder="请输入树节点名" clearable style="width: 240px" @keyup.enter="handleQuery" />
             </el-form-item>
             <el-form-item>
               <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>

+ 5 - 0
src/views/login.vue

@@ -100,6 +100,10 @@ const loginRef = ref<ElFormInstance>();
 // 租户列表
 const tenantList = ref<TenantVO[]>([]);
 
+watch(() => router.currentRoute.value, (newRoute: any) => {
+  redirect.value = newRoute.query && newRoute.query.redirect;
+}, { immediate: true });
+
 const handleLogin = () => {
   loginRef.value?.validate(async (valid: boolean, fields: any) => {
     if (valid) {
@@ -121,6 +125,7 @@ const handleLogin = () => {
       const [err] = await to(userStore.login(loginForm.value));
       if (!err) {
         await router.push({ path: redirect.value || '/' });
+        loading.value = false;
       } else {
         loading.value = false;
         // 重新获取验证码

+ 5 - 1
src/views/monitor/operlog/index.vue

@@ -4,6 +4,9 @@
       <div class="mb-[10px]">
         <el-card shadow="hover">
           <el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px">
+            <el-form-item label="操作地址" prop="operIp">
+              <el-input v-model="queryParams.operIp" placeholder="请输入操作地址" clearable style="width: 240px;" @keyup.enter="handleQuery"/>
+            </el-form-item>
             <el-form-item label="系统模块" prop="title">
               <el-input v-model="queryParams.title" placeholder="请输入系统模块" clearable style="width: 240px;" @keyup.enter="handleQuery" />
             </el-form-item>
@@ -83,7 +86,7 @@
           sortable="custom"
           :sort-orders="['descending', 'ascending']"
         />
-        <el-table-column label="主机" align="center" prop="operIp" width="130" :show-overflow-tooltip="true" />
+        <el-table-column label="操作地址" align="center" prop="operIp" width="130" :show-overflow-tooltip="true" />
         <el-table-column label="操作状态" align="center" prop="status">
           <template #default="scope">
             <dict-tag :options="sys_common_status" :value="scope.row.status" />
@@ -215,6 +218,7 @@ const data = reactive<PageData<OperLogForm, OperLogQuery>>({
   queryParams: {
     pageNum: 1,
     pageSize: 10,
+    operIp: '',
     title: '',
     operName: '',
     businessType: '',

+ 3 - 7
src/views/system/client/index.vue

@@ -4,10 +4,10 @@
       <div class="search" v-show="showSearch">
         <el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="100px">
           <el-form-item label="客户端key" prop="clientKey">
-            <el-input v-model="queryParams.clientKey" placeholder="请输入客户端key" clearable @keyup.enter="handleQuery" />
+            <el-input v-model="queryParams.clientKey" placeholder="请输入客户端key" clearable style="width: 240px" @keyup.enter="handleQuery" />
           </el-form-item>
           <el-form-item label="客户端秘钥" prop="clientSecret">
-            <el-input v-model="queryParams.clientSecret" placeholder="请输入客户端秘钥" clearable @keyup.enter="handleQuery" />
+            <el-input v-model="queryParams.clientSecret" placeholder="请输入客户端秘钥" clearable style="width: 240px" @keyup.enter="handleQuery" />
           </el-form-item>
           <el-form-item label="状态" prop="status">
             <el-select v-model="queryParams.status" placeholder="状态" clearable>
@@ -53,11 +53,7 @@
         <el-table-column label="客户端秘钥" align="center" prop="clientSecret" />
         <el-table-column label="授权类型" align="center">
           <template #default="scope">
-            <div>
-              <template v-for="(type, index) in scope.row.grantTypeList" :key="index">
-                <dict-tag class="el-check-tag" :options="sys_grant_type" :value="type" />
-              </template>
-            </div>
+            <dict-tag :options="sys_grant_type" :value="scope.row.grantTypeList" />
           </template>
         </el-table-column>
         <el-table-column label="设备类型" align="center">

+ 1 - 1
src/views/system/dept/index.vue

@@ -5,7 +5,7 @@
         <el-card shadow="hover">
           <el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="68px">
             <el-form-item label="部门名称" prop="deptName">
-              <el-input v-model="queryParams.deptName" placeholder="请输入部门名称" clearable @keyup.enter="handleQuery" />
+              <el-input v-model="queryParams.deptName" placeholder="请输入部门名称" clearable style="width: 240px" @keyup.enter="handleQuery" />
             </el-form-item>
             <el-form-item label="状态" prop="status">
               <el-select v-model="queryParams.status" placeholder="部门状态" clearable>

+ 2 - 3
src/views/system/dict/data.vue

@@ -49,9 +49,8 @@
         <el-table-column label="字典编码" align="center" prop="dictCode" v-if="false" />
         <el-table-column label="字典标签" align="center" prop="dictLabel">
           <template #default="scope">
-            <span v-if="scope.row.listClass === '' || scope.row.listClass === 'default'">{{ scope.row.dictLabel }}</span>
-            <el-tag v-else :type="scope.row.listClass === 'primary' ? '' : scope.row.listClass">{{ scope.row.dictLabel
-            }}</el-tag>
+            <span v-if="(scope.row.listClass == '' || scope.row.listClass == 'default') && (scope.row.cssClass == '' || scope.row.cssClass == null)">{{ scope.row.dictLabel }}</span>
+            <el-tag v-else :type="scope.row.listClass == 'primary' ? '' : scope.row.listClass" :class="scope.row.cssClass">{{ scope.row.dictLabel }}</el-tag>
           </template>
         </el-table-column>
         <el-table-column label="字典键值" align="center" prop="dictValue" />

+ 2 - 2
src/views/system/menu/index.vue

@@ -5,7 +5,7 @@
         <el-card shadow="hover">
           <el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="68px">
             <el-form-item label="菜单名称" prop="menuName">
-              <el-input v-model="queryParams.menuName" placeholder="请输入菜单名称" clearable @keyup.enter="handleQuery" />
+              <el-input v-model="queryParams.menuName" placeholder="请输入菜单名称" clearable style="width: 240px" @keyup.enter="handleQuery" />
             </el-form-item>
             <el-form-item label="状态" prop="status">
               <el-select v-model="queryParams.status" placeholder="菜单状态" clearable>
@@ -229,7 +229,7 @@
               </el-radio-group>
             </el-form-item>
           </el-col>
-          <el-col :span="12" v-if="form.menuType !== 'F'">
+          <el-col :span="12">
             <el-form-item>
               <template #label>
                 <span>

+ 1 - 1
src/views/system/oss/index.vue

@@ -194,7 +194,7 @@ const { queryParams, form, rules } = toRefs(data);
 const getList = async () => {
   loading.value = true;
   const res = await proxy?.getConfigKey("sys.oss.previewListResource");
-  previewListResource.value = res?.msg === undefined ? true : res.msg === 'true';
+  previewListResource.value = res?.data === undefined ? true : res.data === 'true';
   const response = await listOss(proxy?.addDateRange(queryParams.value, dateRangeCreateTime.value, "CreateTime"));
   ossList.value = response.rows;
   total.value = response.total;

+ 4 - 4
src/views/system/tenant/index.vue

@@ -5,16 +5,16 @@
         <el-card shadow="hover">
           <el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px">
             <el-form-item label="租户编号" prop="tenantId">
-              <el-input v-model="queryParams.tenantId" placeholder="请输入租户编号" clearable @keyup.enter="handleQuery" />
+              <el-input v-model="queryParams.tenantId" placeholder="请输入租户编号" clearable style="width: 240px" @keyup.enter="handleQuery" />
             </el-form-item>
             <el-form-item label="联系人" prop="contactUserName">
-              <el-input v-model="queryParams.contactUserName" placeholder="请输入联系人" clearable @keyup.enter="handleQuery" />
+              <el-input v-model="queryParams.contactUserName" placeholder="请输入联系人" clearable style="width: 240px" @keyup.enter="handleQuery" />
             </el-form-item>
             <el-form-item label="联系电话" prop="contactPhone">
-              <el-input v-model="queryParams.contactPhone" placeholder="请输入联系电话" clearable @keyup.enter="handleQuery" />
+              <el-input v-model="queryParams.contactPhone" placeholder="请输入联系电话" clearable style="width: 240px" @keyup.enter="handleQuery" />
             </el-form-item>
             <el-form-item label="企业名称" prop="companyName">
-              <el-input v-model="queryParams.companyName" placeholder="请输入企业名称" clearable @keyup.enter="handleQuery" />
+              <el-input v-model="queryParams.companyName" placeholder="请输入企业名称" clearable style="width: 240px" @keyup.enter="handleQuery" />
             </el-form-item>
             <el-form-item>
               <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>

+ 1 - 1
src/views/system/tenantPackage/index.vue

@@ -5,7 +5,7 @@
         <el-card shadow="hover">
           <el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px">
             <el-form-item label="套餐名称" prop="packageName">
-              <el-input v-model="queryParams.packageName" placeholder="请输入套餐名称" clearable @keyup.enter="handleQuery" />
+              <el-input v-model="queryParams.packageName" placeholder="请输入套餐名称" clearable style="width: 240px" @keyup.enter="handleQuery" />
             </el-form-item>
             <el-form-item>
               <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>

+ 11 - 8
src/views/system/user/index.vue

@@ -320,7 +320,7 @@ const total = ref(0);
 const dateRange = ref<[DateModelType, DateModelType]>(['', '']);
 const deptName = ref('');
 const deptOptions = ref<DeptVO[]>([]);
-const initPassword = ref('123456');
+const initPassword = ref<String>('');
 const postOptions = ref<PostVO[]>([]);
 const roleOptions = ref<RoleVO[]>([]);
 /*** 用户导入参数 */
@@ -340,13 +340,13 @@ const upload = reactive<ImportOption>({
 })
 // 列显隐信息
 const columns = ref<FieldOption[]>([
-  { key: 0, label: `用户编号`, visible: false },
-  { key: 1, label: `用户名称`, visible: true },
-  { key: 2, label: `用户昵称`, visible: true },
-  { key: 3, label: `部门`, visible: true },
-  { key: 4, label: `手机号码`, visible: true },
-  { key: 5, label: `状态`, visible: true },
-  { key: 6, label: `创建时间`, visible: true }
+  { key: 0, label: `用户编号`, visible: false,children: [] },
+  { key: 1, label: `用户名称`, visible: true,children: [] },
+  { key: 2, label: `用户昵称`, visible: true,children: [] },
+  { key: 3, label: `部门`, visible: true,children: [] },
+  { key: 4, label: `手机号码`, visible: true,children: [] },
+  { key: 5, label: `状态`, visible: true,children: [] },
+  { key: 6, label: `创建时间`, visible: true,children: [] }
 ])
 
 
@@ -613,6 +613,9 @@ const resetForm = () => {
 onMounted(() => {
   getTreeSelect() // 初始化部门数据
   getList() // 初始化列表数据
+  proxy?.getConfigKey("sys.user.initPassword").then(response => {
+    initPassword.value = response.data;
+  });
 });
 </script>
 

+ 5 - 5
src/views/system/user/profile/index.vue

@@ -10,7 +10,7 @@
           </template>
           <div>
             <div class="text-center">
-              <userAvatar :user="state.user" />
+              <userAvatar/>
             </div>
             <ul class="list-group list-group-striped">
               <li class="list-group-item">
@@ -66,10 +66,10 @@
 </template>
 
 <script setup name="Profile" lang="ts">
-import userAvatar from "./userAvatar.vue";
-import userInfo from "./userInfo.vue";
-import resetPwd from "./resetPwd.vue";
-import thirdParty from "./thirdParty.vue";
+import UserAvatar from "./userAvatar.vue";
+import UserInfo from "./userInfo.vue";
+import ResetPwd from "./resetPwd.vue";
+import ThirdParty from "./thirdParty.vue";
 import { getAuthList } from "@/api/system/social/auth";
 import { getUserProfile } from "@/api/system/user";
 

+ 2 - 2
vite/plugins/index.ts

@@ -5,7 +5,7 @@ import createComponents from './components';
 import createIcons from './icons';
 import createSvgIconsPlugin from './svg-icon';
 import createCompression from './compression';
-import createVueSetupExtend from './vue-setup-extend';
+import createSetupExtend from './setup-extend';
 import path from 'path';
 
 export default (viteEnv: any, isBuild = false): [] => {
@@ -17,6 +17,6 @@ export default (viteEnv: any, isBuild = false): [] => {
   vitePlusgins.push(createCompression(viteEnv));
   vitePlusgins.push(createIcons());
   vitePlusgins.push(createSvgIconsPlugin(path, isBuild));
-  vitePlusgins.push(createVueSetupExtend());
+  vitePlusgins.push(createSetupExtend());
   return vitePlusgins;
 };

+ 5 - 0
vite/plugins/setup-extend.ts

@@ -0,0 +1,5 @@
+import setupExtend from 'unplugin-vue-setup-extend-plus/vite'
+
+export default () => {
+  return setupExtend({})
+};

+ 0 - 5
vite/plugins/vue-setup-extend.ts

@@ -1,5 +0,0 @@
-import VueSetupExtend from 'vite-plugin-vue-setup-extend';
-
-export default () => {
-  return VueSetupExtend();
-};