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:
xlsea
2025-10-14 17:20:50 +08:00
13 changed files with 295 additions and 26 deletions
+63
View File
@@ -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 编辑器集成 &nbsp;-&nbsp; by @m-xlsea [<samp>(f182d)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/f182def5)
- **projects**:
- 重构登录页面样式 &nbsp;-&nbsp; by @m-xlsea [<samp>(8412a)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/8412a8db)
- 路由兼容 activeMenu 选项 &nbsp;-&nbsp; by @m-xlsea [<samp>(25ee3)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/25ee3207)
- 用户列表新增头像展示 &nbsp;-&nbsp; by @m-xlsea [<samp>(3146c)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/3146c039)
- 新增岗位部门树接口 &nbsp;-&nbsp; by **AN** [<samp>(28101)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/28101cb2)
- **styles**:
- 优化左侧树形结构样式 &nbsp;-&nbsp; by @m-xlsea [<samp>(513dc)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/513dc31e)
- **utils**:
- 新增本地 Excel 导出工具类 &nbsp;-&nbsp; by @m-xlsea [<samp>(7f2f3)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/7f2f3bd0)
### &nbsp;&nbsp;&nbsp;🐞 Bug 修复
- **components**:
- 修复字典标签会修改字典数据值问题 &nbsp;-&nbsp; by @m-xlsea [<samp>(90a14)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/90a14e33)
- **hooks**:
- 修复下载 hooks 错误未处理 &nbsp;-&nbsp; by @m-xlsea [<samp>(5ef1c)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/5ef1c5de)
- **packages**:
- axios: fix json response. fixed #815 &nbsp;-&nbsp; 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层级问题 &nbsp;-&nbsp; by **AN** [<samp>(2c248)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/2c248d82)
- **projects**:
- 修改代码生成功能模块名为驼峰时,路由错误问题 &nbsp;-&nbsp; by **AN** [<samp>(2f794)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/2f794c4b)
- 修复新增部门时不显示上级部门问题 &nbsp;-&nbsp; by **AN** [<samp>(d5bbc)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/d5bbc37d)
- 修复菜单弹窗打开未清空默认值问题 &nbsp;-&nbsp; by @m-xlsea [<samp>(ad207)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/ad207255)
- 修复退出登录未清空消息列表问题 &nbsp;-&nbsp; by @m-xlsea [<samp>(dc2fb)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/dc2fbbd5)
- 修复菜单默认图标问题 &nbsp;-&nbsp; by @m-xlsea [<samp>(34ab7)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/34ab7d5d)
- 修复消息通知字典值未处理问题 &nbsp;-&nbsp; by @m-xlsea [<samp>(3f148)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/3f148a4e)
- 修复登录页面跳转问题 &nbsp;-&nbsp; by @m-xlsea [<samp>(8aeb7)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/8aeb7362)
- 修复登录页面样式问题 &nbsp;-&nbsp; by @m-xlsea [<samp>(4e27f)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/4e27f3b5)
- **types**:
- fix proxy types &nbsp;-&nbsp; by @soybeanjs [<samp>(12b25)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/12b25e0d)
- **utils**:
- 修复请求工具响应解密问题 &nbsp;-&nbsp; by @m-xlsea [<samp>(9ef0b)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/9ef0bd41)
### &nbsp;&nbsp;&nbsp;🛠 文档
- **components**: 补充国际化 &nbsp;-&nbsp; by **AN** [<samp>(ecad1)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/ecad1c3e)
- **projects**: 字典状态使用枚举值 &nbsp;-&nbsp; by @m-xlsea [<samp>(56fd5)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/56fd5434)
### &nbsp;&nbsp;&nbsp;📖 Documentation
- **other**: 更新 cursor 规则 &nbsp;-&nbsp; by @m-xlsea [<samp>(e623b)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/e623b560)
### &nbsp;&nbsp;&nbsp;🏡 重构
- **deps**:
- update deps &nbsp;-&nbsp; by @soybeanjs [<samp>(e33f9)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/e33f944a)
- update deps &nbsp;-&nbsp; by @soybeanjs [<samp>(9fa95)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/9fa951aa)
### &nbsp;&nbsp;&nbsp;🎨 样式
- **components**: 修改json预览组件样式问题 &nbsp;-&nbsp; by **AN** [<samp>(378aa)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/378aa869)
- **styles**: 修复字体样式导致下划线不可见问题 &nbsp;-&nbsp; by **AN** [<samp>(4a424)</samp>](https://gitee.com/xlsea/ruoyi-plus-soybean/commit/4a4244b5)
### &nbsp;&nbsp;&nbsp;❤️ 贡献者
[![m-xlsea](https://github.com/m-xlsea.png?size=48)](https://github.com/m-xlsea)&nbsp;&nbsp;[![soybeanjs](https://github.com/soybeanjs.png?size=48)](https://github.com/soybeanjs)&nbsp;&nbsp;[![Elio-An](https://github.com/Elio-An.png?size=48)](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)
### &nbsp;&nbsp;&nbsp;🐞 Bug 修复
+7 -2
View File
@@ -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>({
+1 -1
View File
@@ -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
View File
@@ -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",
+1 -1
View File
@@ -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
View File
@@ -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);
}
+51
View File
@@ -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 {}
}
+1 -1
View File
@@ -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 {
+18
View File
@@ -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>;
}
+104
View File
@@ -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>
+14
View File
@@ -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'
}
});
}
+7
View File
@@ -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'
});
}
+2
View File
@@ -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']
}