Compare commits

...

10 Commits

16 changed files with 761 additions and 921 deletions

View File

@@ -5,7 +5,6 @@
"antfu.unocss",
"dbaeumer.vscode-eslint",
"editorconfig.editorconfig",
"esbenp.prettier-vscode",
"lokalise.i18n-ally",
"mhutchie.git-graph",
"mikestead.dotenv",

View File

@@ -139,7 +139,7 @@ Refer to the [Code Synchronization](https://docs.soybeanjs.cn/guide/sync) docume
## Ecosystem
- [react-soybean-admin](https://github.com/mufeng889/react-soybean-admin): SoybeanAdmin based version of React.
- [skyroc-admin](https://github.com/Ohh-889/skyroc-admin): SoybeanAdmin's React version implementation.
- [electron-mock-admin](https://github.com/lixin59/electron-mock-api): A Mock Api management system that helps front-end developers quickly implement interface mocks.
- [T-Shell](https://github.com/TheBlindM/T-Shell): A terminal emulator and SSH client with configurable command prompts.
- [pea](https://github.com/haitang1894/pea) : Adopting SpringBoot3.2 + JDK21, MyBatis-Plus, SpringSecurity security framework, etc., suitable for the simple permission system developed by [soybean-admin](https://gitee.com/honghuangdc/soybean-admin).
@@ -151,6 +151,8 @@ Refer to the [Code Synchronization](https://docs.soybeanjs.cn/guide/sync) docume
- [ba](https://github.com/xiatianYa/Ba-Server): Backend service docking with soybean admin based on goFrame framework, adapted to dynamic routing, and interface authentication permissions.
- [soybean-admin-go](https://github.com/WgoW/soybean-admin-go):A Go backend service developed based on the Gin and GORM frameworks, integrated with the example branch of Soybean Admin. It supports dynamic routing and API permission authentication.
More ecosystem please refer to [Ecosystem](https://docs.soybeanjs.cn/awesome) document.
## How to Contribute
@@ -194,7 +196,7 @@ Here are the most active contributors from the past year. Thank you all for your
<div>
<p>QQ Group</p>
<img src="https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/qq-soybean-admin-4.jpg" style="width:200px" />
<img src="https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/qq-soybean-admin-5.jpg" style="width:200px" />
</div>
<!-- <div>
<p>WeChat Group</p>

View File

@@ -165,7 +165,7 @@ pnpm build
## 周边生态
- [react-soybean-admin](https://github.com/mufeng889/react-soybean-admin): 基于SoybeanAdmin的React版本.
- [skyroc-admin](https://github.com/Ohh-889/skyroc-admin): SoybeanAdmin的React版本实现.
- [electron-mock-admin](https://github.com/lixin59/electron-mock-api): 一个 Mock Api 管理系统,帮助前端开发伙伴快速实现接口的 mock。
- [T-Shell](https://github.com/TheBlindM/T-Shell): 是一个可配置命令提示的终端模拟器和 SSH 客户端。
- [pea](https://github.com/haitang1894/pea) : 采用SpringBoot3.2 + JDK21、MyBatis-Plus、SpringSecurity安全框架等适配 [soybean-admin](https://gitee.com/honghuangdc/soybean-admin) 开发的简单权限系统。
@@ -177,6 +177,8 @@ pnpm build
- [ba](https://github.com/xiatianYa/Ba-Server): 基于goFrame框架开发的后端服务对接soybean-admin,适配动态路由,接口鉴权限。
- [soybean-admin-go](https://github.com/WgoW/soybean-admin-go):基于gin+gorm框架开发的go语言后端服务对接soybean-admin的example分支,适配动态路由,接口鉴权限。
更多周边生态请翻阅 [周边生态](https://docs.soybeanjs.cn/zh/awesome) 文档。
## 如何贡献
@@ -222,7 +224,7 @@ pnpm build
<div>
<p>QQ交流群</p>
<img src="https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/qq-soybean-admin-4.jpg" style="width:200px" />
<img src="https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/qq-soybean-admin-5.jpg" style="width:200px" />
</div>
<!-- <div>
<p>微信群</p>

View File

@@ -62,45 +62,45 @@
"json5": "2.2.3",
"naive-ui": "2.43.1",
"nprogress": "0.2.0",
"pinia": "3.0.3",
"tailwind-merge": "3.3.1",
"vue": "3.5.22",
"pinia": "3.0.4",
"tailwind-merge": "3.4.0",
"vue": "3.5.24",
"vue-draggable-plus": "0.6.0",
"vue-i18n": "11.1.12",
"vue-router": "4.6.3"
},
"devDependencies": {
"@elegant-router/vue": "0.3.8",
"@iconify/json": "2.2.402",
"@iconify/json": "2.2.407",
"@sa/scripts": "workspace:*",
"@sa/uno-preset": "workspace:*",
"@soybeanjs/eslint-config": "1.7.1",
"@types/node": "24.9.2",
"@soybeanjs/eslint-config": "1.7.3",
"@types/node": "24.10.1",
"@types/nprogress": "0.2.3",
"@unocss/eslint-config": "66.5.4",
"@unocss/preset-icons": "66.5.4",
"@unocss/preset-uno": "66.5.4",
"@unocss/transformer-directives": "66.5.4",
"@unocss/transformer-variant-group": "66.5.4",
"@unocss/vite": "66.5.4",
"@unocss/eslint-config": "66.5.6",
"@unocss/preset-icons": "66.5.6",
"@unocss/preset-uno": "66.5.6",
"@unocss/transformer-directives": "66.5.6",
"@unocss/transformer-variant-group": "66.5.6",
"@unocss/vite": "66.5.6",
"@vitejs/plugin-vue": "6.0.1",
"@vitejs/plugin-vue-jsx": "5.1.1",
"consola": "3.4.2",
"eslint": "9.39.0",
"eslint": "9.39.1",
"eslint-plugin-vue": "10.5.1",
"kolorist": "1.8.0",
"sass": "1.93.3",
"sass": "1.94.0",
"simple-git-hooks": "2.13.1",
"tsx": "4.20.6",
"typescript": "5.9.3",
"unplugin-icons": "22.5.0",
"unplugin-vue-components": "30.0.0",
"vite": "7.1.12",
"vite": "7.2.2",
"vite-plugin-progress": "0.0.7",
"vite-plugin-svg-icons": "2.0.1",
"vite-plugin-vue-devtools": "8.0.3",
"vue-eslint-parser": "10.2.0",
"vue-tsc": "3.1.2"
"vue-tsc": "3.1.4"
},
"simple-git-hooks": {
"commit-msg": "pnpm sa git-commit-verify",

View File

@@ -11,7 +11,7 @@
},
"dependencies": {
"@sa/utils": "workspace:*",
"axios": "1.13.1",
"axios": "1.13.2",
"axios-retry": "4.5.0",
"qs": "6.14.0"
},

View File

@@ -15,7 +15,7 @@
"devDependencies": {
"@soybeanjs/changelog": "0.3.25",
"bumpp": "10.3.1",
"c12": "3.3.1",
"c12": "3.3.2",
"cac": "6.7.14",
"consola": "3.4.2",
"enquirer": "2.4.1",

1441
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,8 @@
<script setup lang="ts">
import { computed } from 'vue';
import { defu } from 'defu';
import { useThemeStore } from '@/store/modules/theme';
import { themeSettings } from '@/theme/settings';
import { $t } from '@/locales';
defineOptions({
@@ -31,6 +33,8 @@ type ThemePreset = Pick<
desc: string;
i18nkey?: string;
version: string;
/** Optional NaiveUI theme overrides */
naiveui?: App.Theme.NaiveUIThemeOverride;
};
const presetModules = import.meta.glob('@/theme/preset/*.json', { eager: true, import: 'default' });
@@ -76,7 +80,9 @@ const getPresetDesc = (preset: ThemePreset): string => {
}
};
const applyPreset = ({ themeScheme, grayscale, colourWeakness, layout, watermark, ...rest }: ThemePreset): void => {
const applyPreset = (preset: ThemePreset): void => {
const mergedPreset = defu(preset, themeSettings);
const { themeScheme, grayscale, colourWeakness, layout, watermark, naiveui, ...rest } = mergedPreset;
themeStore.setThemeScheme(themeScheme);
themeStore.setGrayscale(grayscale);
themeStore.setColourWeakness(colourWeakness);
@@ -96,6 +102,9 @@ const applyPreset = ({ themeScheme, grayscale, colourWeakness, layout, watermark
tokens: { ...rest.tokens }
});
// Apply NaiveUI theme overrides if present
themeStore.setNaiveThemeOverrides(naiveui);
window.$message?.success($t('theme.appearance.preset.applySuccess'));
};
</script>

View File

@@ -24,6 +24,9 @@ export const useThemeStore = defineStore(SetupStoreId.Theme, () => {
/** Theme settings */
const settings: Ref<App.Theme.ThemeSetting> = ref(initThemeSettings());
/** Optional NaiveUI theme overrides from preset */
const naiveThemeOverrides: Ref<App.Theme.NaiveUIThemeOverride | undefined> = ref(undefined);
/** Watermark time instance with controls */
const { now: watermarkTime, pause: pauseWatermarkTime, resume: resumeWatermarkTime } = useNow({ controls: true });
@@ -53,7 +56,7 @@ export const useThemeStore = defineStore(SetupStoreId.Theme, () => {
});
/** Naive theme */
const naiveTheme = computed(() => getNaiveTheme(themeColors.value, settings.value));
const naiveTheme = computed(() => getNaiveTheme(themeColors.value, settings.value, naiveThemeOverrides.value));
/**
* Settings json
@@ -198,6 +201,15 @@ export const useThemeStore = defineStore(SetupStoreId.Theme, () => {
}
}
/**
* Set NaiveUI theme overrides
*
* @param overrides NaiveUI theme overrides or undefined to clear
*/
function setNaiveThemeOverrides(overrides?: App.Theme.NaiveUIThemeOverride) {
naiveThemeOverrides.value = overrides;
}
/** Only run timer when watermark is visible and time display is enabled */
function updateWatermarkTimer() {
const { watermark } = settings.value;
@@ -284,6 +296,7 @@ export const useThemeStore = defineStore(SetupStoreId.Theme, () => {
updateThemeColors,
setThemeLayout,
setWatermarkEnableUserName,
setWatermarkEnableTime
setWatermarkEnableTime,
setNaiveThemeOverrides
};
});

View File

@@ -236,11 +236,15 @@ function getNaiveThemeColors(colors: App.Theme.ThemeColor, recommended = false)
/**
* Get naive theme
*
* @param settings Theme settings object.
* @param settings.recommendColor Whether to use recommended color palette.
* @param settings.themeRadius Border radius to use in the theme (in px).
* @param colors Theme colors
* @param settings Theme settings object
* @param overrides Optional manual overrides from preset
*/
export function getNaiveTheme(colors: App.Theme.ThemeColor, settings: App.Theme.ThemeSetting) {
export function getNaiveTheme(
colors: App.Theme.ThemeColor,
settings: App.Theme.ThemeSetting,
overrides?: GlobalThemeOverrides
) {
const { primary: colorLoading } = colors;
const theme: GlobalThemeOverrides = {
@@ -256,5 +260,7 @@ export function getNaiveTheme(colors: App.Theme.ThemeColor, settings: App.Theme.
}
};
return theme;
// If there are overrides, merge them with priority
// overrides has higher priority than auto-generated theme
return overrides ? defu(overrides, theme) : theme;
}

View File

@@ -2,10 +2,8 @@
"name": "Azir's Preset",
"desc": "It is a cold and elegant preset that Azir likes",
"i18nkey": "theme.appearance.preset.azir",
"version": "1.0.0",
"version": "1.0.1",
"themeScheme": "light",
"grayscale": false,
"colourWeakness": false,
"recommendColor": true,
"themeColor": "#78a878",
"otherColor": {
@@ -14,57 +12,7 @@
"warning": "#d4bb9d",
"error": "#c49a9a"
},
"themeRadius": 6,
"isInfoFollowPrimary": true,
"layout": {
"mode": "vertical-mix",
"scrollMode": "wrapper"
},
"page": {
"animate": true,
"animateMode": "zoom-fade"
},
"header": {
"height": 64,
"breadcrumb": {
"visible": true,
"showIcon": true
},
"multilingual": {
"visible": true
},
"globalSearch": {
"visible": true
}
},
"tab": {
"visible": true,
"cache": true,
"height": 48,
"mode": "chrome"
},
"fixedHeaderAndTab": true,
"sider": {
"inverted": false,
"width": 220,
"collapsedWidth": 64,
"mixWidth": 90,
"mixCollapsedWidth": 64,
"mixChildMenuWidth": 200
},
"footer": {
"visible": true,
"fixed": true,
"height": 56,
"right": true
},
"watermark": {
"visible": false,
"text": "SoybeanAdmin",
"enableUserName": false,
"enableTime": true,
"timeFormat": "YYYY-MM-DD HH:mm:ss"
},
"tokens": {
"light": {
"colors": {
@@ -86,5 +34,19 @@
"base-text": "rgb(224, 224, 224)"
}
}
},
"naiveui": {
"Alert": {
"borderRadiusMedium": "12px",
"fontWeightStrong": "600",
"paddingMedium": "0 20px"
},
"Card": {
"borderRadius": "16px",
"paddingMedium": "24px"
},
"Input": {
"borderRadius": "10px"
}
}
}

View File

@@ -2,33 +2,12 @@
"name": "Compact Preset",
"desc": "Compact layout preset for small screens",
"i18nkey": "theme.appearance.preset.compact",
"version": "1.0.0",
"themeScheme": "light",
"grayscale": false,
"colourWeakness": false,
"recommendColor": false,
"themeColor": "#646cff",
"otherColor": {
"info": "#2080f0",
"success": "#52c41a",
"warning": "#faad14",
"error": "#f5222d"
},
"version": "1.0.1",
"themeRadius": 6,
"isInfoFollowPrimary": true,
"layout": {
"mode": "vertical",
"scrollMode": "content"
},
"page": {
"animate": true,
"animateMode": "fade-slide"
},
"header": {
"height": 48,
"breadcrumb": {
"visible": true,
"showIcon": true
"visible": false
},
"multilingual": {
"visible": false
@@ -41,9 +20,9 @@
"visible": true,
"cache": true,
"height": 36,
"mode": "button"
"mode": "button",
"closeTabByMiddleClick": false
},
"fixedHeaderAndTab": true,
"sider": {
"inverted": false,
"width": 180,
@@ -53,38 +32,6 @@
"mixChildMenuWidth": 180
},
"footer": {
"visible": false,
"fixed": false,
"height": 40,
"right": true
},
"watermark": {
"visible": false,
"text": "SoybeanAdmin",
"enableUserName": false,
"enableTime": false,
"timeFormat": "YYYY-MM-DD HH:mm"
},
"tokens": {
"light": {
"colors": {
"container": "rgb(255, 255, 255)",
"layout": "rgb(247, 250, 252)",
"inverted": "rgb(0, 20, 40)",
"base-text": "rgb(31, 31, 31)"
},
"boxShadow": {
"header": "0 1px 2px rgb(0, 21, 41, 0.08)",
"sider": "2px 0 8px 0 rgb(29, 35, 41, 0.05)",
"tab": "0 1px 2px rgb(0, 21, 41, 0.08)"
}
},
"dark": {
"colors": {
"container": "rgb(28, 28, 28)",
"layout": "rgb(18, 18, 18)",
"base-text": "rgb(224, 224, 224)"
}
}
"visible": false
}
}

View File

@@ -2,12 +2,12 @@
"name": "Dark Preset",
"desc": "Dark theme preset for night time usage",
"i18nkey": "theme.appearance.preset.dark",
"version": "1.0.0",
"version": "1.0.1",
"themeScheme": "dark",
"grayscale": false,
"colourWeakness": false,
"recommendColor": false,
"themeColor": "#409eff",
"themeColor": "#646cff",
"otherColor": {
"info": "#2080f0",
"success": "#52c41a",
@@ -41,11 +41,12 @@
"visible": true,
"cache": true,
"height": 44,
"mode": "chrome"
"mode": "chrome",
"closeTabByMiddleClick": false
},
"fixedHeaderAndTab": true,
"sider": {
"inverted": true,
"inverted": false,
"width": 220,
"collapsedWidth": 64,
"mixWidth": 90,

View File

@@ -41,7 +41,8 @@
"visible": true,
"cache": true,
"height": 44,
"mode": "chrome"
"mode": "chrome",
"closeTabByMiddleClick": false
},
"fixedHeaderAndTab": true,
"sider": {

View File

@@ -4,6 +4,9 @@ declare namespace App {
namespace Theme {
type ColorPaletteNumber = import('@sa/color').ColorPaletteNumber;
/** NaiveUI theme overrides that can be specified in preset */
type NaiveUIThemeOverride = import('naive-ui').GlobalThemeOverrides;
/** Theme setting */
interface ThemeSetting {
/** Theme scheme */

View File

@@ -15,7 +15,7 @@ const gap = computed(() => (appStore.isMobile ? 0 : 16));
<template>
<NSpace vertical :size="16">
<NAlert :title="$t('common.warning')" type="warning">
<NAlert :title="$t('common.tip')" type="warning">
{{ $t('page.home.branchDesc') }}
</NAlert>
<HeaderBanner />