Ver código fonte

!263 发布 v5.6.1-v2.6.1 版本 依赖升级漏洞修复

Merge pull request !263 from 疯狂的狮子Li/dev
疯狂的狮子Li 2 meses atrás
pai
commit
9fd2b6f137

+ 2 - 2
package.json

@@ -1,7 +1,7 @@
 {
   "$schema": "https://json.schemastore.org/package",
   "name": "ruoyi-vue-plus",
-  "version": "5.6.0-2.6.0",
+  "version": "5.6.1-2.6.1",
   "description": "RuoYi-Vue-Plus多租户管理系统",
   "author": "LionLi",
   "license": "MIT",
@@ -73,7 +73,7 @@
     "unplugin-icons": "23.0.1",
     "unplugin-vue-components": "31.0.0",
     "unplugin-vue-setup-extend-plus": "1.0.1",
-    "vite": "7.3.1",
+    "vite": "7.3.2",
     "vite-plugin-svg-icons-ng": "^1.5.2",
     "vite-plugin-vue-devtools": "8.0.7",
     "vitest": "4.0.18",

+ 5 - 5
src/assets/styles/element-ui.scss

@@ -74,7 +74,7 @@
 
     .el-dialog {
       margin: 0 auto !important;
-      border-radius: var(--app-radius-lg);
+      border-radius: var(--app-radius-base);
       box-shadow: var(--app-shadow-md);
       overflow: hidden;
 
@@ -158,7 +158,7 @@
 }
 
 .el-message-box {
-  border-radius: var(--app-radius-lg);
+  border-radius: var(--app-radius-base);
   box-shadow: var(--app-shadow-md);
 }
 
@@ -192,7 +192,7 @@
 
 // Buttons
 .el-button {
-  border-radius: var(--app-radius-md);
+  border-radius: var(--el-border-radius-base);
   transition: transform 0.15s ease, box-shadow 0.2s ease, background-color 0.2s ease, border-color 0.2s ease;
 }
 
@@ -215,7 +215,7 @@
 }
 
 .el-drawer {
-  border-radius: var(--app-radius-lg);
+  border-radius: var(--app-radius-base);
   box-shadow: var(--app-shadow-md);
 }
 
@@ -227,7 +227,7 @@
 
 // Table polish
 .el-table {
-  border-radius: var(--app-radius-lg);
+  border-radius: var(--app-radius-base);
   overflow: hidden;
   box-shadow: var(--app-shadow-sm);
   border: 1px solid var(--el-border-color-lighter);

+ 18 - 19
src/assets/styles/index.scss

@@ -115,7 +115,7 @@ div:focus {
 }
 
 aside {
-  background: #eef1f6;
+  background: var(--el-fill-color-light);
   padding: 8px 24px;
   margin-bottom: 20px;
   border-radius: var(--app-radius-md);
@@ -131,44 +131,43 @@ aside {
     'Hiragino Sans GB',
     'Microsoft YaHei',
     sans-serif;
-  color: #2c3e50;
+  color: var(--el-text-color-primary);
   -webkit-font-smoothing: antialiased;
   -moz-osx-font-smoothing: grayscale;
 
   a {
-    color: #337ab7;
+    color: var(--el-color-primary);
     cursor: pointer;
 
     &:hover {
-      color: rgb(32, 160, 255);
+      color: var(--el-color-primary-light-3);
     }
   }
 }
 
 //main-container全局样式
 .app-container {
-  padding: 20px;
-  background: var(--app-surface-bg);
-  border: 1px solid var(--app-surface-border);
-  border-radius: var(--app-radius-lg);
-  box-shadow: var(--app-shadow-sm);
+  padding: 16px;
+  background: transparent;
+  border: none;
+  border-radius: 0;
+  box-shadow: none;
 }
 
 // search面板样式
 .panel,
 .search {
-  margin-bottom: 0.75rem;
-  border-radius: var(--app-radius-lg);
-  border: 1px solid var(--el-border-color-light);
-  background-color: var(--el-bg-color-overlay);
-  padding: 0.75rem;
-  box-shadow: 0 0 0 rgba(0, 0, 0, 0);
-  transition: box-shadow 0.2s ease, transform 0.2s ease, border-color 0.2s ease;
+  margin-bottom: 12px;
+  border-radius: var(--app-radius-base);
+  border: 1px solid var(--el-border-color-lighter);
+  padding: 16px;
+  background: var(--app-surface-bg);
+  box-shadow: var(--app-shadow-sm);
+  transition: box-shadow 0.2s ease, border-color 0.2s ease;
 
   &:hover {
-    box-shadow: var(--app-shadow-sm);
-    border-color: var(--el-border-color);
-    transform: translateY(-1px);
+    box-shadow: var(--app-shadow-md);
+    border-color: var(--el-border-color-light);
   }
 }
 

+ 4 - 5
src/assets/styles/ruoyi.scss

@@ -77,7 +77,7 @@ h6 {
   font-weight: 600;
 }
 .el-dialog:not(.is-fullscreen) {
-  margin-top: 6vh !important;
+  margin-top: 0 !important;
 }
 
 .el-dialog.scrollbar .el-dialog__body {
@@ -125,8 +125,8 @@ h6 {
 /* tree border */
 .tree-border {
   margin-top: 5px;
-  border: 1px solid #e5e6e7;
-  background: #ffffff none;
+  border: 1px solid var(--el-border-color-light);
+  background: var(--el-bg-color);
   border-radius: var(--app-radius-md);
   width: 100%;
 }
@@ -203,12 +203,11 @@ h6 {
   box-shadow: var(--app-shadow-sm);
   border-color: var(--el-border-color-lighter);
   overflow: hidden;
-  transition: box-shadow 0.2s ease, transform 0.2s ease;
+  transition: box-shadow 0.2s ease;
 }
 
 .el-card:hover {
   box-shadow: var(--app-shadow-md);
-  transform: translateY(-1px);
 }
 
 .card-box {

+ 6 - 4
src/assets/styles/variables.module.scss

@@ -27,9 +27,11 @@
   --tags-view-active-border-color: var(--el-color-primary);
 
   // Modern rounded style + soft shadows
-  --app-radius-sm: 6px;
-  --app-radius-md: 10px;
-  --app-radius-lg: 14px;
+  --app-radius-base: 8px;
+  --app-radius-sm: calc(var(--app-radius-base) * 0.6);
+  --app-radius-md: var(--app-radius-base);
+  --app-radius-lg: calc(var(--app-radius-base) * 1.4);
+  --app-radius-lg: var(--app-radius-base);
   --app-shadow-sm: 0 1px 2px rgba(15, 23, 42, 0.08), 0 6px 16px rgba(15, 23, 42, 0.08);
   --app-shadow-md: 0 8px 24px rgba(15, 23, 42, 0.12);
   --app-shadow-lg: 0 12px 32px rgba(15, 23, 42, 0.16);
@@ -52,7 +54,7 @@ html.dark {
   --menuHover: #171819;
 
   --subMenuBg: #1d1e1f;
-  --subMenuActiveText: #1d1e1f;
+  --subMenuActiveText: #f4f4f5;
   --subMenuHover: #171819;
   --subMenuTitleHover: #171819;
 

+ 34 - 41
src/components/Editor/index.vue

@@ -1,13 +1,15 @@
 <template>
   <div>
     <el-upload
-      v-if="type"
-      action=""
+      v-if="type === 'url'"
+      :action="upload.url"
       :before-upload="handleBeforeUpload"
-      :http-request="handleUploadRequest"
+      :on-success="handleUploadSuccess"
+      :on-error="handleUploadError"
       class="editor-img-uploader"
       name="file"
       :show-file-list="false"
+      :headers="upload.headers"
     >
       <i ref="uploadRef"></i>
     </el-upload>
@@ -29,7 +31,7 @@ import '@vueup/vue-quill/dist/vue-quill.snow.css';
 
 import { QuillEditor, Quill } from '@vueup/vue-quill';
 import { propTypes } from '@/utils/propTypes';
-import type { UploadRequestHandler, UploadRequestOptions } from 'element-plus';
+import { globalHeaders } from '@/utils/request';
 
 defineEmits(['update:modelValue']);
 
@@ -45,11 +47,15 @@ const props = defineProps({
   /* 上传文件大小限制(MB) */
   fileSize: propTypes.number.def(5),
   /* 类型(base64格式、url格式) */
-  type: propTypes.string.def('base64')
+  type: propTypes.string.def('url')
 });
 
 const { proxy } = getCurrentInstance() as ComponentInternalInstance;
 
+const upload = reactive<UploadOption>({
+  headers: globalHeaders(),
+  url: import.meta.env.VITE_APP_BASE_API + '/resource/oss/upload'
+});
 const quillEditorRef = ref();
 const uploadRef = ref<HTMLDivElement>();
 
@@ -110,9 +116,28 @@ watch(
   { immediate: true }
 );
 
+// 图片上传成功返回图片地址
+const handleUploadSuccess = (res: any) => {
+  // 如果上传成功
+  if (res.code === 200) {
+    // 获取富文本实例
+    const quill = toRaw(quillEditorRef.value).getQuill();
+    // 获取光标位置
+    const length = quill.selection.savedRange.index;
+    // 插入图片,res为服务器返回的图片链接地址
+    quill.insertEmbed(length, 'image', res.data.url);
+    // 调整光标到最后
+    quill.setSelection(length + 1);
+    proxy?.$modal.closeLoading();
+  } else {
+    proxy?.$modal.msgError('图片插入失败');
+    proxy?.$modal.closeLoading();
+  }
+};
+
 // 图片上传前拦截
 const handleBeforeUpload = (file: any) => {
-  const type = ['image/jpeg', 'image/jpg', 'image/png', 'image/svg', 'image/svg+xml'];
+  const type = ['image/jpeg', 'image/jpg', 'image/png', 'image/svg'];
   const isJPG = type.includes(file.type);
   //检验文件格式
   if (!isJPG) {
@@ -131,41 +156,9 @@ const handleBeforeUpload = (file: any) => {
   return true;
 };
 
-// base64 模式插入图片
-const handleUploadRequest: UploadRequestHandler = (options: UploadRequestOptions) => {
-  return new Promise<void>((resolve, reject) => {
-    const file = options.file as File;
-    const quill = toRaw(quillEditorRef.value)?.getQuill();
-    if (!quill) {
-      proxy?.$modal.msgError('编辑器未就绪');
-      proxy?.$modal.closeLoading();
-      reject(new Error('editor not ready'));
-      return;
-    }
-    const reader = new FileReader();
-    reader.onload = () => {
-      const base64 = reader.result as string;
-      const range = quill.selection?.savedRange;
-      const length = range ? range.index : quill.getLength();
-      quill.insertEmbed(length, 'image', base64);
-      quill.setSelection(length + 1);
-      proxy?.$modal.closeLoading();
-      options.onSuccess?.({ url: base64 });
-      resolve();
-    };
-    reader.onerror = () => {
-      proxy?.$modal.msgError('图片插入失败');
-      proxy?.$modal.closeLoading();
-      const err = Object.assign(new Error('read image failed'), {
-        status: 0,
-        method: 'POST',
-        url: options.action || ''
-      });
-      options.onError?.(err as any);
-      reject(err);
-    };
-    reader.readAsDataURL(file);
-  });
+// 图片失败拦截
+const handleUploadError = (err: any) => {
+  proxy?.$modal.msgError('上传文件失败');
 };
 </script>
 

+ 14 - 12
src/layout/components/Navbar.vue

@@ -14,7 +14,7 @@
         <el-select
           v-if="userId === 1 && tenantEnabled"
           v-model="companyName"
-          class="min-w-244px"
+          class="min-w-244px mr-2"
           clearable
           filterable
           reserve-keyword
@@ -34,11 +34,11 @@
         </el-tooltip>
         <!-- 消息 -->
         <el-tooltip :content="proxy.$t('navbar.message')" effect="dark" placement="bottom">
-          <div>
+          <div style="display:flex;align-items:center">
             <el-popover placement="bottom" trigger="click" transition="el-zoom-in-top" :width="300" :persistent="false">
               <template #reference>
                 <el-badge :value="newNotice > 0 ? newNotice : ''" :max="99">
-                  <div class="right-menu-item hover-effect" style="display: block"><svg-icon icon-class="message" /></div>
+                  <div class="right-menu-item hover-effect"><svg-icon icon-class="message" /></div>
                 </el-badge>
               </template>
               <template #default>
@@ -289,9 +289,7 @@ watch(
   }
 
   .right-menu {
-    //float: right;
     height: 100%;
-    line-height: 50px;
     display: flex;
     align-items: center;
     margin-left: auto;
@@ -301,19 +299,22 @@ watch(
     }
 
     .right-menu-item {
-      display: inline-block;
+      display: inline-flex;
+      align-items: center;
+      justify-content: center;
       padding: 0 8px;
-      height: 100%;
+      height: 32px;
       font-size: 18px;
       color: var(--el-text-color-regular);
-      vertical-align: text-bottom;
+      border-radius: var(--app-radius-md);
 
       &.hover-effect {
         cursor: pointer;
-        transition: background 0.3s;
+        transition: background 0.2s ease, color 0.2s ease;
 
         &:hover {
-          background: var(--el-fill-color-lighter);
+          background: var(--el-fill-color-light);
+          color: var(--el-color-primary);
         }
       }
     }
@@ -322,7 +323,7 @@ watch(
       margin-right: 40px;
 
       .avatar-wrapper {
-        margin-top: 5px;
+        margin-top: 0;
         position: relative;
 
         .user-avatar {
@@ -330,7 +331,8 @@ watch(
           width: 40px;
           height: 40px;
           border-radius: var(--app-radius-md);
-          margin-top: 10px;
+          margin-top: 0;
+          display: block;
         }
 
         i {

+ 22 - 0
src/layout/components/Settings/index.vue

@@ -75,6 +75,12 @@
         <el-switch v-model="isDark" class="drawer-switch" @change="toggleDark" />
       </span>
     </div>
+    <div class="drawer-item">
+      <span>页面圆角</span>
+      <span class="comp-style">
+        <el-slider v-model="radiusBase" :min="0" :max="32" :step="2" style="width: 120px" @change="radiusBaseChange" />
+      </span>
+    </div>
 
     <el-divider />
 
@@ -143,6 +149,7 @@ const sideTheme = ref(settingsStore.sideTheme);
 const storeSettings = computed(() => settingsStore);
 const predefineColors = ref(['#409EFF', '#ff4500', '#ff8c00', '#ffd700', '#90ee90', '#00ced1', '#1e90ff', '#c71585']);
 const navType = ref(settingsStore.navType);
+const radiusBase = ref(settingsStore.radiusBase);
 // 是否暗黑模式
 const isDark = useDark({
   storageKey: 'useDarkKey',
@@ -190,6 +197,16 @@ const themeChange = (val: string) => {
   settingsStore.theme = val;
   handleThemeStyle(val);
 };
+const radiusBaseChange = (val: number) => {
+  settingsStore.radiusBase = val;
+  const el = document.documentElement;
+  el.style.setProperty('--app-radius-base', `${val}px`);
+  el.style.setProperty('--app-radius-sm', `${Math.round(val * 0.6)}px`);
+  el.style.setProperty('--app-radius-md', `${val}px`);
+  el.style.setProperty('--app-radius-lg', `${Math.round(val * 1.4)}px`);
+  el.style.setProperty('--el-border-radius-base', `${val}px`);
+  el.style.setProperty('--el-border-radius-small', `${Math.round(val * 0.6)}px`);
+};
 const handleTheme = (val: string) => {
   sideTheme.value = val;
   if (isDark.value && val === SideThemeEnum.LIGHT) {
@@ -210,6 +227,7 @@ const saveSetting = () => {
   settings.value.sideTheme = storeSettings.value.sideTheme;
   settings.value.theme = storeSettings.value.theme;
   settings.value.navType = storeSettings.value.navType;
+  settings.value.radiusBase = storeSettings.value.radiusBase;
   setTimeout(() => {
     proxy?.$modal.closeLoading();
   }, 1000);
@@ -223,6 +241,10 @@ const openSetting = () => {
   showSettings.value = true;
 };
 
+onMounted(() => {
+  radiusBaseChange(storeSettings.value.radiusBase);
+});
+
 defineExpose({
   openSetting
 });

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

@@ -243,9 +243,9 @@ onMounted(() => {
   height: 34px;
   width: 100%;
   background-color: var(--el-bg-color);
-  border: 1px solid var(--el-border-color-light);
-  border-radius: var(--app-radius-md);
-  box-shadow: var(--app-shadow-sm);
+  border-top: none;
+  border-bottom: 1px solid var(--el-border-color-lighter);
+  box-shadow: none;
   .tags-view-wrapper {
     .tags-view-item {
       display: inline-block;
@@ -275,12 +275,12 @@ onMounted(() => {
         margin-right: 15px;
       }
       &.active {
-        background-color: #42b983;
+        background-color: var(--tags-view-active-bg);
         color: #fff;
-        border-color: #42b983;
+        border-color: var(--tags-view-active-border-color);
         &::before {
           content: '';
-          background: #fff;
+          background: rgba(255, 255, 255, 0.7);
           display: inline-block;
           width: 8px;
           height: 8px;

+ 21 - 18
src/layout/components/TopBar/search.vue

@@ -1,10 +1,11 @@
 <template>
   <div class="layout-search-dialog">
-    <el-dialog v-model="state.isShowSearch" destroy-on-close :show-close="false">
-      <template #footer>
+    <el-dialog v-model="state.isShowSearch" destroy-on-close :show-close="false" width="600px" top="12vh">
+      <div class="layout-search-dialog__content">
         <el-autocomplete
           ref="layoutMenuAutocompleteRef"
           v-model="state.menuQuery"
+          class="layout-search-dialog__autocomplete"
           :fetch-suggestions="menuSearch"
           placeholder="搜索"
           :fit-input-width="true"
@@ -20,7 +21,7 @@
             </div>
           </template>
         </el-autocomplete>
-      </template>
+      </div>
     </el-dialog>
   </div>
 </template>
@@ -132,27 +133,29 @@ defineExpose({
 
 <style lang="scss" scoped>
 .layout-search-dialog {
-  position: relative;
   :deep(.el-dialog) {
-    padding: 0;
+    border-radius: var(--app-radius-base);
+    overflow: visible;
     .el-dialog__header,
-    .el-dialog__body {
+    .el-dialog__footer {
       display: none;
     }
-    .el-dialog__footer {
-      width: 100%;
-      position: absolute;
-      left: 50%;
-      transform: translateX(-50%);
-      top: -53vh;
+    .el-dialog__body {
+      padding: 20px;
     }
   }
-  :deep(.el-autocomplete) {
-    width: 560px;
-    position: absolute;
-    top: 150px;
-    left: 50%;
-    transform: translateX(-50%);
+
+  &__content {
+    width: 100%;
+  }
+
+  &__autocomplete {
+    width: 100%;
+  }
+
+  :deep(.el-input__wrapper) {
+    min-height: 44px;
+    border-radius: var(--app-radius-md);
   }
 }
 </style>

+ 1 - 0
src/layout/index.vue

@@ -127,6 +127,7 @@ const setLayout = () => {
   width: calc(100% - #{$base-sidebar-width});
   transition: width 0.28s;
   background: $fixed-header-bg;
+  box-shadow: 0 2px 8px rgba(0, 21, 41, 0.10);
 }
 
 .hideSidebar .fixed-header {

+ 6 - 1
src/settings.ts

@@ -71,6 +71,11 @@ const setting: DefaultSettings = {
   /**
    * 默认布局
    */
-  layout: ''
+  layout: '',
+
+  /**
+   * 页面圆角大小
+   */
+  radiusBase: 8
 };
 export default setting;

+ 4 - 1
src/store/modules/settings.ts

@@ -15,7 +15,8 @@ export const useSettingsStore = defineStore('setting', () => {
     dynamicTitle: defaultSettings.dynamicTitle,
     sideTheme: defaultSettings.sideTheme,
     theme: defaultSettings.theme,
-    navType: defaultSettings.navType
+    navType: defaultSettings.navType,
+    radiusBase: defaultSettings.radiusBase
   });
   const title = ref<string>(defaultSettings.title);
   const theme = ref<string>(storageSetting.value.theme);
@@ -29,6 +30,7 @@ export const useSettingsStore = defineStore('setting', () => {
   const animationEnable = ref<boolean>(defaultSettings.animationEnable);
   const dark = ref<boolean>(defaultSettings.dark);
   const navType = ref<NavTypeEnum>(storageSetting.value.navType || NavTypeEnum.LEFT);
+  const radiusBase = ref<number>(storageSetting.value.radiusBase ?? defaultSettings.radiusBase);
 
   const setTitle = (value: string) => {
     title.value = value;
@@ -47,6 +49,7 @@ export const useSettingsStore = defineStore('setting', () => {
     animationEnable,
     dark,
     navType,
+    radiusBase,
     setTitle
   };
 });

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

@@ -123,6 +123,10 @@ declare global {
      * 主题模式
      */
     theme: string;
+    /**
+     * 页面圆角大小
+     */
+    radiusBase: number;
   }
 
   declare interface DefaultSettings extends LayoutSetting {

+ 2 - 2
src/views/index.vue

@@ -33,7 +33,7 @@
           * 部署方式 Docker 容器编排 一键部署业务集群<br />
           * 国际化 SpringMessage Spring标准国际化方案<br />
         </p>
-        <p><b>当前版本:</b> <span>v5.6.0</span></p>
+        <p><b>当前版本:</b> <span>v5.6.1</span></p>
         <p>
           <el-tag type="danger">&yen;免费开源</el-tag>
         </p>
@@ -77,7 +77,7 @@
           * 分布式监控 Prometheus、Grafana 全方位性能监控<br />
           * 其余与 Vue 版本一致<br />
         </p>
-        <p><b>当前版本:</b> <span>v2.6.0</span></p>
+        <p><b>当前版本:</b> <span>v2.6.1</span></p>
         <p>
           <el-tag type="danger">&yen;免费开源</el-tag>
         </p>

+ 3 - 1
vite/plugins/auto-import.ts

@@ -12,7 +12,9 @@ export default (path: any) => {
     },
     resolvers: [
       // 自动导入 Element Plus 相关函数ElMessage, ElMessageBox... (带样式)
-      ElementPlusResolver()
+      ElementPlusResolver({
+        importStyle: false
+      })
     ],
     vueTemplate: true, // 是否在 vue 模板中自动导入
     dts: path.resolve(path.resolve(__dirname, '../../src'), 'types', 'auto-imports.d.ts')

+ 3 - 1
vite/plugins/components.ts

@@ -6,7 +6,9 @@ export default (path: any) => {
   return Components({
     resolvers: [
       // 自动导入 Element Plus 组件
-      ElementPlusResolver(),
+      ElementPlusResolver({
+        importStyle: false
+      }),
       // 自动注册图标组件
       IconsResolver({
         enabledCollections: ['ep']