chore(merge): Merge Soybean branch main into dev

This commit is contained in:
xlsea
2026-05-23 10:07:48 +08:00
23 changed files with 1617 additions and 1598 deletions
+1 -1
View File
@@ -15,7 +15,7 @@ jobs:
steps: steps:
- name: Checkout Code - name: Checkout Code
uses: actions/checkout@v3 uses: actions/checkout@v6
with: with:
fetch-depth: 0 fetch-depth: 0
+3 -3
View File
@@ -12,13 +12,13 @@ jobs:
release: release:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v6
with: with:
fetch-depth: 0 fetch-depth: 0
- uses: actions/setup-node@v3 - uses: actions/setup-node@v6
with: with:
node-version: 18.x node-version: lts/*
- run: npx githublogen - run: npx githublogen
env: env:
+1
View File
@@ -33,6 +33,7 @@ package-lock.json
yarn.lock yarn.lock
.VSCodeCounter .VSCodeCounter
src-tauri/target
# VibeCoding state # VibeCoding state
.ai_state/ .ai_state/
-3
View File
@@ -1,4 +1 @@
registry=https://registry.npmmirror.com/ registry=https://registry.npmmirror.com/
shamefully-hoist=true
ignore-workspace-root-check=true
link-workspace-packages=true
+4 -1
View File
@@ -13,7 +13,10 @@
"i18n-ally.keystyle": "nested", "i18n-ally.keystyle": "nested",
"i18n-ally.localesPaths": ["src/locales/langs"], "i18n-ally.localesPaths": ["src/locales/langs"],
"i18n-ally.parsers.typescript.compilerOptions": { "i18n-ally.parsers.typescript.compilerOptions": {
"moduleResolution": "node" "moduleResolution": "node",
"ignoreDeprecations": "6.0",
"rootDir": ".",
"baseUrl": "."
}, },
"oxc.fmt.configPath": ".oxfmtrc.json", "oxc.fmt.configPath": ".oxfmtrc.json",
"unocss.root": ["./"], "unocss.root": ["./"],
+22 -22
View File
@@ -1,13 +1,13 @@
{ {
"name": "ruoyi-vue-plus", "name": "ruoyi-vue-plus",
"version": "2.1.0", "version": "2.2.0",
"description": "结合了 RuoYi-Vue-Plus 的强大后端功能和 Soybean Admin 的现代化前端特性,为开发者提供了完整的企业管理解决方案。", "description": "结合了 RuoYi-Vue-Plus 的强大后端功能和 Soybean Admin 的现代化前端特性,为开发者提供了完整的企业管理解决方案。",
"keywords": [ "keywords": [
"RuoYi-Vue-Plus", "RuoYi-Vue-Plus",
"Soybean Admin", "Soybean Admin",
"TypeScript", "TypeScript",
"UnoCSS", "UnoCSS",
"Vite7", "Vite8",
"Vue3 admin ", "Vue3 admin ",
"ant-design-vue v4", "ant-design-vue v4",
"naive-ui", "naive-ui",
@@ -61,19 +61,19 @@
}, },
"dependencies": { "dependencies": {
"@better-scroll/core": "2.5.1", "@better-scroll/core": "2.5.1",
"@iconify/vue": "5.0.0", "@iconify/vue": "5.0.1",
"@sa/axios": "workspace:*", "@sa/axios": "workspace:*",
"@sa/color": "workspace:*", "@sa/color": "workspace:*",
"@sa/hooks": "workspace:*", "@sa/hooks": "workspace:*",
"@sa/materials": "workspace:*", "@sa/materials": "workspace:*",
"@sa/utils": "workspace:*", "@sa/utils": "workspace:*",
"@types/streamsaver": "^2.0.5", "@types/streamsaver": "^2.0.5",
"@vueuse/core": "14.2.1", "@vueuse/core": "14.3.0",
"@wangeditor-next/editor": "^5.6.49", "@wangeditor-next/editor": "^5.7.4",
"@wangeditor-next/editor-for-vue": "^5.1.14", "@wangeditor-next/editor-for-vue": "^5.1.14",
"clipboard": "2.0.11", "clipboard": "2.0.11",
"dayjs": "1.11.20", "dayjs": "1.11.20",
"defu": "6.1.4", "defu": "6.1.7",
"echarts": "6.0.0", "echarts": "6.0.0",
"highlight.js": "^11.11.1", "highlight.js": "^11.11.1",
"jsencrypt": "^3.5.4", "jsencrypt": "^3.5.4",
@@ -82,41 +82,41 @@
"nprogress": "0.2.0", "nprogress": "0.2.0",
"pinia": "3.0.4", "pinia": "3.0.4",
"streamsaver": "^2.0.6", "streamsaver": "^2.0.6",
"tailwind-merge": "3.5.0", "tailwind-merge": "3.6.0",
"vue": "3.5.31", "vue": "3.5.34",
"vue-advanced-cropper": "^2.8.9", "vue-advanced-cropper": "^2.8.9",
"vue-draggable-plus": "0.6.1", "vue-draggable-plus": "0.6.1",
"vue-i18n": "11.3.0", "vue-i18n": "11.4.2",
"vue-router": "5.0.4" "vue-router": "5.0.7"
}, },
"devDependencies": { "devDependencies": {
"@elegant-router/vue": "0.3.8", "@elegant-router/vue": "0.3.8",
"@iconify/json": "2.2.456", "@iconify/json": "2.2.472",
"@sa/scripts": "workspace:*", "@sa/scripts": "workspace:*",
"@sa/uno-preset": "workspace:*", "@sa/uno-preset": "workspace:*",
"@soybeanjs/eslint-config-vue": "^0.0.2", "@soybeanjs/eslint-config-vue": "^0.0.2",
"@types/node": "25.5.0", "@types/node": "25.7.0",
"@types/nprogress": "0.2.3", "@types/nprogress": "0.2.3",
"@vitejs/plugin-vue": "6.0.5", "@vitejs/plugin-vue": "6.0.6",
"@vitejs/plugin-vue-jsx": "5.1.5", "@vitejs/plugin-vue-jsx": "5.1.5",
"consola": "3.4.2", "consola": "3.4.2",
"eslint": "10.1.0", "eslint": "10.3.0",
"kolorist": "1.8.0", "kolorist": "1.8.0",
"oxfmt": "^0.42.0", "oxfmt": "^0.49.0",
"oxlint": "^1.57.0", "oxlint": "^1.64.0",
"sass": "1.98.0", "sass": "1.99.0",
"simple-git-hooks": "2.13.1", "simple-git-hooks": "2.13.1",
"tsx": "4.21.0", "tsx": "4.21.0",
"typescript": "6.0.2", "typescript": "6.0.3",
"unocss": "^66.6.7", "unocss": "^66.6.8",
"unplugin-icons": "23.0.1", "unplugin-icons": "23.0.1",
"unplugin-vue-components": "32.0.0", "unplugin-vue-components": "32.0.0",
"vite": "8.0.3", "vite": "8.0.12",
"vite-plugin-progress": "0.0.7", "vite-plugin-progress": "0.0.7",
"vite-plugin-svg-icons": "2.0.1", "vite-plugin-svg-icons": "2.0.1",
"vite-plugin-vue-devtools": "8.1.1", "vite-plugin-vue-devtools": "8.1.2",
"vite-plugin-vue-transition-root-validator": "^0.1.0", "vite-plugin-vue-transition-root-validator": "^0.1.0",
"vue-tsc": "3.2.6" "vue-tsc": "3.2.8"
}, },
"simple-git-hooks": { "simple-git-hooks": {
"commit-msg": "pnpm sa git-commit-verify", "commit-msg": "pnpm sa git-commit-verify",
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@sa/alova", "name": "@sa/alova",
"version": "2.1.0", "version": "2.2.0",
"typesVersions": { "typesVersions": {
"*": { "*": {
"*": [ "*": [
+4 -4
View File
@@ -1,6 +1,6 @@
{ {
"name": "@sa/axios", "name": "@sa/axios",
"version": "2.1.0", "version": "2.2.0",
"typesVersions": { "typesVersions": {
"*": { "*": {
"*": [ "*": [
@@ -13,11 +13,11 @@
}, },
"dependencies": { "dependencies": {
"@sa/utils": "workspace:*", "@sa/utils": "workspace:*",
"axios": "1.14.0", "axios": "1.16.0",
"axios-retry": "4.5.0", "axios-retry": "4.5.0",
"qs": "6.15.0" "qs": "6.15.1"
}, },
"devDependencies": { "devDependencies": {
"@types/qs": "6.15.0" "@types/qs": "6.15.1"
} }
} }
+2 -2
View File
@@ -1,4 +1,4 @@
import axios, { AxiosError } from 'axios'; import { create, AxiosError } from 'axios';
import type { AxiosResponse, CreateAxiosDefaults, InternalAxiosRequestConfig } from 'axios'; import type { AxiosResponse, CreateAxiosDefaults, InternalAxiosRequestConfig } from 'axios';
import axiosRetry from 'axios-retry'; import axiosRetry from 'axios-retry';
import { nanoid } from '@sa/utils'; import { nanoid } from '@sa/utils';
@@ -22,7 +22,7 @@ function createCommonRequest<
const opts = createDefaultOptions<ResponseData, ApiData, State>(options); const opts = createDefaultOptions<ResponseData, ApiData, State>(options);
const axiosConf = createAxiosConfig(axiosConfig); const axiosConf = createAxiosConfig(axiosConfig);
const instance = axios.create(axiosConf); const instance = create(axiosConf);
const abortControllerMap = new Map<string, AbortController>(); const abortControllerMap = new Map<string, AbortController>();
+1 -1
View File
@@ -32,7 +32,7 @@ export async function transformResponse(response: AxiosResponse) {
const responseType: ResponseType = (response.config?.responseType as ResponseType) || 'json'; const responseType: ResponseType = (response.config?.responseType as ResponseType) || 'json';
if (responseType === 'json') return; if (responseType === 'json') return;
const isJson = response.headers['content-type']?.includes('application/json'); const isJson = (response.headers['content-type'] as string)?.includes('application/json');
if (!isJson) return; if (!isJson) return;
if (responseType === 'blob') { if (responseType === 'blob') {
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@sa/color", "name": "@sa/color",
"version": "2.1.0", "version": "2.2.0",
"typesVersions": { "typesVersions": {
"*": { "*": {
"*": [ "*": [
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@sa/hooks", "name": "@sa/hooks",
"version": "2.1.0", "version": "2.2.0",
"typesVersions": { "typesVersions": {
"*": { "*": {
"*": [ "*": [
+30 -69
View File
@@ -1,66 +1,31 @@
import { inject, provide } from 'vue'; import { inject, provide } from 'vue';
type ContextName = string | { name: string; key: string | symbol };
type ContextValue<T> = T extends (...args: any[]) => any ? ReturnType<T> : T;
type ContextProvider<T> = T extends (...args: any[]) => any ? T : (arg: T) => T;
type ContextConsumer<Context> = <N extends string | null | undefined = undefined>(
consumerName?: N,
defaultValue?: Context
) => N extends null | undefined ? Context | null : Context;
/** /**
* Use context * Creates a context provider and consumer pair.
* *
* @example * @param contextName - The name of the context. This can be a string or an object with a `name` and `key` property.
* ```ts * @param composable - An optional composable function that returns the context value. If not provided, the context value will be the first argument passed to the provider.
* // there are three vue files: A.vue, B.vue, C.vue, and A.vue is the parent component of B.vue and C.vue
*
* // context.ts
* import { ref } from 'vue';
* import { useContext } from '@sa/hooks';
*
* export const [provideDemoContext, useDemoContext] = useContext('demo', () => {
* const count = ref(0);
*
* function increment() {
* count.value++;
* }
*
* function decrement() {
* count.value--;
* }
*
* return {
* count,
* increment,
* decrement
* };
* })
* ``` // A.vue
* ```vue
* <template>
* <div>A</div>
* </template>
* <script setup lang="ts">
* import { provideDemoContext } from './context';
*
* provideDemoContext();
* // const { increment } = provideDemoContext(); // also can control the store in the parent component
* </script>
* ``` // B.vue
* ```vue
* <template>
* <div>B</div>
* </template>
* <script setup lang="ts">
* import { useDemoContext } from './context';
*
* const { count, increment } = useDemoContext();
* </script>
* ```;
*
* // C.vue is same as B.vue
*
* @param contextName Context name
* @param fn Context function
*/ */
export default function useContext<Arguments extends Array<any>, T>( export default function useContext<T>(
contextName: string, contextName: ContextName,
composable: (...args: Arguments) => T composable?: T extends (...args: any[]) => any ? T : never
) { ) {
const key = Symbol(contextName); type Context = ContextValue<T>;
const name = typeof contextName === 'string' ? contextName : contextName.name;
const key = typeof contextName === 'string' ? Symbol(contextName) : contextName.key;
/** /**
* Injects the context value. * Injects the context value.
@@ -70,27 +35,23 @@ export default function useContext<Arguments extends Array<any>, T>(
* @param defaultValue - The default value to return if the context is not provided. * @param defaultValue - The default value to return if the context is not provided.
* @returns The context value. * @returns The context value.
*/ */
const useInject = <N extends string | null | undefined = undefined>( const useInject = (consumerName?: string | null, defaultValue?: any) => {
consumerName?: N, const value = inject(key, defaultValue) ?? null;
defaultValue?: T
): N extends null | undefined ? T | null : T => {
const value = inject(key, defaultValue);
if (consumerName && !value) { if (consumerName != null && value === null) {
throw new Error(`\`${consumerName}\` must be used within \`${contextName}\``); throw new Error(`\`${consumerName}\` must be used within \`${name}\``);
} }
// @ts-expect-error - we want to return null if the value is undefined or null return value;
return value || null;
}; };
const useProvide = (...args: Arguments) => { const useProvide = (...args: any[]) => {
const value = composable(...args); const value = composable?.(...args) ?? args[0];
provide(key, value); provide(key, value);
return value; return value;
}; };
return [useProvide, useInject] as const; return [useProvide, useInject] as [ContextProvider<T>, ContextConsumer<Context>];
} }
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@sa/materials", "name": "@sa/materials",
"version": "2.1.0", "version": "2.2.0",
"typesVersions": { "typesVersions": {
"*": { "*": {
"*": [ "*": [
+5 -5
View File
@@ -1,6 +1,6 @@
{ {
"name": "@sa/scripts", "name": "@sa/scripts",
"version": "2.1.0", "version": "2.2.0",
"bin": { "bin": {
"sa": "./bin.ts" "sa": "./bin.ts"
}, },
@@ -15,15 +15,15 @@
".": "./src/index.ts" ".": "./src/index.ts"
}, },
"devDependencies": { "devDependencies": {
"@soybeanjs/changelog": "0.4.5", "@soybeanjs/changelog": "0.4.7",
"bumpp": "11.0.1", "bumpp": "11.1.0",
"c12": "3.3.3", "c12": "3.3.4",
"cac": "7.0.0", "cac": "7.0.0",
"consola": "3.4.2", "consola": "3.4.2",
"enquirer": "2.4.1", "enquirer": "2.4.1",
"execa": "9.6.1", "execa": "9.6.1",
"kolorist": "1.8.0", "kolorist": "1.8.0",
"npm-check-updates": "19.6.6", "npm-check-updates": "22.2.0",
"picomatch": "4.0.4", "picomatch": "4.0.4",
"rimraf": "6.1.3" "rimraf": "6.1.3"
} }
+42 -19
View File
@@ -17,7 +17,14 @@ interface PromptObject {
* @param lang * @param lang
*/ */
export async function gitCommit(lang: Lang = 'en-us') { export async function gitCommit(lang: Lang = 'en-us') {
const { gitCommitMessages, gitCommitTypes, gitCommitScopes } = locales[lang]; const { gitCommitMessages, gitCommitNoStaged, gitCommitTypes, gitCommitScopes } = locales[lang];
const stagedFiles = await execCommand('git', ['diff', '--cached', '--name-only']);
if (!stagedFiles) {
console.error(gitCommitNoStaged);
process.exit(1);
}
const typesChoices = gitCommitTypes.map(([value, msg]) => { const typesChoices = gitCommitTypes.map(([value, msg]) => {
const nameWithSuffix = `${value}:`; const nameWithSuffix = `${value}:`;
@@ -35,25 +42,41 @@ export async function gitCommit(lang: Lang = 'en-us') {
message: `${value.padEnd(30)} (${msg})` message: `${value.padEnd(30)} (${msg})`
})); }));
const result = await prompt<PromptObject>([ // Suppress ERR_USE_AFTER_CLOSE thrown by enquirer's readline cleanup on Ctrl+C (Node.js v24+)
{ const errorHandler = (error: NodeJS.ErrnoException) => {
name: 'types', if (error.code === 'ERR_USE_AFTER_CLOSE') {
type: 'select', process.exit(0);
message: gitCommitMessages.types,
choices: typesChoices
},
{
name: 'scopes',
type: 'select',
message: gitCommitMessages.scopes,
choices: scopesChoices
},
{
name: 'description',
type: 'text',
message: gitCommitMessages.description
} }
]); };
process.on('uncaughtException', errorHandler);
let result: PromptObject;
try {
result = await prompt<PromptObject>([
{
name: 'types',
type: 'select',
message: gitCommitMessages.types,
choices: typesChoices
},
{
name: 'scopes',
type: 'select',
message: gitCommitMessages.scopes,
choices: scopesChoices
},
{
name: 'description',
type: 'text',
message: gitCommitMessages.description
}
]);
} catch {
process.exit(0);
} finally {
process.off('uncaughtException', errorHandler);
}
const breaking = result.description.startsWith('!') ? '!' : ''; const breaking = result.description.startsWith('!') ? '!' : '';
+2
View File
@@ -37,6 +37,7 @@ export const locales = {
['release', '发布项目新版本'], ['release', '发布项目新版本'],
['other', '其他的变更'] ['other', '其他的变更']
] as [string, string][], ] as [string, string][],
gitCommitNoStaged: `${bgRed(' 错误 ')} ${red('暂存区没有文件,请先使用 git add 添加文件后再提交!')}`,
gitCommitVerify: `${bgRed(' 错误 ')} ${red('git 提交信息必须符合 Conventional Commits 标准!')}\n\n${green( gitCommitVerify: `${bgRed(' 错误 ')} ${red('git 提交信息必须符合 Conventional Commits 标准!')}\n\n${green(
'推荐使用命令 `pnpm commit` 生成符合 Conventional Commits 标准的提交信息。\n获取有关 Conventional Commits 的更多信息,请访问此链接: https://conventionalcommits.org' '推荐使用命令 `pnpm commit` 生成符合 Conventional Commits 标准的提交信息。\n获取有关 Conventional Commits 的更多信息,请访问此链接: https://conventionalcommits.org'
)}` )}`
@@ -75,6 +76,7 @@ export const locales = {
['release', 'release project'], ['release', 'release project'],
['other', 'other changes'] ['other', 'other changes']
] as [string, string][], ] as [string, string][],
gitCommitNoStaged: `${bgRed(' ERROR ')} ${red('No files in staging area, please use git add to stage files first!')}`,
gitCommitVerify: `${bgRed(' ERROR ')} ${red('git commit message must match the Conventional Commits standard!')}\n\n${green( gitCommitVerify: `${bgRed(' ERROR ')} ${red('git commit message must match the Conventional Commits standard!')}\n\n${green(
'Recommended to use the command `pnpm commit` to generate Conventional Commits compliant commit information.\nGet more info about Conventional Commits, follow this link: https://conventionalcommits.org' 'Recommended to use the command `pnpm commit` to generate Conventional Commits compliant commit information.\nGet more info about Conventional Commits, follow this link: https://conventionalcommits.org'
)}` )}`
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@sa/uno-preset", "name": "@sa/uno-preset",
"version": "2.1.0", "version": "2.2.0",
"typesVersions": { "typesVersions": {
"*": { "*": {
"*": [ "*": [
+2 -2
View File
@@ -1,6 +1,6 @@
{ {
"name": "@sa/utils", "name": "@sa/utils",
"version": "2.1.0", "version": "2.2.0",
"typesVersions": { "typesVersions": {
"*": { "*": {
"*": [ "*": [
@@ -16,7 +16,7 @@
"crypto-js": "4.2.0", "crypto-js": "4.2.0",
"klona": "2.0.6", "klona": "2.0.6",
"localforage": "1.10.0", "localforage": "1.10.0",
"nanoid": "5.1.7" "nanoid": "5.1.11"
}, },
"devDependencies": { "devDependencies": {
"@types/crypto-js": "4.2.2" "@types/crypto-js": "4.2.2"
+1478 -1459
View File
File diff suppressed because it is too large Load Diff
+8
View File
@@ -1,2 +1,10 @@
packages: packages:
- 'packages/*' - 'packages/*'
allowBuilds:
'@parcel/watcher': false
esbuild: false
simple-git-hooks: false
vue-demi: false
shamefullyHoist: true
ignoreWorkspaceRootCheck: true
linkWorkspacePackages: true
-1
View File
@@ -37,7 +37,6 @@ const editorConfig: Partial<IEditorConfig> = {
fieldName: 'file', fieldName: 'file',
meta: {}, meta: {},
headers: { headers: {
// @ts-expect-error ignore this type error
Authorization: `Bearer ${getToken()}`, Authorization: `Bearer ${getToken()}`,
clientid: import.meta.env.VITE_APP_CLIENT_ID! clientid: import.meta.env.VITE_APP_CLIENT_ID!
}, },
@@ -6,7 +6,13 @@ import { useRouteStore } from '@/store/modules/route';
import { useThemeStore } from '@/store/modules/theme'; import { useThemeStore } from '@/store/modules/theme';
import { useRouterPush } from '@/hooks/common/router'; import { useRouterPush } from '@/hooks/common/router';
export const [provideMixMenuContext, useMixMenuContext] = useContext('MixMenu', useMixMenu); export const [provideMixMenuContext, useMixMenuContext] = useContext(
{
name: 'MixMenu',
key: Symbol.for('MixMenu')
},
useMixMenu
);
function useMixMenu() { function useMixMenu() {
const route = useRoute(); const route = useRoute();