chore(merge): Merge Soybean branch main into dev

This commit is contained in:
xlsea
2026-03-31 14:26:51 +08:00
35 changed files with 1400 additions and 2264 deletions
+1
View File
@@ -9,6 +9,7 @@
"mhutchie.git-graph",
"mikestead.dotenv",
"naumovs.color-highlight",
"oxc.oxc-vscode",
"pkief.material-icon-theme",
"sdras.vue-vscode-snippets",
"vue.volar",
+4 -2
View File
@@ -15,10 +15,12 @@
"i18n-ally.parsers.typescript.compilerOptions": {
"moduleResolution": "node"
},
"prettier.enable": false,
"oxc.fmt.configPath": ".oxfmtrc.json",
"unocss.root": ["./"],
"search.exclude": {
"/docs": true,
"**/dist/**": true
"**/dist/**": true,
"/.claude": true,
"/.codex": true
}
}
+1
View File
@@ -1,5 +1,6 @@
MIT License
Copyright (c) 2021 Soybean
Copyright (c) 2024 xlsea
Permission is hereby granted, free of charge, to any person obtaining a copy
+1
View File
@@ -6,6 +6,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="color-scheme" content="light dark" />
<meta name="author" content="https://blog.xlsea.cn" />
<meta name="license" content="https://github.com/m-xlsea/ruoyi-plus-soybean/blob/master/LICENSE" />
<title>%VITE_APP_TITLE%</title>
</head>
<body>
+22 -17
View File
@@ -29,6 +29,11 @@
"name": "Elio",
"email": "1983933789@qq.com",
"url": "https://gitee.com/elio-an"
},
{
"name": "Soybean",
"email": "soybeanjs@outlook.com",
"url": "https://github.com/soybeanjs"
}
],
"repository": {
@@ -67,7 +72,7 @@
"@wangeditor-next/editor": "^5.6.49",
"@wangeditor-next/editor-for-vue": "^5.1.14",
"clipboard": "2.0.11",
"dayjs": "1.11.19",
"dayjs": "1.11.20",
"defu": "6.1.4",
"echarts": "6.0.0",
"highlight.js": "^11.11.1",
@@ -78,40 +83,40 @@
"pinia": "3.0.4",
"streamsaver": "^2.0.6",
"tailwind-merge": "3.5.0",
"vue": "3.5.29",
"vue": "3.5.31",
"vue-advanced-cropper": "^2.8.9",
"vue-draggable-plus": "0.6.1",
"vue-i18n": "11.3.0",
"vue-router": "5.0.3"
"vue-router": "5.0.4"
},
"devDependencies": {
"@elegant-router/vue": "0.3.8",
"@iconify/json": "2.2.447",
"@iconify/json": "2.2.456",
"@sa/scripts": "workspace:*",
"@sa/uno-preset": "workspace:*",
"@soybeanjs/eslint-config-vue": "^0.0.2",
"@types/node": "25.3.5",
"@types/node": "25.5.0",
"@types/nprogress": "0.2.3",
"@vitejs/plugin-vue": "6.0.4",
"@vitejs/plugin-vue-jsx": "5.1.4",
"@vitejs/plugin-vue": "6.0.5",
"@vitejs/plugin-vue-jsx": "5.1.5",
"consola": "3.4.2",
"eslint": "10.0.3",
"eslint": "10.1.0",
"kolorist": "1.8.0",
"oxfmt": "^0.36.0",
"oxlint": "^1.51.0",
"sass": "1.97.3",
"oxfmt": "^0.42.0",
"oxlint": "^1.57.0",
"sass": "1.98.0",
"simple-git-hooks": "2.13.1",
"tsx": "4.21.0",
"typescript": "5.9.3",
"unocss": "^66.6.6",
"typescript": "6.0.2",
"unocss": "^66.6.7",
"unplugin-icons": "23.0.1",
"unplugin-vue-components": "31.0.0",
"vite": "7.3.1",
"unplugin-vue-components": "32.0.0",
"vite": "8.0.3",
"vite-plugin-progress": "0.0.7",
"vite-plugin-svg-icons": "2.0.1",
"vite-plugin-vue-devtools": "8.0.7",
"vite-plugin-vue-devtools": "8.1.1",
"vite-plugin-vue-transition-root-validator": "^0.1.0",
"vue-tsc": "3.2.5"
"vue-tsc": "3.2.6"
},
"simple-git-hooks": {
"commit-msg": "pnpm sa git-commit-verify",
+1 -1
View File
@@ -15,7 +15,7 @@
"./mock": "./src/mock.ts"
},
"dependencies": {
"@alova/mock": "2.0.19",
"@alova/mock": "2.0.20",
"@sa/utils": "workspace:*",
"alova": "3.5.1"
}
+1 -2
View File
@@ -3,9 +3,8 @@
"target": "ESNext",
"jsx": "preserve",
"lib": ["DOM", "ESNext"],
"baseUrl": ".",
"module": "ESNext",
"moduleResolution": "node",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"types": ["node"],
"strict": true,
+1 -1
View File
@@ -13,7 +13,7 @@
},
"dependencies": {
"@sa/utils": "workspace:*",
"axios": "1.13.6",
"axios": "1.14.0",
"axios-retry": "4.5.0",
"qs": "6.15.0"
},
+1 -2
View File
@@ -3,9 +3,8 @@
"target": "ESNext",
"jsx": "preserve",
"lib": ["DOM", "ESNext"],
"baseUrl": ".",
"module": "ESNext",
"moduleResolution": "node",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"types": ["node"],
"strict": true,
+1 -2
View File
@@ -3,9 +3,8 @@
"target": "ESNext",
"jsx": "preserve",
"lib": ["DOM", "ESNext"],
"baseUrl": ".",
"module": "ESNext",
"moduleResolution": "node",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"types": ["node"],
"strict": true,
+1 -2
View File
@@ -3,9 +3,8 @@
"target": "ESNext",
"jsx": "preserve",
"lib": ["DOM", "ESNext"],
"baseUrl": ".",
"module": "ESNext",
"moduleResolution": "node",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"types": ["node"],
"strict": true,
+1 -2
View File
@@ -3,9 +3,8 @@
"target": "ESNext",
"jsx": "preserve",
"lib": ["DOM", "ESNext"],
"baseUrl": ".",
"module": "ESNext",
"moduleResolution": "node",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"types": ["node"],
"strict": true,
+1 -1
View File
@@ -1,3 +1,3 @@
#!/usr/bin/env tsx
import './src/index.ts';
import './src/index';
+5 -5
View File
@@ -15,16 +15,16 @@
".": "./src/index.ts"
},
"devDependencies": {
"@soybeanjs/changelog": "0.4.3",
"bumpp": "10.4.1",
"@soybeanjs/changelog": "0.4.5",
"bumpp": "11.0.1",
"c12": "3.3.3",
"cac": "6.7.14",
"cac": "7.0.0",
"consola": "3.4.2",
"enquirer": "2.4.1",
"execa": "9.6.1",
"kolorist": "1.8.0",
"npm-check-updates": "19.6.3",
"picomatch": "4.0.3",
"npm-check-updates": "19.6.6",
"picomatch": "4.0.4",
"rimraf": "6.1.3"
}
}
+1 -2
View File
@@ -3,9 +3,8 @@
"target": "ESNext",
"jsx": "preserve",
"lib": ["DOM", "ESNext"],
"baseUrl": ".",
"module": "ESNext",
"moduleResolution": "node",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"types": ["node"],
"strict": true,
+1 -2
View File
@@ -3,9 +3,8 @@
"target": "ESNext",
"jsx": "preserve",
"lib": ["DOM", "ESNext"],
"baseUrl": ".",
"module": "ESNext",
"moduleResolution": "node",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"types": ["node"],
"strict": true,
+1 -1
View File
@@ -16,7 +16,7 @@
"crypto-js": "4.2.0",
"klona": "2.0.6",
"localforage": "1.10.0",
"nanoid": "5.1.6"
"nanoid": "5.1.7"
},
"devDependencies": {
"@types/crypto-js": "4.2.2"
+1 -2
View File
@@ -3,9 +3,8 @@
"target": "ESNext",
"jsx": "preserve",
"lib": ["DOM", "ESNext"],
"baseUrl": ".",
"module": "ESNext",
"moduleResolution": "node",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"types": ["node"],
"strict": true,
+1289 -2146
View File
File diff suppressed because it is too large Load Diff
+1 -4
View File
@@ -1,6 +1,4 @@
<script setup lang="ts">
import { useAttrs } from 'vue';
import type { DataTableProps } from 'naive-ui';
import type { CreateRowKey } from 'naive-ui/es/data-table/src/interface';
import { useThemeStore } from '@/store/modules/theme';
@@ -16,7 +14,6 @@ interface Props {
defineProps<Props>();
const { table } = useThemeStore();
const attrs: DataTableProps = useAttrs();
</script>
<template>
@@ -28,7 +25,7 @@ const attrs: DataTableProps = useAttrs();
:size="table.size"
:striped="table.striped"
:row-key="rowKey"
v-bind="attrs"
v-bind="$attrs"
/>
</template>
+3 -6
View File
@@ -1,10 +1,9 @@
<script setup lang="ts">
import { useAttrs } from 'vue';
import type { TagProps } from 'naive-ui';
import { isNotNull } from '@/utils/common';
defineOptions({
name: 'BooleanTag'
name: 'BooleanTag',
inheritAttrs: false
});
const value = defineModel<'0' | '1'>('value', { required: true });
@@ -13,12 +12,10 @@ const tagMap: Record<'0' | '1', NaiveUI.ThemeColor> = {
0: 'success',
1: 'error'
};
const attrs: TagProps = useAttrs();
</script>
<template>
<NTag v-if="isNotNull(value)" :type="tagMap[value]" v-bind="attrs">{{ value === '0' ? '是' : '否' }}</NTag>
<NTag v-if="isNotNull(value)" :type="tagMap[value]" v-bind="$attrs">{{ value === '0' ? '是' : '否' }}</NTag>
</template>
<style scoped></style>
+2 -2
View File
@@ -1,6 +1,6 @@
<script setup lang="ts">
import { type VNode, computed, useAttrs } from 'vue';
import type { ButtonProps, PopoverPlacement } from 'naive-ui';
import type { PopoverPlacement } from 'naive-ui';
import { twMerge } from 'tailwind-merge';
defineOptions({
@@ -48,7 +48,7 @@ const emit = defineEmits<Emits>();
const DEFAULT_CLASS = 'h-[36px] text-icon';
const attrs: ButtonProps = useAttrs();
const attrs = useAttrs();
const quaternary = computed(() => {
return !(attrs.text || attrs.dashed || attrs.ghost) && props.quaternary;
+5 -5
View File
@@ -1,10 +1,11 @@
<script setup lang="tsx">
import { useAttrs } from 'vue';
import type { TreeSelectProps } from 'naive-ui';
import { useLoading } from '@sa/hooks';
import { fetchGetDeptTree } from '@/service/api/system';
defineOptions({ name: 'DeptTreeSelect' });
defineOptions({
name: 'DeptTreeSelect',
inheritAttrs: false
});
interface Props {
[key: string]: any;
@@ -16,7 +17,6 @@ const value = defineModel<CommonType.IdType | null>('value', { required: false }
const options = defineModel<Api.Common.CommonTreeRecord>('options', { required: false, default: [] });
const expandedKeys = defineModel<CommonType.IdType[]>('expandedKeys', { required: false, default: [] });
const attrs: TreeSelectProps = useAttrs();
const { loading, startLoading, endLoading } = useLoading();
async function getDeptList() {
@@ -44,7 +44,7 @@ getDeptList();
key-field="id"
label-field="label"
:options="options as []"
v-bind="attrs"
v-bind="$attrs"
/>
</template>
+7 -6
View File
@@ -1,10 +1,13 @@
<script setup lang="tsx">
import { onMounted, ref, useAttrs, watch } from 'vue';
import type { TreeSelectInst, TreeSelectProps } from 'naive-ui';
import { onMounted, ref, watch } from 'vue';
import type { TreeSelectInst } from 'naive-ui';
import { useBoolean } from '@sa/hooks';
import { fetchGetDeptTree } from '@/service/api/system/user';
defineOptions({ name: 'DeptTree' });
defineOptions({
name: 'DeptTree',
inheritAttrs: false
});
interface Props {
immediate?: boolean;
@@ -25,8 +28,6 @@ const options = defineModel<any[]>('options', { required: false, default: [] });
const cascade = defineModel<boolean>('cascade', { required: false, default: true });
const loading = defineModel<boolean>('loading', { required: false, default: false });
const attrs: TreeSelectProps = useAttrs();
async function getDeptList() {
loading.value = true;
const { error, data } = await fetchGetDeptTree();
@@ -124,7 +125,7 @@ defineExpose({
:loading="loading"
virtual-scroll
:check-strategy="cascade ? 'child' : 'all'"
v-bind="attrs"
v-bind="$attrs"
/>
</NSpin>
</div>
+5 -5
View File
@@ -1,9 +1,10 @@
<script setup lang="ts">
import { useAttrs } from 'vue';
import type { SelectProps } from 'naive-ui';
import { useDict } from '@/hooks/business/dict';
defineOptions({ name: 'DictSelect' });
defineOptions({
name: 'DictSelect',
inheritAttrs: false
});
interface Props {
dictCode: string;
@@ -19,7 +20,6 @@ const props = withDefaults(defineProps<Props>(), {
const value = defineModel<string | string[] | null>('value', { required: false });
const attrs: SelectProps = useAttrs();
const { options } = useDict(props.dictCode, props.immediate);
</script>
@@ -30,7 +30,7 @@ const { options } = useDict(props.dictCode, props.immediate);
:loading="!options.length"
:options="options"
:clear-filter-after-select="false"
v-bind="attrs"
v-bind="$attrs"
/>
</template>
+6 -6
View File
@@ -1,12 +1,14 @@
<script setup lang="ts">
import { computed, useAttrs } from 'vue';
import type { TagProps } from 'naive-ui';
import { computed } from 'vue';
import { jsonClone } from '@sa/utils';
import { useDict } from '@/hooks/business/dict';
import { isNotNull } from '@/utils/common';
import { $t } from '@/locales';
defineOptions({ name: 'DictTag' });
defineOptions({
name: 'DictTag',
inheritAttrs: false
});
interface Props {
value?: string[] | number[] | string | number;
@@ -23,8 +25,6 @@ const props = withDefaults(defineProps<Props>(), {
value: () => []
});
const attrs = useAttrs() as TagProps;
const { transformDictData } = useDict(props.dictCode, props.immediate);
const dictTagData = computed<Api.System.DictData[]>(() => {
@@ -51,7 +51,7 @@ const dictTagData = computed<Api.System.DictData[]>(() => {
:key="item.dictValue"
class="m-1"
:class="[item.cssClass]"
v-bind="attrs"
v-bind="$attrs"
:type="item.listClass || 'default'"
>
{{ item.dictLabel }}
+5 -6
View File
@@ -1,6 +1,6 @@
<script setup lang="tsx">
import { computed, defineComponent, useAttrs } from 'vue';
import type { UploadFileInfo, UploadProps } from 'naive-ui';
import { computed, defineComponent } from 'vue';
import type { UploadFileInfo } from 'naive-ui';
import type { JSX } from 'vue/jsx-runtime';
import { fetchBatchDeleteOss } from '@/service/api/system/oss';
import { getToken } from '@/store/modules/auth/shared';
@@ -8,7 +8,8 @@ import { getServiceBaseURL } from '@/utils/service';
import { AcceptType } from '@/enum/business';
defineOptions({
name: 'FileUpload'
name: 'FileUpload',
inheritAttrs: false
});
interface Props {
@@ -40,8 +41,6 @@ const accept = computed(() => {
return props.uploadType === 'file' ? AcceptType.File : AcceptType.Image;
});
const attrs: UploadProps = useAttrs();
let fileNum = 0;
const fileList = defineModel<UploadFileInfo[]>('fileList', {
default: () => []
@@ -177,7 +176,7 @@ async function handleRemove(file: UploadFileInfo) {
<template>
<div class="w-full flex-col">
<NUpload
v-bind="attrs"
v-bind="$attrs"
v-model:file-list="fileList"
:action="`${baseURL}${action}`"
:data="data"
+7 -5
View File
@@ -1,13 +1,16 @@
<script setup lang="tsx">
import { onMounted, useAttrs } from 'vue';
import type { TreeOption, TreeSelectProps } from 'naive-ui';
import { onMounted } from 'vue';
import type { TreeOption } from 'naive-ui';
import { useLoading } from '@sa/hooks';
import { fetchGetMenuList } from '@/service/api/system';
import { handleTree } from '@/utils/common';
import SvgIcon from '@/components/custom/svg-icon.vue';
import { $t } from '@/locales';
defineOptions({ name: 'MenuTreeSelect' });
defineOptions({
name: 'MenuTreeSelect',
inheritAttrs: false
});
interface Props {
immediate?: boolean;
@@ -21,7 +24,6 @@ const props = withDefaults(defineProps<Props>(), {
const value = defineModel<CommonType.IdType | null>('value', { required: false });
const options = defineModel<Api.System.MenuList>('options', { required: false, default: [] });
const attrs: TreeSelectProps = useAttrs();
const { loading, startLoading, endLoading } = useLoading();
async function getMenuList() {
@@ -75,7 +77,7 @@ function renderPrefix({ option }: { option: TreeOption }) {
:render-tag="renderLabel"
:render-label="renderLabel"
:render-prefix="renderPrefix"
v-bind="attrs"
v-bind="$attrs"
/>
</template>
+7 -5
View File
@@ -1,12 +1,15 @@
<script setup lang="tsx">
import { onMounted, ref, useAttrs, watch } from 'vue';
import type { TreeOption, TreeSelectInst, TreeSelectProps } from 'naive-ui';
import { onMounted, ref, watch } from 'vue';
import type { TreeOption, TreeSelectInst } from 'naive-ui';
import { useBoolean } from '@sa/hooks';
import { fetchGetMenuTreeSelect } from '@/service/api/system';
import SvgIcon from '@/components/custom/svg-icon.vue';
import { $t } from '@/locales';
defineOptions({ name: 'MenuTree' });
defineOptions({
name: 'MenuTree',
inheritAttrs: false
});
interface Props {
immediate?: boolean;
@@ -28,7 +31,6 @@ const checkedKeys = defineModel<CommonType.IdType[]>('checkedKeys', { required:
const options = defineModel<Api.System.MenuList>('options', { required: false, default: [] });
const cascade = defineModel<boolean>('cascade', { required: false, default: true });
const loading = defineModel<boolean>('loading', { required: false, default: false });
const attrs: TreeSelectProps = useAttrs();
async function getMenuList() {
loading.value = true;
@@ -192,7 +194,7 @@ defineExpose({
check-strategy="all"
:render-label="renderLabel"
:render-prefix="renderPrefix"
v-bind="attrs"
v-bind="$attrs"
/>
</NSpin>
</div>
+4 -5
View File
@@ -1,5 +1,5 @@
<script setup lang="ts">
import { ref, useAttrs, watch } from 'vue';
import { ref, watch } from 'vue';
import type { UploadFileInfo } from 'naive-ui';
import { useLoading } from '@sa/hooks';
import { fetchGetOssListByIds } from '@/service/api/system/oss';
@@ -7,11 +7,10 @@ import { isNotNull } from '@/utils/common';
import FileUpload from '@/components/custom/file-upload.vue';
defineOptions({
name: 'OssUpload'
name: 'OssUpload',
inheritAttrs: false
});
const attrs = useAttrs();
const value = defineModel<string>('value', { default: '' });
const { loading, startLoading, endLoading } = useLoading();
@@ -63,7 +62,7 @@ watch(fileList, val => {
<template>
<NSpin v-if="loading" />
<FileUpload v-else v-bind="attrs" v-model:file-list="fileList" />
<FileUpload v-else v-bind="$attrs" v-model:file-list="fileList" />
</template>
<style scoped></style>
+4 -6
View File
@@ -1,11 +1,11 @@
<script setup lang="ts">
import { ref, useAttrs, watch } from 'vue';
import type { SelectProps } from 'naive-ui';
import { ref, watch } from 'vue';
import { useLoading } from '@sa/hooks';
import { fetchGetPostSelect } from '@/service/api/system';
defineOptions({
name: 'PostSelect'
name: 'PostSelect',
inheritAttrs: false
});
interface Props {
@@ -17,8 +17,6 @@ const props = defineProps<Props>();
const value = defineModel<CommonType.IdType[] | null>('value', { required: false });
const attrs: SelectProps = useAttrs();
const { loading: postLoading, startLoading: startPostLoading, endLoading: endPostLoading } = useLoading();
/** the enabled post options */
@@ -55,7 +53,7 @@ async function getPostOptions() {
v-model:value="value"
:loading="postLoading"
:options="postOptions"
v-bind="attrs"
v-bind="$attrs"
placeholder="请选择岗位"
/>
</template>
+4 -6
View File
@@ -1,11 +1,11 @@
<script setup lang="ts">
import { ref, useAttrs } from 'vue';
import type { SelectProps } from 'naive-ui';
import { ref } from 'vue';
import { useLoading } from '@sa/hooks';
import { fetchGetRoleSelect } from '@/service/api/system';
defineOptions({
name: 'RoleSelect'
name: 'RoleSelect',
inheritAttrs: false
});
interface Props {
@@ -16,8 +16,6 @@ defineProps<Props>();
const value = defineModel<CommonType.IdType[] | null>('value', { required: false });
const attrs: SelectProps = useAttrs();
const { loading: roleLoading, startLoading: startRoleLoading, endLoading: endRoleLoading } = useLoading();
/** the enabled role options */
@@ -44,7 +42,7 @@ getRoleOptions();
v-model:value="value"
:loading="roleLoading"
:options="roleOptions"
v-bind="attrs"
v-bind="$attrs"
placeholder="请选择角色"
/>
</template>
+4 -6
View File
@@ -1,11 +1,11 @@
<script setup lang="ts">
import { ref, useAttrs } from 'vue';
import type { SelectProps } from 'naive-ui';
import { ref } from 'vue';
import { useLoading } from '@sa/hooks';
import { fetchGetUserSelect } from '@/service/api/system';
defineOptions({
name: 'UserSelect'
name: 'UserSelect',
inheritAttrs: false
});
interface Props {
@@ -16,8 +16,6 @@ defineProps<Props>();
const value = defineModel<CommonType.IdType | null>('value', { required: false });
const attrs: SelectProps = useAttrs();
const { loading: userLoading, startLoading: startUserLoading, endLoading: endUserLoading } = useLoading();
/** the enabled role options */
@@ -44,7 +42,7 @@ getUserOptions();
v-model:value="value"
:loading="userLoading"
:options="userOptions"
v-bind="attrs"
v-bind="$attrs"
placeholder="请选择用户"
/>
</template>
+1
View File
@@ -1,3 +1,4 @@
// @ts-expect-error ignore type error of svg icons
import 'virtual:svg-icons-register';
import 'uno.css';
import '../styles/css/global.css';
-1
View File
@@ -4,7 +4,6 @@
"jsx": "preserve",
"jsxImportSource": "vue",
"lib": ["DOM", "ESNext"],
"baseUrl": ".",
"module": "ESNext",
"moduleResolution": "bundler",
"paths": {