Merge branch 'dev' into v2.0
# Conflicts: # packages/scripts/package.json # pnpm-lock.yaml # src/views/system/dict/index.vue # src/views/system/menu/index.vue # src/views/system/notice/modules/notice-operate-drawer.vue # src/views/system/post/index.vue
This commit is contained in:
@@ -1,5 +1,68 @@
|
||||
# 更新日志
|
||||
|
||||
## [v1.2.0](https://gitee.com/xlsea/ruoyi-plus-soybean/compare/v1.1.3...v1.2.0) (2025-09-26)
|
||||
|
||||
### 🚀 新功能
|
||||
|
||||
- **components**:
|
||||
- 新增 umodoc 编辑器集成 - by @m-xlsea [<samp>(f182d)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/f182def5)
|
||||
- **projects**:
|
||||
- 重构登录页面样式 - by @m-xlsea [<samp>(8412a)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/8412a8db)
|
||||
- 路由兼容 activeMenu 选项 - by @m-xlsea [<samp>(25ee3)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/25ee3207)
|
||||
- 用户列表新增头像展示 - by @m-xlsea [<samp>(3146c)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/3146c039)
|
||||
- 新增岗位部门树接口 - by **AN** [<samp>(28101)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/28101cb2)
|
||||
- **styles**:
|
||||
- 优化左侧树形结构样式 - by @m-xlsea [<samp>(513dc)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/513dc31e)
|
||||
- **utils**:
|
||||
- 新增本地 Excel 导出工具类 - by @m-xlsea [<samp>(7f2f3)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/7f2f3bd0)
|
||||
|
||||
### 🐞 Bug 修复
|
||||
|
||||
- **components**:
|
||||
- 修复字典标签会修改字典数据值问题 - by @m-xlsea [<samp>(90a14)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/90a14e33)
|
||||
- **hooks**:
|
||||
- 修复下载 hooks 错误未处理 - by @m-xlsea [<samp>(5ef1c)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/5ef1c5de)
|
||||
- **packages**:
|
||||
- axios: fix json response. fixed #815 - by @soybeanjs in https://gitee.com/xlsea/ruoyi-plus-soybean/issues/815 [<samp>(fd087)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/fd087f59)
|
||||
- 修复tinymce层级问题 - by **AN** [<samp>(2c248)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/2c248d82)
|
||||
- **projects**:
|
||||
- 修改代码生成功能模块名为驼峰时,路由错误问题 - by **AN** [<samp>(2f794)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/2f794c4b)
|
||||
- 修复新增部门时不显示上级部门问题 - by **AN** [<samp>(d5bbc)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/d5bbc37d)
|
||||
- 修复菜单弹窗打开未清空默认值问题 - by @m-xlsea [<samp>(ad207)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/ad207255)
|
||||
- 修复退出登录未清空消息列表问题 - by @m-xlsea [<samp>(dc2fb)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/dc2fbbd5)
|
||||
- 修复菜单默认图标问题 - by @m-xlsea [<samp>(34ab7)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/34ab7d5d)
|
||||
- 修复消息通知字典值未处理问题 - by @m-xlsea [<samp>(3f148)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/3f148a4e)
|
||||
- 修复登录页面跳转问题 - by @m-xlsea [<samp>(8aeb7)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/8aeb7362)
|
||||
- 修复登录页面样式问题 - by @m-xlsea [<samp>(4e27f)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/4e27f3b5)
|
||||
- **types**:
|
||||
- fix proxy types - by @soybeanjs [<samp>(12b25)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/12b25e0d)
|
||||
- **utils**:
|
||||
- 修复请求工具响应解密问题 - by @m-xlsea [<samp>(9ef0b)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/9ef0bd41)
|
||||
|
||||
### 🛠 文档
|
||||
|
||||
- **components**: 补充国际化 - by **AN** [<samp>(ecad1)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/ecad1c3e)
|
||||
- **projects**: 字典状态使用枚举值 - by @m-xlsea [<samp>(56fd5)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/56fd5434)
|
||||
|
||||
### 📖 Documentation
|
||||
|
||||
- **other**: 更新 cursor 规则 - by @m-xlsea [<samp>(e623b)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/e623b560)
|
||||
|
||||
### 🏡 重构
|
||||
|
||||
- **deps**:
|
||||
- update deps - by @soybeanjs [<samp>(e33f9)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/e33f944a)
|
||||
- update deps - by @soybeanjs [<samp>(9fa95)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/9fa951aa)
|
||||
|
||||
### 🎨 样式
|
||||
|
||||
- **components**: 修改json预览组件样式问题 - by **AN** [<samp>(378aa)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/378aa869)
|
||||
- **styles**: 修复字体样式导致下划线不可见问题 - by **AN** [<samp>(4a424)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/4a4244b5)
|
||||
|
||||
### ❤️ 贡献者
|
||||
|
||||
[](https://github.com/m-xlsea) [](https://github.com/soybeanjs) [](https://gitee.com/elio-an)
|
||||
|
||||
## [v1.1.3](https://gitee.com/xlsea/ruoyi-plus-soybean/compare/v1.1.2...v1.1.3) (2025-08-16)
|
||||
|
||||
### 🐞 Bug 修复
|
||||
|
||||
Vendored
+7
-2
@@ -1,14 +1,19 @@
|
||||
import { request } from '@/service/request';
|
||||
#set($responseType = "")
|
||||
#if($tplCategory == "tree")
|
||||
#set($responseType = "Api.${ModuleName}.${BusinessName}[]")
|
||||
#else
|
||||
#set($responseType = "Api.${ModuleName}.${BusinessName}List")
|
||||
#end
|
||||
|
||||
/** 获取${functionName}列表 */
|
||||
export function fetchGet${BusinessName}List (params?: Api.${ModuleName}.${BusinessName}SearchParams) {
|
||||
return request<Api.${ModuleName}.${BusinessName}List>({
|
||||
return request<$responseType>({
|
||||
url: '/${moduleName}/${businessName}/list',
|
||||
method: 'get',
|
||||
params
|
||||
});
|
||||
}
|
||||
|
||||
/** 新增${functionName} */
|
||||
export function fetchCreate${BusinessName} (data: Api.${ModuleName}.${BusinessName}OperateParams) {
|
||||
return request<boolean>({
|
||||
|
||||
Vendored
+1
-1
@@ -195,7 +195,7 @@ function handleExport() {
|
||||
</template>
|
||||
<NDataTable
|
||||
v-model:checked-row-keys="checkedRowKeys"
|
||||
v-model::expanded-row-keys="expandedRowKeys"
|
||||
v-model:expanded-row-keys="expandedRowKeys"
|
||||
:columns="columns"
|
||||
:data="data"
|
||||
size="small"
|
||||
|
||||
+23
-21
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "ruoyi-vue-plus",
|
||||
"type": "module",
|
||||
"version": "1.1.3",
|
||||
"version": "1.2.0",
|
||||
"description": "结合了 RuoYi-Vue-Plus 的强大后端功能和 Soybean Admin 的现代化前端特性,为开发者提供了完整的企业管理解决方案。",
|
||||
"author": {
|
||||
"name": "xlsea",
|
||||
@@ -25,6 +25,7 @@
|
||||
"TypeScript",
|
||||
"naive-ui",
|
||||
"naive-ui-admin",
|
||||
"ant-design-vue v4",
|
||||
"UnoCSS"
|
||||
],
|
||||
"contributors": [
|
||||
@@ -66,61 +67,62 @@
|
||||
"@sa/tinymce": "workspace:*",
|
||||
"@sa/utils": "workspace:*",
|
||||
"@types/streamsaver": "^2.0.5",
|
||||
"@vueuse/core": "13.8.0",
|
||||
"@umoteam/editor": "^8.1.0",
|
||||
"@vueuse/core": "13.9.0",
|
||||
"clipboard": "2.0.11",
|
||||
"dayjs": "1.11.14",
|
||||
"dayjs": "1.11.18",
|
||||
"defu": "6.1.4",
|
||||
"echarts": "6.0.0",
|
||||
"highlight.js": "^11.11.1",
|
||||
"jsencrypt": "^3.3.2",
|
||||
"json5": "2.2.3",
|
||||
"monaco-editor": "^0.52.2",
|
||||
"naive-ui": "2.42.0",
|
||||
"naive-ui": "2.43.1",
|
||||
"nprogress": "0.2.0",
|
||||
"pinia": "3.0.3",
|
||||
"streamsaver": "^2.0.6",
|
||||
"tailwind-merge": "3.3.1",
|
||||
"vue": "3.5.20",
|
||||
"vue": "3.5.21",
|
||||
"vue-advanced-cropper": "^2.8.9",
|
||||
"vue-draggable-plus": "0.6.0",
|
||||
"vue-i18n": "11.1.11",
|
||||
"vue-i18n": "11.1.12",
|
||||
"vue-router": "4.5.1",
|
||||
"xlsx": "0.18.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@elegant-router/vue": "0.3.8",
|
||||
"@iconify/json": "2.2.378",
|
||||
"@iconify/json": "2.2.385",
|
||||
"@sa/scripts": "workspace:*",
|
||||
"@sa/uno-preset": "workspace:*",
|
||||
"@soybeanjs/eslint-config": "1.7.1",
|
||||
"@types/node": "24.3.0",
|
||||
"@types/node": "24.5.1",
|
||||
"@types/nprogress": "0.2.3",
|
||||
"@unocss/eslint-config": "66.4.2",
|
||||
"@unocss/preset-icons": "66.4.2",
|
||||
"@unocss/preset-uno": "66.4.2",
|
||||
"@unocss/transformer-directives": "66.4.2",
|
||||
"@unocss/transformer-variant-group": "66.4.2",
|
||||
"@unocss/vite": "66.4.2",
|
||||
"@unocss/eslint-config": "66.5.1",
|
||||
"@unocss/preset-icons": "66.5.1",
|
||||
"@unocss/preset-uno": "66.5.1",
|
||||
"@unocss/transformer-directives": "66.5.1",
|
||||
"@unocss/transformer-variant-group": "66.5.1",
|
||||
"@unocss/vite": "66.5.1",
|
||||
"@vitejs/plugin-vue": "6.0.1",
|
||||
"@vitejs/plugin-vue-jsx": "5.1.0",
|
||||
"@vitejs/plugin-vue-jsx": "5.1.1",
|
||||
"consola": "3.4.2",
|
||||
"eslint": "9.34.0",
|
||||
"eslint": "9.35.0",
|
||||
"eslint-plugin-vue": "10.4.0",
|
||||
"kolorist": "1.8.0",
|
||||
"sass": "1.91.0",
|
||||
"sass": "1.92.1",
|
||||
"simple-git-hooks": "2.13.1",
|
||||
"tsx": "4.20.5",
|
||||
"typescript": "5.9.2",
|
||||
"unplugin-icons": "22.2.0",
|
||||
"unplugin-icons": "22.3.0",
|
||||
"unplugin-vue-components": "29.0.0",
|
||||
"vite": "7.1.3",
|
||||
"vite": "7.1.5",
|
||||
"vite-plugin-monaco-editor": "^1.1.0",
|
||||
"vite-plugin-progress": "0.0.7",
|
||||
"vite-plugin-static-copy": "^3.1.0",
|
||||
"vite-plugin-svg-icons": "2.0.1",
|
||||
"vite-plugin-vue-devtools": "8.0.1",
|
||||
"vite-plugin-vue-devtools": "8.0.2",
|
||||
"vue-eslint-parser": "10.2.0",
|
||||
"vue-tsc": "3.0.6"
|
||||
"vue-tsc": "3.0.7"
|
||||
},
|
||||
"simple-git-hooks": {
|
||||
"commit-msg": "pnpm sa git-commit-verify",
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@sa/utils": "workspace:*",
|
||||
"axios": "1.11.0",
|
||||
"axios": "1.12.2",
|
||||
"axios-retry": "4.5.0",
|
||||
"qs": "6.14.0"
|
||||
},
|
||||
|
||||
@@ -3,6 +3,7 @@ import type { AxiosResponse, CreateAxiosDefaults, InternalAxiosRequestConfig } f
|
||||
import axiosRetry from 'axios-retry';
|
||||
import { nanoid } from '@sa/utils';
|
||||
import { createAxiosConfig, createDefaultOptions, createRetryOptions } from './options';
|
||||
import { transformResponse } from './shared';
|
||||
import { BACKEND_ERROR_CODE, REQUEST_CANCELED_CODE, REQUEST_ID_KEY } from './constant';
|
||||
import type {
|
||||
CustomAxiosRequestConfig,
|
||||
@@ -53,6 +54,8 @@ function createCommonRequest<
|
||||
async response => {
|
||||
const responseType: ResponseType = (response.config?.responseType as ResponseType) || 'json';
|
||||
|
||||
await transformResponse(response);
|
||||
|
||||
if (responseType !== 'json' || opts.isBackendSuccess(response)) {
|
||||
return Promise.resolve(response);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { AxiosHeaderValue, AxiosResponse, InternalAxiosRequestConfig } from 'axios';
|
||||
import type { ResponseType } from './type';
|
||||
|
||||
export function getContentType(config: InternalAxiosRequestConfig) {
|
||||
const contentType: AxiosHeaderValue = config.headers?.['Content-Type'] || 'application/json';
|
||||
@@ -26,3 +27,53 @@ export function isResponseJson(response: AxiosResponse) {
|
||||
|
||||
return responseType === 'json' || responseType === undefined;
|
||||
}
|
||||
|
||||
export async function transformResponse(response: AxiosResponse) {
|
||||
const responseType: ResponseType = (response.config?.responseType as ResponseType) || 'json';
|
||||
if (responseType === 'json') return;
|
||||
|
||||
const isJson = response.headers['content-type']?.includes('application/json');
|
||||
if (!isJson) return;
|
||||
|
||||
if (responseType === 'blob') {
|
||||
await transformBlobToJson(response);
|
||||
}
|
||||
|
||||
if (responseType === 'arrayBuffer') {
|
||||
await transformArrayBufferToJson(response);
|
||||
}
|
||||
}
|
||||
|
||||
export async function transformBlobToJson(response: AxiosResponse) {
|
||||
try {
|
||||
let data = response.data;
|
||||
|
||||
if (typeof data === 'string') {
|
||||
data = JSON.parse(data);
|
||||
}
|
||||
|
||||
if (Object.prototype.toString.call(data) === '[object Blob]') {
|
||||
const json = await data.text();
|
||||
data = JSON.parse(json);
|
||||
}
|
||||
|
||||
response.data = data;
|
||||
} catch {}
|
||||
}
|
||||
|
||||
export async function transformArrayBufferToJson(response: AxiosResponse) {
|
||||
try {
|
||||
let data = response.data;
|
||||
|
||||
if (typeof data === 'string') {
|
||||
data = JSON.parse(data);
|
||||
}
|
||||
|
||||
if (Object.prototype.toString.call(data) === '[object ArrayBuffer]') {
|
||||
const json = new TextDecoder().decode(data);
|
||||
data = JSON.parse(json);
|
||||
}
|
||||
|
||||
response.data = data;
|
||||
} catch {}
|
||||
}
|
||||
|
||||
@@ -220,7 +220,7 @@ const events = computed(() => {
|
||||
<style lang="scss">
|
||||
.tox.tox-silver-sink.tox-tinymce-aux {
|
||||
/** 该样式默认为1300的zIndex */
|
||||
z-index: 2025;
|
||||
z-index: 2025 !important;
|
||||
}
|
||||
|
||||
.app-tinymce {
|
||||
|
||||
@@ -66,6 +66,24 @@ function renderLabel({ option }: { option: TreeOption }) {
|
||||
if (label?.startsWith('route.') || label?.startsWith('menu.')) {
|
||||
label = $t(label as App.I18n.I18nKey);
|
||||
}
|
||||
// 禁用的菜单显示红色
|
||||
if (option.status === '1') {
|
||||
return (
|
||||
<div class="flex items-center gap-4px text-error-200">
|
||||
{label}
|
||||
<SvgIcon icon="ri:prohibited-line" class="text-16px" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
// 隐藏的菜单显示灰色
|
||||
if (option.visible === '1') {
|
||||
return (
|
||||
<div class="flex items-center gap-4px text-gray-400">
|
||||
{label}
|
||||
<SvgIcon icon="codex:hidden" class="text-21px" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return <div>{label}</div>;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
<script lang="ts" setup>
|
||||
import { nextTick, ref, useAttrs, watch } from 'vue';
|
||||
import type { UmoEditorOptions } from '@umoteam/editor';
|
||||
import { UmoEditor } from '@umoteam/editor';
|
||||
import { fetchBatchDeleteOss, fetchUploadFile } from '@/service/api/system/oss';
|
||||
import { useAppStore } from '@/store/modules/app';
|
||||
import { useThemeStore } from '@/store/modules/theme';
|
||||
|
||||
defineOptions({
|
||||
name: 'UmoDocEditor'
|
||||
});
|
||||
|
||||
const attrs: UmoEditorOptions = useAttrs();
|
||||
const appStore = useAppStore();
|
||||
const themeStore = useThemeStore();
|
||||
const umoEditorRef = ref<InstanceType<typeof UmoEditor>>();
|
||||
const isSave = ref(false);
|
||||
|
||||
const value = defineModel<string>('value', { required: true, default: '' });
|
||||
|
||||
watch(
|
||||
value,
|
||||
() => {
|
||||
nextTick(() => {
|
||||
if (isSave.value) {
|
||||
isSave.value = false;
|
||||
return;
|
||||
}
|
||||
umoEditorRef.value?.setContent(value.value);
|
||||
});
|
||||
},
|
||||
{
|
||||
immediate: true
|
||||
}
|
||||
);
|
||||
|
||||
watch(
|
||||
() => appStore.locale,
|
||||
() => {
|
||||
umoEditorRef.value?.setLocale(appStore.locale);
|
||||
}
|
||||
);
|
||||
|
||||
async function handleSave(content: { html: string }) {
|
||||
isSave.value = true;
|
||||
value.value = content.html;
|
||||
return true;
|
||||
}
|
||||
|
||||
async function handleFileUpload(file: File) {
|
||||
const { error, data } = await fetchUploadFile(file);
|
||||
if (error) throw new Error(error.message || '上传失败');
|
||||
|
||||
return {
|
||||
id: data.ossId,
|
||||
url: data.url
|
||||
};
|
||||
}
|
||||
|
||||
function handleFileDelete(id: CommonType.IdType) {
|
||||
window.$dialog?.warning({
|
||||
title: '确认删除文件?',
|
||||
content: '文件删除后不可恢复,请确认是否删除!',
|
||||
positiveText: '确认删除',
|
||||
negativeText: '取消',
|
||||
onPositiveClick: async () => {
|
||||
const { error } = await fetchBatchDeleteOss([id]);
|
||||
if (error) throw new Error(error.message || '文件删除失败');
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
saveContent: () => umoEditorRef.value?.saveContent()
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="umo-editor size-full">
|
||||
<UmoEditor
|
||||
v-bind="attrs"
|
||||
ref="umoEditorRef"
|
||||
:theme="themeStore.darkMode ? 'dark' : 'light'"
|
||||
@save="handleSave"
|
||||
@file-upload="handleFileUpload"
|
||||
@file-delete="handleFileDelete"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
body .flex-center {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.umo-editor .flex-center {
|
||||
display: inherit !important;
|
||||
align-items: inherit !important;
|
||||
justify-content: inherit !important;
|
||||
}
|
||||
</style>
|
||||
@@ -24,3 +24,17 @@ export function fetchGetOssListByIds(ossIds: CommonType.IdType[]) {
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
|
||||
// 上传文件
|
||||
export function fetchUploadFile(file: File) {
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
return request<Api.System.Oss>({
|
||||
url: '/resource/oss/upload',
|
||||
method: 'post',
|
||||
data: formData,
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data'
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -43,3 +43,10 @@ export function fetchGetPostSelect(deptId?: CommonType.IdType, postIds?: CommonT
|
||||
params: { postIds, deptId }
|
||||
});
|
||||
}
|
||||
/** 获取部门选择框列表 */
|
||||
export function fetchGetPostDeptSelect() {
|
||||
return request<Api.Common.CommonTreeRecord>({
|
||||
url: '/system/post/deptTree',
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
|
||||
Vendored
+2
@@ -129,6 +129,8 @@ declare module 'vue' {
|
||||
TenantSelect: typeof import('./../components/custom/tenant-select.vue')['default']
|
||||
ThemeSchemaSwitch: typeof import('./../components/common/theme-schema-switch.vue')['default']
|
||||
TinymceEditor: typeof import('./../components/custom/tinymce-editor.vue')['default']
|
||||
UmoDocEditor: typeof import('./../components/custom/umo-doc-editor.vue')['default']
|
||||
UmoEditor: typeof import('./../components/custom/umo-editor.vue')['default']
|
||||
UserSelect: typeof import('./../components/custom/user-select.vue')['default']
|
||||
WaveBg: typeof import('./../components/custom/wave-bg.vue')['default']
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user