Merge remote-tracking branch 'soybean/main' into dev

# Conflicts:
#	CHANGELOG.md
#	README.en_US.md
#	README.md
#	package.json
#	pnpm-lock.yaml
#	src/App.vue
#	src/theme/settings.ts
#	src/views/_builtin/iframe-page/[url].vue
This commit is contained in:
xlsea 2025-06-25 11:17:46 +08:00
commit 1680ce4e26
42 changed files with 1234 additions and 1978 deletions

View File

@ -57,7 +57,7 @@
"@sa/materials": "workspace:*",
"@sa/tinymce": "workspace:*",
"@sa/utils": "workspace:*",
"@vueuse/core": "13.3.0",
"@vueuse/core": "13.4.0",
"clipboard": "2.0.11",
"dayjs": "1.11.13",
"defu": "6.1.4",
@ -66,49 +66,49 @@
"jsencrypt": "^3.3.2",
"json5": "2.2.3",
"monaco-editor": "^0.52.0",
"naive-ui": "2.41.1",
"naive-ui": "2.42.0",
"nprogress": "0.2.0",
"pinia": "3.0.3",
"tailwind-merge": "3.3.0",
"vue": "3.5.16",
"tailwind-merge": "3.3.1",
"vue": "3.5.17",
"vue-advanced-cropper": "^2.8.9",
"vue-draggable-plus": "0.6.0",
"vue-i18n": "11.1.5",
"vue-i18n": "11.1.7",
"vue-router": "4.5.1"
},
"devDependencies": {
"@elegant-router/vue": "0.3.8",
"@iconify/json": "2.2.347",
"@iconify/json": "2.2.352",
"@sa/scripts": "workspace:*",
"@sa/uno-preset": "workspace:*",
"@soybeanjs/eslint-config": "1.6.1",
"@types/node": "22.15.30",
"@types/node": "24.0.3",
"@types/nprogress": "0.2.3",
"@unocss/eslint-config": "66.1.4",
"@unocss/preset-icons": "66.1.4",
"@unocss/preset-uno": "66.1.4",
"@unocss/transformer-directives": "66.1.4",
"@unocss/transformer-variant-group": "66.1.4",
"@unocss/vite": "66.1.4",
"@vitejs/plugin-vue": "5.2.4",
"@vitejs/plugin-vue-jsx": "4.2.0",
"@unocss/eslint-config": "66.2.3",
"@unocss/preset-icons": "66.2.3",
"@unocss/preset-uno": "66.2.3",
"@unocss/transformer-directives": "66.2.3",
"@unocss/transformer-variant-group": "66.2.3",
"@unocss/vite": "66.2.3",
"@vitejs/plugin-vue": "6.0.0",
"@vitejs/plugin-vue-jsx": "5.0.0",
"consola": "3.4.2",
"eslint": "9.28.0",
"eslint": "9.29.0",
"eslint-plugin-vue": "10.2.0",
"kolorist": "1.8.0",
"sass": "1.89.1",
"sass": "1.89.2",
"simple-git-hooks": "2.13.0",
"tsx": "4.19.4",
"tsx": "4.20.3",
"typescript": "5.8.3",
"unplugin-icons": "22.1.0",
"unplugin-vue-components": "28.7.0",
"vite": "6.3.5",
"vite": "7.0.0",
"vite-plugin-monaco-editor": "^1.1.0",
"vite-plugin-progress": "0.0.7",
"vite-plugin-static-copy": "^3.0.0",
"vite-plugin-svg-icons": "2.0.1",
"vite-plugin-vue-devtools": "7.7.6",
"vue-eslint-parser": "10.1.3",
"vite-plugin-vue-devtools": "7.7.7",
"vue-eslint-parser": "10.1.4",
"vue-tsc": "2.2.10"
},
"simple-git-hooks": {

View File

@ -1,6 +1,6 @@
{
"name": "@sa/alova",
"version": "1.3.14",
"version": "1.3.15",
"exports": {
".": "./src/index.ts",
"./fetch": "./src/fetch.ts",
@ -13,8 +13,8 @@
}
},
"dependencies": {
"@alova/mock": "2.0.16",
"@alova/mock": "2.0.17",
"@sa/utils": "workspace:*",
"alova": "3.3.0"
"alova": "3.3.3"
}
}

View File

@ -1,6 +1,6 @@
{
"name": "@sa/axios",
"version": "1.3.14",
"version": "1.3.15",
"exports": {
".": "./src/index.ts"
},
@ -11,7 +11,7 @@
},
"dependencies": {
"@sa/utils": "workspace:*",
"axios": "1.9.0",
"axios": "1.10.0",
"axios-retry": "4.5.0",
"qs": "6.14.0"
},

View File

@ -1,6 +1,6 @@
{
"name": "@sa/color",
"version": "1.3.14",
"version": "1.3.15",
"exports": {
".": "./src/index.ts"
},

View File

@ -1,6 +1,6 @@
{
"name": "@sa/hooks",
"version": "1.3.14",
"version": "1.3.15",
"exports": {
".": "./src/index.ts"
},

View File

@ -1,6 +1,6 @@
{
"name": "@sa/materials",
"version": "1.3.14",
"version": "1.3.15",
"exports": {
".": "./src/index.ts"
},

View File

@ -1,6 +1,6 @@
{
"name": "@sa/fetch",
"version": "1.3.14",
"version": "1.3.15",
"exports": {
".": "./src/index.ts"
},

View File

@ -1,6 +1,6 @@
{
"name": "@sa/scripts",
"version": "1.3.14",
"version": "1.3.15",
"bin": {
"sa": "./bin.ts"
},
@ -14,7 +14,7 @@
},
"devDependencies": {
"@soybeanjs/changelog": "0.3.24",
"bumpp": "10.1.1",
"bumpp": "10.2.0",
"c12": "3.0.4",
"cac": "6.7.14",
"consola": "3.4.2",

View File

@ -1,6 +1,6 @@
{
"name": "@sa/uno-preset",
"version": "1.3.14",
"version": "1.3.15",
"exports": {
".": "./src/index.ts"
},

View File

@ -1,6 +1,6 @@
{
"name": "@sa/utils",
"version": "1.3.14",
"version": "1.3.15",
"exports": {
".": "./src/index.ts"
},

File diff suppressed because it is too large Load Diff

View File

@ -4,8 +4,8 @@ import { NConfigProvider, darkTheme } from 'naive-ui';
import type { WatermarkProps } from 'naive-ui';
import { useAppStore } from './store/modules/app';
import { useThemeStore } from './store/modules/theme';
import { naiveDateLocales, naiveLocales } from './locales/naive';
import { useAuthStore } from './store/modules/auth';
import { naiveDateLocales, naiveLocales } from './locales/naive';
defineOptions({
name: 'App'
@ -27,8 +27,12 @@ const naiveDateLocale = computed(() => {
const watermarkProps = computed<WatermarkProps>(() => {
const appTitle = import.meta.env.VITE_APP_TITLE || 'RuoYi-Vue-Plus';
const content =
themeStore.watermark.enableUserName && userInfo.user?.userName
? `${userInfo.user?.nickName}@${appTitle} ${userInfo.user?.userName}`
: appTitle;
return {
content: userInfo.user?.userName ? `${userInfo.user?.nickName}@${appTitle} ${userInfo.user?.userName}` : appTitle,
content,
cross: true,
fullscreen: true,
fontSize: 14,

View File

@ -31,13 +31,25 @@ const tooltipContent = computed(() => {
return $t('icon.lang');
});
/** Add bottom margin to all options except the last one for proper visual separation */
const dropdownOptions = computed(() => {
const lastIndex = props.langOptions.length - 1;
return props.langOptions.map((option, index) => ({
...option,
props: {
class: index < lastIndex ? 'mb-1' : undefined
}
}));
});
function changeLang(lang: App.I18n.LangType) {
emit('changeLang', lang);
}
</script>
<template>
<NDropdown :value="lang" :options="langOptions" trigger="hover" @select="changeLang">
<NDropdown :value="lang" :options="dropdownOptions" trigger="hover" @select="changeLang">
<div>
<ButtonIcon :tooltip-content="tooltipContent" tooltip-placement="left">
<SvgIcon icon="heroicons:language" />

View File

@ -117,7 +117,10 @@ const isWrapperScrollMode = computed(() => themeStore.layout.scrollMode === 'wra
<SettingItem key="8" :label="$t('theme.watermark.visible')">
<NSwitch v-model:value="themeStore.watermark.visible" />
</SettingItem>
<SettingItem v-if="themeStore.watermark.visible" key="8-1" :label="$t('theme.watermark.text')">
<SettingItem v-if="themeStore.watermark.visible" key="8-1" :label="$t('theme.watermark.enableUserName')">
<NSwitch v-model:value="themeStore.watermark.enableUserName" />
</SettingItem>
<SettingItem v-if="themeStore.watermark.visible" key="8-2" :label="$t('theme.watermark.text')">
<NInput
v-model:value="themeStore.watermark.text"
autosize

View File

@ -166,7 +166,8 @@ const local: App.I18n.Schema = {
},
watermark: {
visible: 'Watermark Full Screen Visible',
text: 'Watermark Text'
text: 'Watermark Text',
enableUserName: 'Enable User Name Watermark'
},
tablePropsTitle: 'Table Props',
table: {

View File

@ -166,7 +166,8 @@ const local: App.I18n.Schema = {
},
watermark: {
visible: '显示全屏水印',
text: '水印文本'
text: '水印文本',
enableUserName: '启用用户名水印'
},
tablePropsTitle: '表格配置',
table: {

View File

@ -95,7 +95,6 @@ async function getHtmlBuildTime(): Promise<string | null> {
const res = await fetch(`${baseUrl}index.html?time=${Date.now()}`);
if (!res.ok) {
console.error('getHtmlBuildTime error:', res.status, res.statusText);
return null;
}
@ -103,7 +102,7 @@ async function getHtmlBuildTime(): Promise<string | null> {
const match = html.match(/<meta name="buildTime" content="(.*)">/);
return match?.[1] || null;
} catch (error) {
console.error('getHtmlBuildTime error:', error);
window.console.error('getHtmlBuildTime error:', error);
return null;
}
}

View File

@ -10,4 +10,5 @@ body,
html {
overflow-x: hidden;
color: rgb(var(--base-text-color));
}

View File

@ -58,7 +58,8 @@ export const themeSettings: App.Theme.ThemeSetting = {
},
watermark: {
visible: import.meta.env.VITE_WATERMARK === 'Y',
text: 'RuoYi-Vue-Plus'
text: 'RuoYi-Vue-Plus',
enableUserName: false
},
table: {
bordered: true,

View File

@ -112,6 +112,8 @@ declare namespace App {
visible: boolean;
/** Watermark text */
text: string;
/** Whether to use user name as watermark text */
enableUserName: boolean;
};
table: {
/** Whether to show the table border */
@ -446,6 +448,7 @@ declare namespace App {
watermark: {
visible: string;
text: string;
enableUserName: string;
};
tablePropsTitle: string;
table: {

View File

@ -25,7 +25,7 @@ declare namespace Env {
*
* This prefix is start with the icon prefix
*/
readonly VITE_ICON_LOCAL_PREFIX: 'local-icon';
readonly VITE_ICON_LOCAL_PREFIX: 'icon-local';
/** backend service base url */
readonly VITE_SERVICE_BASE_URL: string;
/**

View File

@ -1,21 +1,9 @@
<script setup lang="ts">
import { onActivated, onMounted } from 'vue';
interface Props {
url: string;
}
defineProps<Props>();
onMounted(() => {
// eslint-disable-next-line no-console
console.log('mounted');
});
onActivated(() => {
// eslint-disable-next-line no-console
console.log('activated');
});
</script>
<template>

View File

@ -169,7 +169,7 @@ async function handleExport() {
<template>
<div class="min-h-500px flex-col-stretch gap-16px overflow-hidden lt-sm:overflow-auto">
<DemoSearch v-model:model="searchParams" @reset="resetSearchParams" @search="getDataByPage" />
<NCard title="测试单表列表" :bordered="false" size="small" class="sm:flex-1-hidden card-wrapper">
<NCard title="测试单表列表" :bordered="false" size="small" class="card-wrapper sm:flex-1-hidden">
<template #header-extra>
<TableHeaderOperation
v-model:columns="columnChecks"

View File

@ -179,7 +179,7 @@ function handleExport() {
<template>
<div class="min-h-500px flex-col-stretch gap-16px overflow-hidden lt-sm:overflow-auto">
<TreeSearch v-model:model="searchParams" :tree-list="data" @reset="resetSearchParams" @search="getData" />
<NCard title="测试树列表" :bordered="false" size="small" class="sm:flex-1-hidden card-wrapper">
<NCard title="测试树列表" :bordered="false" size="small" class="card-wrapper sm:flex-1-hidden">
<template #header-extra>
<TableHeaderOperation
v-model:columns="columnChecks"

View File

@ -228,7 +228,7 @@ async function handleUnlockLoginInfor(username: string) {
<template>
<div class="min-h-500px flex-col-stretch gap-16px overflow-hidden lt-sm:overflow-auto">
<LoginInforSearch v-model:model="searchParams" @reset="resetSearchParams" @search="getDataByPage" />
<NCard title="登录日志列表" :bordered="false" size="small" class="sm:flex-1-hidden card-wrapper">
<NCard title="登录日志列表" :bordered="false" size="small" class="card-wrapper sm:flex-1-hidden">
<template #header-extra>
<TableHeaderOperation
v-model:columns="columnChecks"

View File

@ -141,7 +141,7 @@ async function handleForceLogout(tokenId: string) {
<template>
<div class="min-h-500px flex-col-stretch gap-16px overflow-hidden lt-sm:overflow-auto">
<OnlineSearch v-model:model="searchParams" @reset="resetSearchParams" @search="getData" />
<NCard title="在线用户列表" :bordered="false" size="small" class="sm:flex-1-hidden card-wrapper">
<NCard title="在线用户列表" :bordered="false" size="small" class="card-wrapper sm:flex-1-hidden">
<template #header-extra>
<TableHeaderOperation
v-model:columns="columnChecks"

View File

@ -174,7 +174,7 @@ async function handleCleanOperLog() {
<template>
<div class="min-h-500px flex-col-stretch gap-16px overflow-hidden lt-sm:overflow-auto">
<OperLogSearch v-model:model="searchParams" @reset="resetSearchParams" @search="getDataByPage" />
<NCard title="操作日志列表" :bordered="false" size="small" class="sm:flex-1-hidden card-wrapper">
<NCard title="操作日志列表" :bordered="false" size="small" class="card-wrapper sm:flex-1-hidden">
<template #header-extra>
<TableHeaderOperation
v-model:columns="columnChecks"

View File

@ -207,7 +207,7 @@ async function handleExport() {
<template>
<div class="min-h-500px flex-col-stretch gap-16px overflow-hidden lt-sm:overflow-auto">
<ClientSearch v-model:model="searchParams" @reset="resetSearchParams" @search="getDataByPage" />
<NCard :title="$t('page.system.client.title')" :bordered="false" size="small" class="sm:flex-1-hidden card-wrapper">
<NCard :title="$t('page.system.client.title')" :bordered="false" size="small" class="card-wrapper sm:flex-1-hidden">
<template #header-extra>
<TableHeaderOperation
v-model:columns="columnChecks"

View File

@ -193,7 +193,7 @@ async function handleRefreshCache() {
<template>
<div class="min-h-500px flex-col-stretch gap-16px overflow-hidden lt-sm:overflow-auto">
<ConfigSearch v-model:model="searchParams" @reset="resetSearchParams" @search="getDataByPage" />
<NCard :title="$t('page.system.config.title')" :bordered="false" size="small" class="sm:flex-1-hidden card-wrapper">
<NCard :title="$t('page.system.config.title')" :bordered="false" size="small" class="card-wrapper sm:flex-1-hidden">
<template #header-extra>
<TableHeaderOperation
v-model:columns="columnChecks"

View File

@ -168,7 +168,7 @@ async function handleAddOperate() {
<template>
<div class="min-h-500px flex-col-stretch gap-16px overflow-hidden lt-sm:overflow-auto">
<DeptSearch v-model:model="searchParams" @reset="resetSearchParams" @search="getData" />
<NCard :title="$t('page.system.dept.title')" :bordered="false" size="small" class="sm:flex-1-hidden card-wrapper">
<NCard :title="$t('page.system.dept.title')" :bordered="false" size="small" class="card-wrapper sm:flex-1-hidden">
<template #header-extra>
<TableHeaderOperation
v-model:columns="columnChecks"

View File

@ -362,7 +362,7 @@ const selectable = computed(() => {
<div class="h-full flex-col-stretch gap-12px overflow-hidden lt-sm:overflow-auto">
<DictDataSearch v-model:model="searchParams" @reset="handleReset" @search="getDataByPage" />
<TableRowCheckAlert v-model:checked-row-keys="checkedRowKeys" />
<NCard :title="$t('page.system.dict.title')" :bordered="false" size="small" class="sm:flex-1-hidden card-wrapper">
<NCard :title="$t('page.system.dict.title')" :bordered="false" size="small" class="card-wrapper sm:flex-1-hidden">
<template #header-extra>
<TableHeaderOperation
v-model:columns="columnChecks"

View File

@ -163,7 +163,7 @@ async function edit(noticeId: CommonType.IdType) {
<template>
<div class="min-h-500px flex-col-stretch gap-16px overflow-hidden lt-sm:overflow-auto">
<NoticeSearch v-model:model="searchParams" @reset="resetSearchParams" @search="getDataByPage" />
<NCard title="通知公告列表" :bordered="false" size="small" class="sm:flex-1-hidden card-wrapper">
<NCard title="通知公告列表" :bordered="false" size="small" class="card-wrapper sm:flex-1-hidden">
<template #header-extra>
<TableHeaderOperation
v-model:columns="columnChecks"

View File

@ -221,7 +221,7 @@ async function handleStatusChange(
<template>
<div class="min-h-500px flex-col-stretch gap-16px overflow-hidden lt-sm:overflow-auto">
<OssConfigSearch v-model:model="searchParams" @reset="resetSearchParams" @search="getDataByPage" />
<NCard title="OSS配置列表" :bordered="false" size="small" class="sm:flex-1-hidden card-wrapper">
<NCard title="OSS配置列表" :bordered="false" size="small" class="card-wrapper sm:flex-1-hidden">
<template #header-extra>
<TableHeaderOperation
v-model:columns="columnChecks"

View File

@ -270,7 +270,7 @@ function handleToOssConfig() {
<template>
<div class="min-h-500px flex-col-stretch gap-16px overflow-hidden lt-sm:overflow-auto">
<OssSearch v-model:model="searchParams" @reset="resetSearchParams" @search="getDataByPage" />
<NCard title="OSS 对象存储列表" :bordered="false" size="small" class="sm:flex-1-hidden card-wrapper">
<NCard title="OSS 对象存储列表" :bordered="false" size="small" class="card-wrapper sm:flex-1-hidden">
<template #header-extra>
<TableHeaderOperation
v-model:columns="columnChecks"

View File

@ -255,7 +255,7 @@ function handleResetSearch() {
</template>
<div class="h-full flex-col-stretch gap-12px overflow-hidden lt-sm:overflow-auto">
<PostSearch v-model:model="searchParams" @reset="handleResetSearch" @search="getDataByPage" />
<NCard title="岗位信息列表" :bordered="false" size="small" class="sm:flex-1-hidden card-wrapper">
<NCard title="岗位信息列表" :bordered="false" size="small" class="card-wrapper sm:flex-1-hidden">
<template #header-extra>
<TableHeaderOperation
v-model:columns="columnChecks"

View File

@ -249,7 +249,7 @@ function handleAuthUser(row: Api.System.Role) {
<template>
<div class="min-h-500px flex-col-stretch gap-16px overflow-hidden lt-sm:overflow-auto">
<RoleSearch v-model:model="searchParams" @reset="resetSearchParams" @search="getDataByPage" />
<NCard title="角色列表" :bordered="false" size="small" class="sm:flex-1-hidden card-wrapper">
<NCard title="角色列表" :bordered="false" size="small" class="card-wrapper sm:flex-1-hidden">
<template #header-extra>
<TableHeaderOperation
v-model:columns="columnChecks"

View File

@ -177,7 +177,7 @@ watch(visible, () => {
</NGrid>
</NForm>
<TableRowCheckAlert v-model:checked-row-keys="checkedRowKeys" />
<NCard :bordered="false" size="small" class="sm:flex-1-hidden card-wrapper">
<NCard :bordered="false" size="small" class="card-wrapper sm:flex-1-hidden">
<NDataTable
v-model:checked-row-keys="checkedRowKeys"
:columns="columns"

View File

@ -196,7 +196,7 @@ async function handleStatusChange(
:title="$t('page.system.tenantPackage.title')"
:bordered="false"
size="small"
class="sm:flex-1-hidden card-wrapper"
class="card-wrapper sm:flex-1-hidden"
>
<template #header-extra>
<TableHeaderOperation

View File

@ -220,7 +220,7 @@ async function handleExport() {
<template>
<div class="min-h-500px flex-col-stretch gap-16px overflow-hidden lt-sm:overflow-auto">
<TenantSearch v-model:model="searchParams" @reset="resetSearchParams" @search="getDataByPage" />
<NCard title="租户列表" :bordered="false" size="small" class="sm:flex-1-hidden card-wrapper">
<NCard title="租户列表" :bordered="false" size="small" class="card-wrapper sm:flex-1-hidden">
<template #header-extra>
<TableHeaderOperation
v-model:columns="columnChecks"

View File

@ -311,7 +311,7 @@ function handleResetSearch() {
<div class="h-full flex-col-stretch gap-12px overflow-hidden lt-sm:overflow-auto">
<UserSearch v-model:model="searchParams" @reset="handleResetSearch" @search="getDataByPage" />
<TableRowCheckAlert v-model:checked-row-keys="checkedRowKeys" />
<NCard :title="$t('page.system.user.title')" :bordered="false" size="small" class="sm:flex-1-hidden card-wrapper">
<NCard :title="$t('page.system.user.title')" :bordered="false" size="small" class="card-wrapper sm:flex-1-hidden">
<template #header-extra>
<TableHeaderOperation
v-model:columns="columnChecks"

View File

@ -268,7 +268,7 @@ getDataNames();
@search="getDataByPage"
/>
<TableRowCheckAlert v-model:checked-row-keys="checkedRowKeys" />
<NCard title="代码生成" :bordered="false" size="small" class="sm:flex-1-hidden card-wrapper">
<NCard title="代码生成" :bordered="false" size="small" class="card-wrapper sm:flex-1-hidden">
<template #header-extra>
<TableHeaderOperation
v-model:columns="columnChecks"

View File

@ -6,7 +6,7 @@
"lib": ["DOM", "ESNext"],
"baseUrl": ".",
"module": "ESNext",
"moduleResolution": "node",
"moduleResolution": "bundler",
"paths": {
"@/*": ["./src/*"],
"~/*": ["./*"]