mirror of
https://github.com/soybeanjs/soybean-admin.git
synced 2025-11-26 09:48:00 +08:00
Compare commits
6 Commits
v2.0.0-bet
...
5790a12910
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5790a12910 | ||
|
|
4aba5c502b | ||
|
|
36b5b4a21b | ||
|
|
a0e486f76d | ||
|
|
2bce9b4be0 | ||
|
|
206d5a4459 |
@@ -27,12 +27,12 @@
|
|||||||
|
|
||||||
## Introduction
|
## Introduction
|
||||||
|
|
||||||
[`SoybeanAdmin`](https://github.com/soybeanjs/soybean-admin) is a clean, elegant, beautiful and powerful admin template, based on the latest front-end technology stack, including Vue3, Vite6, TypeScript, Pinia and UnoCSS. It has built-in rich theme configuration and components, strict code specifications, and an automated file routing system. In addition, it also uses the online mock data solution based on ApiFox. `SoybeanAdmin` provides you with a one-stop admin solution, no additional configuration, and out of the box. It is also a best practice for learning cutting-edge technologies quickly.
|
[`SoybeanAdmin`](https://github.com/soybeanjs/soybean-admin) is a clean, elegant, beautiful and powerful admin template, based on the latest front-end technology stack, including Vue3, Vite7, TypeScript, Pinia and UnoCSS. It has built-in rich theme configuration and components, strict code specifications, and an automated file routing system. In addition, it also uses the online mock data solution based on ApiFox. `SoybeanAdmin` provides you with a one-stop admin solution, no additional configuration, and out of the box. It is also a best practice for learning cutting-edge technologies quickly.
|
||||||
|
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
- **Cutting-edge technology application**: using the latest popular technology stack such as Vue3, Vite6, TypeScript, Pinia and UnoCSS.
|
- **Cutting-edge technology application**: using the latest popular technology stack such as Vue3, Vite7, TypeScript, Pinia and UnoCSS.
|
||||||
- **Clear project architecture**: using pnpm monorepo architecture, clear structure, elegant and easy to understand.
|
- **Clear project architecture**: using pnpm monorepo architecture, clear structure, elegant and easy to understand.
|
||||||
- **Strict code specifications**: follow the [SoybeanJS specification](https://docs.soybeanjs.cn/standard), integrate eslint, prettier and simple-git-hooks to ensure the code is standardized.
|
- **Strict code specifications**: follow the [SoybeanJS specification](https://docs.soybeanjs.cn/standard), integrate eslint, prettier and simple-git-hooks to ensure the code is standardized.
|
||||||
- **TypeScript**: support strict type checking to improve code maintainability.
|
- **TypeScript**: support strict type checking to improve code maintainability.
|
||||||
|
|||||||
@@ -26,11 +26,11 @@
|
|||||||
|
|
||||||
## 简介
|
## 简介
|
||||||
|
|
||||||
[`SoybeanAdmin`](https://github.com/soybeanjs/soybean-admin) 是一个清新优雅、高颜值且功能强大的后台管理模板,基于最新的前端技术栈,包括 Vue3, Vite6, TypeScript, Pinia 和 UnoCSS。它内置了丰富的主题配置和组件,代码规范严谨,实现了自动化的文件路由系统。此外,它还采用了基于 ApiFox 的在线Mock数据方案。`SoybeanAdmin` 为您提供了一站式的后台管理解决方案,无需额外配置,开箱即用。同样是一个快速学习前沿技术的最佳实践。
|
[`SoybeanAdmin`](https://github.com/soybeanjs/soybean-admin) 是一个清新优雅、高颜值且功能强大的后台管理模板,基于最新的前端技术栈,包括 Vue3, Vite7, TypeScript, Pinia 和 UnoCSS。它内置了丰富的主题配置和组件,代码规范严谨,实现了自动化的文件路由系统。此外,它还采用了基于 ApiFox 的在线Mock数据方案。`SoybeanAdmin` 为您提供了一站式的后台管理解决方案,无需额外配置,开箱即用。同样是一个快速学习前沿技术的最佳实践。
|
||||||
|
|
||||||
## 特性
|
## 特性
|
||||||
|
|
||||||
- **前沿技术应用**:采用 Vue3, Vite6, TypeScript, Pinia 和 UnoCSS 等最新流行的技术栈。
|
- **前沿技术应用**:采用 Vue3, Vite7, TypeScript, Pinia 和 UnoCSS 等最新流行的技术栈。
|
||||||
- **清晰的项目架构**:采用 pnpm monorepo 架构,结构清晰,优雅易懂。
|
- **清晰的项目架构**:采用 pnpm monorepo 架构,结构清晰,优雅易懂。
|
||||||
- **严格的代码规范**:遵循 [SoybeanJS 规范](https://docs.soybeanjs.cn/zh/standard),集成了eslint, prettier 和 simple-git-hooks,保证代码的规范性。
|
- **严格的代码规范**:遵循 [SoybeanJS 规范](https://docs.soybeanjs.cn/zh/standard),集成了eslint, prettier 和 simple-git-hooks,保证代码的规范性。
|
||||||
- **TypeScript**: 支持严格的类型检查,提高代码的可维护性。
|
- **TypeScript**: 支持严格的类型检查,提高代码的可维护性。
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"name": "soybean-admin",
|
"name": "soybean-admin",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"version": "1.3.15",
|
"version": "1.3.15",
|
||||||
"description": "A fresh and elegant admin template, based on Vue3、Vite6、TypeScript、NaiveUI and UnoCSS. 一个基于Vue3、Vite6、TypeScript、NaiveUI and UnoCSS的清新优雅的中后台模版。",
|
"description": "A fresh and elegant admin template, based on Vue3、Vite7、TypeScript、NaiveUI and UnoCSS. 一个基于Vue3、Vite7、TypeScript、NaiveUI and UnoCSS的清新优雅的中后台模版。",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "Soybean",
|
"name": "Soybean",
|
||||||
"email": "soybeanjs@outlook.com",
|
"email": "soybeanjs@outlook.com",
|
||||||
@@ -19,7 +19,7 @@
|
|||||||
"keywords": [
|
"keywords": [
|
||||||
"Vue3 admin ",
|
"Vue3 admin ",
|
||||||
"vue-admin-template",
|
"vue-admin-template",
|
||||||
"Vite6",
|
"Vite7",
|
||||||
"TypeScript",
|
"TypeScript",
|
||||||
"naive-ui",
|
"naive-ui",
|
||||||
"naive-ui-admin",
|
"naive-ui-admin",
|
||||||
|
|||||||
@@ -127,7 +127,6 @@ function handleClickMask() {
|
|||||||
:class="[
|
:class="[
|
||||||
style['layout-header'],
|
style['layout-header'],
|
||||||
commonClass,
|
commonClass,
|
||||||
headerClass,
|
|
||||||
headerLeftGapClass,
|
headerLeftGapClass,
|
||||||
{ 'absolute top-0 left-0 w-full': fixedHeaderAndTab }
|
{ 'absolute top-0 left-0 w-full': fixedHeaderAndTab }
|
||||||
]"
|
]"
|
||||||
|
|||||||
@@ -6,12 +6,6 @@ interface AdminLayoutHeaderConfig {
|
|||||||
* @default true
|
* @default true
|
||||||
*/
|
*/
|
||||||
headerVisible?: boolean;
|
headerVisible?: boolean;
|
||||||
/**
|
|
||||||
* Header class
|
|
||||||
*
|
|
||||||
* @default ''
|
|
||||||
*/
|
|
||||||
headerClass?: string;
|
|
||||||
/**
|
/**
|
||||||
* Header height
|
* Header height
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import { NConfigProvider, darkTheme } from 'naive-ui';
|
|||||||
import type { WatermarkProps } from 'naive-ui';
|
import type { WatermarkProps } from 'naive-ui';
|
||||||
import { useAppStore } from './store/modules/app';
|
import { useAppStore } from './store/modules/app';
|
||||||
import { useThemeStore } from './store/modules/theme';
|
import { useThemeStore } from './store/modules/theme';
|
||||||
import { useAuthStore } from './store/modules/auth';
|
|
||||||
import { naiveDateLocales, naiveLocales } from './locales/naive';
|
import { naiveDateLocales, naiveLocales } from './locales/naive';
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
@@ -13,7 +12,6 @@ defineOptions({
|
|||||||
|
|
||||||
const appStore = useAppStore();
|
const appStore = useAppStore();
|
||||||
const themeStore = useThemeStore();
|
const themeStore = useThemeStore();
|
||||||
const authStore = useAuthStore();
|
|
||||||
|
|
||||||
const naiveDarkTheme = computed(() => (themeStore.darkMode ? darkTheme : undefined));
|
const naiveDarkTheme = computed(() => (themeStore.darkMode ? darkTheme : undefined));
|
||||||
|
|
||||||
@@ -26,13 +24,8 @@ const naiveDateLocale = computed(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const watermarkProps = computed<WatermarkProps>(() => {
|
const watermarkProps = computed<WatermarkProps>(() => {
|
||||||
const content =
|
|
||||||
themeStore.watermark.enableUserName && authStore.userInfo.userName
|
|
||||||
? authStore.userInfo.userName
|
|
||||||
: themeStore.watermark.text;
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
content,
|
content: themeStore.watermarkContent,
|
||||||
cross: true,
|
cross: true,
|
||||||
fullscreen: true,
|
fullscreen: true,
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
|
|||||||
@@ -5,9 +5,9 @@ export const GLOBAL_HEADER_MENU_ID = '__GLOBAL_HEADER_MENU__';
|
|||||||
export const GLOBAL_SIDER_MENU_ID = '__GLOBAL_SIDER_MENU__';
|
export const GLOBAL_SIDER_MENU_ID = '__GLOBAL_SIDER_MENU__';
|
||||||
|
|
||||||
export const themeSchemaRecord: Record<UnionKey.ThemeScheme, App.I18n.I18nKey> = {
|
export const themeSchemaRecord: Record<UnionKey.ThemeScheme, App.I18n.I18nKey> = {
|
||||||
light: 'theme.themeSchema.light',
|
light: 'theme.appearance.themeSchema.light',
|
||||||
dark: 'theme.themeSchema.dark',
|
dark: 'theme.appearance.themeSchema.dark',
|
||||||
auto: 'theme.themeSchema.auto'
|
auto: 'theme.appearance.themeSchema.auto'
|
||||||
};
|
};
|
||||||
|
|
||||||
export const themeSchemaOptions = transformRecordToOption(themeSchemaRecord);
|
export const themeSchemaOptions = transformRecordToOption(themeSchemaRecord);
|
||||||
@@ -21,45 +21,56 @@ export const loginModuleRecord: Record<UnionKey.LoginModule, App.I18n.I18nKey> =
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const themeLayoutModeRecord: Record<UnionKey.ThemeLayoutMode, App.I18n.I18nKey> = {
|
export const themeLayoutModeRecord: Record<UnionKey.ThemeLayoutMode, App.I18n.I18nKey> = {
|
||||||
vertical: 'theme.layoutMode.vertical',
|
vertical: 'theme.layout.layoutMode.vertical',
|
||||||
'vertical-mix': 'theme.layoutMode.vertical-mix',
|
'vertical-mix': 'theme.layout.layoutMode.vertical-mix',
|
||||||
horizontal: 'theme.layoutMode.horizontal',
|
horizontal: 'theme.layout.layoutMode.horizontal',
|
||||||
'horizontal-mix': 'theme.layoutMode.horizontal-mix'
|
'top-hybrid-sidebar-first': 'theme.layout.layoutMode.top-hybrid-sidebar-first',
|
||||||
|
'top-hybrid-header-first': 'theme.layout.layoutMode.top-hybrid-header-first'
|
||||||
};
|
};
|
||||||
|
|
||||||
export const themeLayoutModeOptions = transformRecordToOption(themeLayoutModeRecord);
|
export const themeLayoutModeOptions = transformRecordToOption(themeLayoutModeRecord);
|
||||||
|
|
||||||
export const themeScrollModeRecord: Record<UnionKey.ThemeScrollMode, App.I18n.I18nKey> = {
|
export const themeScrollModeRecord: Record<UnionKey.ThemeScrollMode, App.I18n.I18nKey> = {
|
||||||
wrapper: 'theme.scrollMode.wrapper',
|
wrapper: 'theme.layout.content.scrollMode.wrapper',
|
||||||
content: 'theme.scrollMode.content'
|
content: 'theme.layout.content.scrollMode.content'
|
||||||
};
|
};
|
||||||
|
|
||||||
export const themeScrollModeOptions = transformRecordToOption(themeScrollModeRecord);
|
export const themeScrollModeOptions = transformRecordToOption(themeScrollModeRecord);
|
||||||
|
|
||||||
export const themeTabModeRecord: Record<UnionKey.ThemeTabMode, App.I18n.I18nKey> = {
|
export const themeTabModeRecord: Record<UnionKey.ThemeTabMode, App.I18n.I18nKey> = {
|
||||||
chrome: 'theme.tab.mode.chrome',
|
chrome: 'theme.layout.tab.mode.chrome',
|
||||||
button: 'theme.tab.mode.button'
|
button: 'theme.layout.tab.mode.button'
|
||||||
};
|
};
|
||||||
|
|
||||||
export const themeTabModeOptions = transformRecordToOption(themeTabModeRecord);
|
export const themeTabModeOptions = transformRecordToOption(themeTabModeRecord);
|
||||||
|
|
||||||
export const themePageAnimationModeRecord: Record<UnionKey.ThemePageAnimateMode, App.I18n.I18nKey> = {
|
export const themePageAnimationModeRecord: Record<UnionKey.ThemePageAnimateMode, App.I18n.I18nKey> = {
|
||||||
'fade-slide': 'theme.page.mode.fade-slide',
|
'fade-slide': 'theme.layout.content.page.mode.fade-slide',
|
||||||
fade: 'theme.page.mode.fade',
|
fade: 'theme.layout.content.page.mode.fade',
|
||||||
'fade-bottom': 'theme.page.mode.fade-bottom',
|
'fade-bottom': 'theme.layout.content.page.mode.fade-bottom',
|
||||||
'fade-scale': 'theme.page.mode.fade-scale',
|
'fade-scale': 'theme.layout.content.page.mode.fade-scale',
|
||||||
'zoom-fade': 'theme.page.mode.zoom-fade',
|
'zoom-fade': 'theme.layout.content.page.mode.zoom-fade',
|
||||||
'zoom-out': 'theme.page.mode.zoom-out',
|
'zoom-out': 'theme.layout.content.page.mode.zoom-out',
|
||||||
none: 'theme.page.mode.none'
|
none: 'theme.layout.content.page.mode.none'
|
||||||
};
|
};
|
||||||
|
|
||||||
export const themePageAnimationModeOptions = transformRecordToOption(themePageAnimationModeRecord);
|
export const themePageAnimationModeOptions = transformRecordToOption(themePageAnimationModeRecord);
|
||||||
|
|
||||||
export const resetCacheStrategyRecord: Record<UnionKey.ResetCacheStrategy, App.I18n.I18nKey> = {
|
export const resetCacheStrategyRecord: Record<UnionKey.ResetCacheStrategy, App.I18n.I18nKey> = {
|
||||||
close: 'theme.resetCacheStrategy.close',
|
close: 'theme.layout.resetCacheStrategy.close',
|
||||||
refresh: 'theme.resetCacheStrategy.refresh'
|
refresh: 'theme.layout.resetCacheStrategy.refresh'
|
||||||
};
|
};
|
||||||
|
|
||||||
export const resetCacheStrategyOptions = transformRecordToOption(resetCacheStrategyRecord);
|
export const resetCacheStrategyOptions = transformRecordToOption(resetCacheStrategyRecord);
|
||||||
|
|
||||||
export const DARK_CLASS = 'dark';
|
export const DARK_CLASS = 'dark';
|
||||||
|
|
||||||
|
export const watermarkTimeFormatOptions = [
|
||||||
|
{ label: 'YYYY-MM-DD HH:mm', value: 'YYYY-MM-DD HH:mm' },
|
||||||
|
{ label: 'YYYY-MM-DD HH:mm:ss', value: 'YYYY-MM-DD HH:mm:ss' },
|
||||||
|
{ label: 'YYYY/MM/DD HH:mm', value: 'YYYY/MM/DD HH:mm' },
|
||||||
|
{ label: 'YYYY/MM/DD HH:mm:ss', value: 'YYYY/MM/DD HH:mm:ss' },
|
||||||
|
{ label: 'HH:mm', value: 'HH:mm' },
|
||||||
|
{ label: 'HH:mm:ss', value: 'HH:mm:ss' },
|
||||||
|
{ label: 'MM-DD HH:mm', value: 'MM-DD HH:mm' }
|
||||||
|
];
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ const layoutMode = computed(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const headerProps = computed(() => {
|
const headerProps = computed(() => {
|
||||||
const { mode, reverseHorizontalMix } = themeStore.layout;
|
const { mode } = themeStore.layout;
|
||||||
|
|
||||||
const headerPropsConfig: Record<UnionKey.ThemeLayoutMode, App.Global.HeaderProps> = {
|
const headerPropsConfig: Record<UnionKey.ThemeLayoutMode, App.Global.HeaderProps> = {
|
||||||
vertical: {
|
vertical: {
|
||||||
@@ -47,10 +47,15 @@ const headerProps = computed(() => {
|
|||||||
showMenu: true,
|
showMenu: true,
|
||||||
showMenuToggler: false
|
showMenuToggler: false
|
||||||
},
|
},
|
||||||
'horizontal-mix': {
|
'top-hybrid-sidebar-first': {
|
||||||
showLogo: true,
|
showLogo: true,
|
||||||
showMenu: true,
|
showMenu: true,
|
||||||
showMenuToggler: reverseHorizontalMix && isActiveFirstLevelMenuHasChildren.value
|
showMenuToggler: false
|
||||||
|
},
|
||||||
|
'top-hybrid-header-first': {
|
||||||
|
showLogo: true,
|
||||||
|
showMenu: true,
|
||||||
|
showMenuToggler: isActiveFirstLevelMenuHasChildren.value
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -61,21 +66,22 @@ const siderVisible = computed(() => themeStore.layout.mode !== 'horizontal');
|
|||||||
|
|
||||||
const isVerticalMix = computed(() => themeStore.layout.mode === 'vertical-mix');
|
const isVerticalMix = computed(() => themeStore.layout.mode === 'vertical-mix');
|
||||||
|
|
||||||
const isHorizontalMix = computed(() => themeStore.layout.mode === 'horizontal-mix');
|
const isTopHybridSidebarFirst = computed(() => themeStore.layout.mode === 'top-hybrid-sidebar-first');
|
||||||
|
|
||||||
|
const isTopHybridHeaderFirst = computed(() => themeStore.layout.mode === 'top-hybrid-header-first');
|
||||||
|
|
||||||
const siderWidth = computed(() => getSiderWidth());
|
const siderWidth = computed(() => getSiderWidth());
|
||||||
|
|
||||||
const siderCollapsedWidth = computed(() => getSiderCollapsedWidth());
|
const siderCollapsedWidth = computed(() => getSiderCollapsedWidth());
|
||||||
|
|
||||||
function getSiderWidth() {
|
function getSiderWidth() {
|
||||||
const { reverseHorizontalMix } = themeStore.layout;
|
|
||||||
const { width, mixWidth, mixChildMenuWidth } = themeStore.sider;
|
const { width, mixWidth, mixChildMenuWidth } = themeStore.sider;
|
||||||
|
|
||||||
if (isHorizontalMix.value && reverseHorizontalMix) {
|
if (isTopHybridHeaderFirst.value) {
|
||||||
return isActiveFirstLevelMenuHasChildren.value ? width : 0;
|
return isActiveFirstLevelMenuHasChildren.value ? width : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
let w = isVerticalMix.value || isHorizontalMix.value ? mixWidth : width;
|
let w = isVerticalMix.value || isTopHybridSidebarFirst.value ? mixWidth : width;
|
||||||
|
|
||||||
if (isVerticalMix.value && appStore.mixSiderFixed && childLevelMenus.value.length) {
|
if (isVerticalMix.value && appStore.mixSiderFixed && childLevelMenus.value.length) {
|
||||||
w += mixChildMenuWidth;
|
w += mixChildMenuWidth;
|
||||||
@@ -85,14 +91,13 @@ function getSiderWidth() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getSiderCollapsedWidth() {
|
function getSiderCollapsedWidth() {
|
||||||
const { reverseHorizontalMix } = themeStore.layout;
|
|
||||||
const { collapsedWidth, mixCollapsedWidth, mixChildMenuWidth } = themeStore.sider;
|
const { collapsedWidth, mixCollapsedWidth, mixChildMenuWidth } = themeStore.sider;
|
||||||
|
|
||||||
if (isHorizontalMix.value && reverseHorizontalMix) {
|
if (isTopHybridHeaderFirst.value) {
|
||||||
return isActiveFirstLevelMenuHasChildren.value ? collapsedWidth : 0;
|
return isActiveFirstLevelMenuHasChildren.value ? collapsedWidth : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
let w = isVerticalMix.value || isHorizontalMix.value ? mixCollapsedWidth : collapsedWidth;
|
let w = isVerticalMix.value || isTopHybridSidebarFirst.value ? mixCollapsedWidth : collapsedWidth;
|
||||||
|
|
||||||
if (isVerticalMix.value && appStore.mixSiderFixed && childLevelMenus.value.length) {
|
if (isVerticalMix.value && appStore.mixSiderFixed && childLevelMenus.value.length) {
|
||||||
w += mixChildMenuWidth;
|
w += mixChildMenuWidth;
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ import { useThemeStore } from '@/store/modules/theme';
|
|||||||
import VerticalMenu from './modules/vertical-menu.vue';
|
import VerticalMenu from './modules/vertical-menu.vue';
|
||||||
import VerticalMixMenu from './modules/vertical-mix-menu.vue';
|
import VerticalMixMenu from './modules/vertical-mix-menu.vue';
|
||||||
import HorizontalMenu from './modules/horizontal-menu.vue';
|
import HorizontalMenu from './modules/horizontal-menu.vue';
|
||||||
import HorizontalMixMenu from './modules/horizontal-mix-menu.vue';
|
import TopHybridSidebarFirst from './modules/top-hybrid-sidebar-first.vue';
|
||||||
import ReversedHorizontalMixMenu from './modules/reversed-horizontal-mix-menu.vue';
|
import TopHybridHeaderFirst from './modules/top-hybrid-header-first.vue';
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'GlobalMenu'
|
name: 'GlobalMenu'
|
||||||
@@ -21,7 +21,8 @@ const activeMenu = computed(() => {
|
|||||||
vertical: VerticalMenu,
|
vertical: VerticalMenu,
|
||||||
'vertical-mix': VerticalMixMenu,
|
'vertical-mix': VerticalMixMenu,
|
||||||
horizontal: HorizontalMenu,
|
horizontal: HorizontalMenu,
|
||||||
'horizontal-mix': themeStore.layout.reverseHorizontalMix ? ReversedHorizontalMixMenu : HorizontalMixMenu
|
'top-hybrid-sidebar-first': TopHybridSidebarFirst,
|
||||||
|
'top-hybrid-header-first': TopHybridHeaderFirst
|
||||||
};
|
};
|
||||||
|
|
||||||
return menuMap[themeStore.layout.mode];
|
return menuMap[themeStore.layout.mode];
|
||||||
|
|||||||
@@ -13,9 +13,15 @@ const appStore = useAppStore();
|
|||||||
const themeStore = useThemeStore();
|
const themeStore = useThemeStore();
|
||||||
|
|
||||||
const isVerticalMix = computed(() => themeStore.layout.mode === 'vertical-mix');
|
const isVerticalMix = computed(() => themeStore.layout.mode === 'vertical-mix');
|
||||||
const isHorizontalMix = computed(() => themeStore.layout.mode === 'horizontal-mix');
|
const isTopHybridSidebarFirst = computed(() => themeStore.layout.mode === 'top-hybrid-sidebar-first');
|
||||||
const darkMenu = computed(() => !themeStore.darkMode && !isHorizontalMix.value && themeStore.sider.inverted);
|
const isTopHybridHeaderFirst = computed(() => themeStore.layout.mode === 'top-hybrid-header-first');
|
||||||
const showLogo = computed(() => !isVerticalMix.value && !isHorizontalMix.value);
|
const darkMenu = computed(
|
||||||
|
() =>
|
||||||
|
!themeStore.darkMode && !isTopHybridSidebarFirst.value && !isTopHybridHeaderFirst.value && themeStore.sider.inverted
|
||||||
|
);
|
||||||
|
const showLogo = computed(
|
||||||
|
() => !isVerticalMix.value && !isTopHybridSidebarFirst.value && !isTopHybridHeaderFirst.value
|
||||||
|
);
|
||||||
const menuWrapperClass = computed(() => (showLogo.value ? 'flex-1-hidden' : 'h-full'));
|
const menuWrapperClass = computed(() => (showLogo.value ? 'flex-1-hidden' : 'h-full'));
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ type LayoutConfig = Record<
|
|||||||
UnionKey.ThemeLayoutMode,
|
UnionKey.ThemeLayoutMode,
|
||||||
{
|
{
|
||||||
placement: PopoverPlacement;
|
placement: PopoverPlacement;
|
||||||
headerClass: string;
|
|
||||||
menuClass: string;
|
menuClass: string;
|
||||||
mainClass: string;
|
mainClass: string;
|
||||||
}
|
}
|
||||||
@@ -36,25 +35,26 @@ type LayoutConfig = Record<
|
|||||||
const layoutConfig: LayoutConfig = {
|
const layoutConfig: LayoutConfig = {
|
||||||
vertical: {
|
vertical: {
|
||||||
placement: 'bottom',
|
placement: 'bottom',
|
||||||
headerClass: '',
|
|
||||||
menuClass: 'w-1/3 h-full',
|
menuClass: 'w-1/3 h-full',
|
||||||
mainClass: 'w-2/3 h-3/4'
|
mainClass: 'w-2/3 h-3/4'
|
||||||
},
|
},
|
||||||
'vertical-mix': {
|
'vertical-mix': {
|
||||||
placement: 'bottom',
|
placement: 'bottom',
|
||||||
headerClass: '',
|
|
||||||
menuClass: 'w-1/4 h-full',
|
menuClass: 'w-1/4 h-full',
|
||||||
mainClass: 'w-2/3 h-3/4'
|
mainClass: 'w-2/3 h-3/4'
|
||||||
},
|
},
|
||||||
horizontal: {
|
horizontal: {
|
||||||
placement: 'bottom',
|
placement: 'bottom',
|
||||||
headerClass: '',
|
|
||||||
menuClass: 'w-full h-1/4',
|
menuClass: 'w-full h-1/4',
|
||||||
mainClass: 'w-full h-3/4'
|
mainClass: 'w-full h-3/4'
|
||||||
},
|
},
|
||||||
'horizontal-mix': {
|
'top-hybrid-sidebar-first': {
|
||||||
|
placement: 'bottom',
|
||||||
|
menuClass: 'w-full h-1/4',
|
||||||
|
mainClass: 'w-2/3 h-3/4'
|
||||||
|
},
|
||||||
|
'top-hybrid-header-first': {
|
||||||
placement: 'bottom',
|
placement: 'bottom',
|
||||||
headerClass: '',
|
|
||||||
menuClass: 'w-full h-1/4',
|
menuClass: 'w-full h-1/4',
|
||||||
mainClass: 'w-2/3 h-3/4'
|
mainClass: 'w-2/3 h-3/4'
|
||||||
}
|
}
|
||||||
@@ -68,25 +68,27 @@ function handleChangeMode(mode: UnionKey.ThemeLayoutMode) {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="flex-center flex-wrap gap-x-32px gap-y-16px">
|
<div class="grid grid-cols-2 gap-x-16px gap-y-12px md:grid-cols-3">
|
||||||
<div
|
<div
|
||||||
v-for="(item, key) in layoutConfig"
|
v-for="(item, key) in layoutConfig"
|
||||||
:key="key"
|
:key="key"
|
||||||
class="flex cursor-pointer border-2px rounded-6px hover:border-primary"
|
class="flex-col-center cursor-pointer"
|
||||||
:class="[mode === key ? 'border-primary' : 'border-transparent']"
|
|
||||||
@click="handleChangeMode(key)"
|
@click="handleChangeMode(key)"
|
||||||
>
|
>
|
||||||
<NTooltip :placement="item.placement">
|
<NTooltip :placement="item.placement">
|
||||||
<template #trigger>
|
<template #trigger>
|
||||||
<div
|
<div
|
||||||
class="h-64px w-96px gap-6px rd-4px p-6px shadow dark:shadow-coolGray-5"
|
class="h-64px w-96px gap-6px rd-4px p-6px shadow ring-2 ring-transparent transition-all hover:ring-primary"
|
||||||
:class="[key.includes('vertical') ? 'flex' : 'flex-col']"
|
:class="{ '!ring-primary': mode === key }"
|
||||||
>
|
>
|
||||||
<slot :name="key"></slot>
|
<div class="h-full w-full gap-1" :class="[key.includes('vertical') ? 'flex' : 'flex-col']">
|
||||||
|
<slot :name="key"></slot>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
{{ $t(themeLayoutModeRecord[key]) }}
|
{{ $t(`theme.layout.layoutMode.${key}_detail`) }}
|
||||||
</NTooltip>
|
</NTooltip>
|
||||||
|
<p class="mt-8px text-12px">{{ $t(themeLayoutModeRecord[key]) }}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { computed, ref } from 'vue';
|
||||||
import { useAppStore } from '@/store/modules/app';
|
import { useAppStore } from '@/store/modules/app';
|
||||||
import { $t } from '@/locales';
|
import { $t } from '@/locales';
|
||||||
import DarkMode from './modules/dark-mode.vue';
|
import AppearanceSettings from './modules/appearance/index.vue';
|
||||||
import LayoutMode from './modules/layout-mode.vue';
|
import LayoutSettings from './modules/layout/index.vue';
|
||||||
import ThemeColor from './modules/theme-color.vue';
|
import GeneralSettings from './modules/general/index.vue';
|
||||||
import PageFun from './modules/page-fun.vue';
|
|
||||||
import ConfigOperation from './modules/config-operation.vue';
|
import ConfigOperation from './modules/config-operation.vue';
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
@@ -12,15 +12,35 @@ defineOptions({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const appStore = useAppStore();
|
const appStore = useAppStore();
|
||||||
|
const activeTab = ref('appearance');
|
||||||
|
|
||||||
|
const drawerWidth = computed(() => {
|
||||||
|
// On mobile devices, use 90% of viewport width with a maximum of 400px
|
||||||
|
if (appStore.isMobile) {
|
||||||
|
return 'min(90vw, 400px)';
|
||||||
|
}
|
||||||
|
|
||||||
|
return 460;
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<NDrawer v-model:show="appStore.themeDrawerVisible" display-directive="show" :width="360">
|
<NDrawer v-model:show="appStore.themeDrawerVisible" display-directive="show" :width="drawerWidth">
|
||||||
<NDrawerContent :title="$t('theme.themeDrawerTitle')" :native-scrollbar="false" closable>
|
<NDrawerContent :title="$t('theme.themeDrawerTitle')" :native-scrollbar="false" closable>
|
||||||
<DarkMode />
|
<NTabs v-model:value="activeTab" type="segment" size="medium" class="mb-16px">
|
||||||
<LayoutMode />
|
<NTab name="appearance" :tab="$t('theme.tabs.appearance')"></NTab>
|
||||||
<ThemeColor />
|
<NTab name="layout" :tab="$t('theme.tabs.layout')"></NTab>
|
||||||
<PageFun />
|
<NTab name="general" :tab="$t('theme.tabs.general')"></NTab>
|
||||||
|
</NTabs>
|
||||||
|
|
||||||
|
<div class="min-h-400px">
|
||||||
|
<KeepAlive>
|
||||||
|
<AppearanceSettings v-if="activeTab === 'appearance'" />
|
||||||
|
<LayoutSettings v-else-if="activeTab === 'layout'" />
|
||||||
|
<GeneralSettings v-else-if="activeTab === 'general'" />
|
||||||
|
</KeepAlive>
|
||||||
|
</div>
|
||||||
|
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<ConfigOperation />
|
<ConfigOperation />
|
||||||
</template>
|
</template>
|
||||||
@@ -28,4 +48,14 @@ const appStore = useAppStore();
|
|||||||
</NDrawer>
|
</NDrawer>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped></style>
|
<style scoped>
|
||||||
|
:deep(.n-tab) {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.n-tab-pane) {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import ThemeSchema from './modules/theme-schema.vue';
|
||||||
|
import ThemeColor from './modules/theme-color.vue';
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'AppearanceSettings'
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="flex-col-stretch gap-16px">
|
||||||
|
<ThemeSchema />
|
||||||
|
<ThemeColor />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped></style>
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useThemeStore } from '@/store/modules/theme';
|
import { useThemeStore } from '@/store/modules/theme';
|
||||||
import { $t } from '@/locales';
|
import { $t } from '@/locales';
|
||||||
import SettingItem from '../components/setting-item.vue';
|
import SettingItem from '../../../components/setting-item.vue';
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'ThemeColor'
|
name: 'ThemeColor'
|
||||||
@@ -34,16 +34,16 @@ const swatches: string[] = [
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<NDivider>{{ $t('theme.themeColor.title') }}</NDivider>
|
<NDivider>{{ $t('theme.appearance.themeColor.title') }}</NDivider>
|
||||||
<div class="flex-col-stretch gap-12px">
|
<div class="flex-col-stretch gap-12px">
|
||||||
<NTooltip placement="top-start">
|
<NTooltip placement="top-start">
|
||||||
<template #trigger>
|
<template #trigger>
|
||||||
<SettingItem key="recommend-color" :label="$t('theme.recommendColor')">
|
<SettingItem key="recommend-color" :label="$t('theme.appearance.recommendColor')">
|
||||||
<NSwitch v-model:value="themeStore.recommendColor" />
|
<NSwitch v-model:value="themeStore.recommendColor" />
|
||||||
</SettingItem>
|
</SettingItem>
|
||||||
</template>
|
</template>
|
||||||
<p>
|
<p>
|
||||||
<span class="pr-12px">{{ $t('theme.recommendColorDesc') }}</span>
|
<span class="pr-12px">{{ $t('theme.appearance.recommendColorDesc') }}</span>
|
||||||
<br />
|
<br />
|
||||||
<NButton
|
<NButton
|
||||||
text
|
text
|
||||||
@@ -57,10 +57,14 @@ const swatches: string[] = [
|
|||||||
</NButton>
|
</NButton>
|
||||||
</p>
|
</p>
|
||||||
</NTooltip>
|
</NTooltip>
|
||||||
<SettingItem v-for="(_, key) in themeStore.themeColors" :key="key" :label="$t(`theme.themeColor.${key}`)">
|
<SettingItem
|
||||||
|
v-for="(_, key) in themeStore.themeColors"
|
||||||
|
:key="key"
|
||||||
|
:label="$t(`theme.appearance.themeColor.${key}`)"
|
||||||
|
>
|
||||||
<template v-if="key === 'info'" #suffix>
|
<template v-if="key === 'info'" #suffix>
|
||||||
<NCheckbox v-model:checked="themeStore.isInfoFollowPrimary">
|
<NCheckbox v-model:checked="themeStore.isInfoFollowPrimary">
|
||||||
{{ $t('theme.themeColor.followPrimary') }}
|
{{ $t('theme.appearance.themeColor.followPrimary') }}
|
||||||
</NCheckbox>
|
</NCheckbox>
|
||||||
</template>
|
</template>
|
||||||
<NColorPicker
|
<NColorPicker
|
||||||
@@ -3,10 +3,10 @@ import { computed } from 'vue';
|
|||||||
import { themeSchemaRecord } from '@/constants/app';
|
import { themeSchemaRecord } from '@/constants/app';
|
||||||
import { useThemeStore } from '@/store/modules/theme';
|
import { useThemeStore } from '@/store/modules/theme';
|
||||||
import { $t } from '@/locales';
|
import { $t } from '@/locales';
|
||||||
import SettingItem from '../components/setting-item.vue';
|
import SettingItem from '../../../components/setting-item.vue';
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'DarkMode'
|
name: 'ThemeSchema'
|
||||||
});
|
});
|
||||||
|
|
||||||
const themeStore = useThemeStore();
|
const themeStore = useThemeStore();
|
||||||
@@ -33,7 +33,7 @@ const showSiderInverted = computed(() => !themeStore.darkMode && themeStore.layo
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<NDivider>{{ $t('theme.themeSchema.title') }}</NDivider>
|
<NDivider>{{ $t('theme.appearance.themeSchema.title') }}</NDivider>
|
||||||
<div class="flex-col-stretch gap-16px">
|
<div class="flex-col-stretch gap-16px">
|
||||||
<div class="i-flex-center">
|
<div class="i-flex-center">
|
||||||
<NTabs
|
<NTabs
|
||||||
@@ -50,14 +50,14 @@ const showSiderInverted = computed(() => !themeStore.darkMode && themeStore.layo
|
|||||||
</NTabs>
|
</NTabs>
|
||||||
</div>
|
</div>
|
||||||
<Transition name="sider-inverted">
|
<Transition name="sider-inverted">
|
||||||
<SettingItem v-if="showSiderInverted" :label="$t('theme.sider.inverted')">
|
<SettingItem v-if="showSiderInverted" :label="$t('theme.layout.sider.inverted')">
|
||||||
<NSwitch v-model:value="themeStore.sider.inverted" />
|
<NSwitch v-model:value="themeStore.sider.inverted" />
|
||||||
</SettingItem>
|
</SettingItem>
|
||||||
</Transition>
|
</Transition>
|
||||||
<SettingItem :label="$t('theme.grayscale')">
|
<SettingItem :label="$t('theme.appearance.grayscale')">
|
||||||
<NSwitch :value="themeStore.grayscale" @update:value="handleGrayscaleChange" />
|
<NSwitch :value="themeStore.grayscale" @update:value="handleGrayscaleChange" />
|
||||||
</SettingItem>
|
</SettingItem>
|
||||||
<SettingItem :label="$t('theme.colourWeakness')">
|
<SettingItem :label="$t('theme.appearance.colourWeakness')">
|
||||||
<NSwitch :value="themeStore.colourWeakness" @update:value="handleColourWeaknessChange" />
|
<NSwitch :value="themeStore.colourWeakness" @update:value="handleColourWeaknessChange" />
|
||||||
</SettingItem>
|
</SettingItem>
|
||||||
</div>
|
</div>
|
||||||
17
src/layouts/modules/theme-drawer/modules/general/index.vue
Normal file
17
src/layouts/modules/theme-drawer/modules/general/index.vue
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import GlobalSettings from './modules/global-settings.vue';
|
||||||
|
import WatermarkSettings from './modules/watermark-settings.vue';
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'GeneralSettings'
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="flex-col-stretch gap-16px">
|
||||||
|
<GlobalSettings />
|
||||||
|
<WatermarkSettings />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped></style>
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { useThemeStore } from '@/store/modules/theme';
|
||||||
|
import { $t } from '@/locales';
|
||||||
|
import SettingItem from '../../../components/setting-item.vue';
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'GlobalSettings'
|
||||||
|
});
|
||||||
|
|
||||||
|
const themeStore = useThemeStore();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<NDivider>{{ $t('theme.general.title') }}</NDivider>
|
||||||
|
<SettingItem :label="$t('theme.general.multilingual.visible')">
|
||||||
|
<NSwitch v-model:value="themeStore.header.multilingual.visible" />
|
||||||
|
</SettingItem>
|
||||||
|
|
||||||
|
<SettingItem :label="$t('theme.general.globalSearch.visible')">
|
||||||
|
<NSwitch v-model:value="themeStore.header.globalSearch.visible" />
|
||||||
|
</SettingItem>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.setting-list-move,
|
||||||
|
.setting-list-enter-active,
|
||||||
|
.setting-list-leave-active {
|
||||||
|
--uno: transition-all-300;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting-list-enter-from,
|
||||||
|
.setting-list-leave-to {
|
||||||
|
--uno: opacity-0 -translate-x-30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting-list-leave-active {
|
||||||
|
--uno: absolute;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { computed } from 'vue';
|
||||||
|
import { watermarkTimeFormatOptions } from '@/constants/app';
|
||||||
|
import { useThemeStore } from '@/store/modules/theme';
|
||||||
|
import { $t } from '@/locales';
|
||||||
|
import SettingItem from '../../../components/setting-item.vue';
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'WatermarkSettings'
|
||||||
|
});
|
||||||
|
|
||||||
|
const themeStore = useThemeStore();
|
||||||
|
|
||||||
|
const isWatermarkTextVisible = computed(
|
||||||
|
() => themeStore.watermark.visible && !themeStore.watermark.enableUserName && !themeStore.watermark.enableTime
|
||||||
|
);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<NDivider>{{ $t('theme.general.watermark.title') }}</NDivider>
|
||||||
|
<TransitionGroup tag="div" name="setting-list" class="flex-col-stretch gap-12px">
|
||||||
|
<SettingItem key="1" :label="$t('theme.general.watermark.visible')">
|
||||||
|
<NSwitch v-model:value="themeStore.watermark.visible" />
|
||||||
|
</SettingItem>
|
||||||
|
<SettingItem v-if="themeStore.watermark.visible" key="2" :label="$t('theme.general.watermark.enableUserName')">
|
||||||
|
<NSwitch :value="themeStore.watermark.enableUserName" @update:value="themeStore.setWatermarkEnableUserName" />
|
||||||
|
</SettingItem>
|
||||||
|
<SettingItem v-if="themeStore.watermark.visible" key="3" :label="$t('theme.general.watermark.enableTime')">
|
||||||
|
<NSwitch :value="themeStore.watermark.enableTime" @update:value="themeStore.setWatermarkEnableTime" />
|
||||||
|
</SettingItem>
|
||||||
|
<SettingItem
|
||||||
|
v-if="themeStore.watermark.visible && themeStore.watermark.enableTime"
|
||||||
|
key="4"
|
||||||
|
:label="$t('theme.general.watermark.timeFormat')"
|
||||||
|
>
|
||||||
|
<NSelect
|
||||||
|
v-model:value="themeStore.watermark.timeFormat"
|
||||||
|
:options="watermarkTimeFormatOptions"
|
||||||
|
size="small"
|
||||||
|
class="w-210px"
|
||||||
|
/>
|
||||||
|
</SettingItem>
|
||||||
|
<SettingItem v-if="isWatermarkTextVisible" key="5" :label="$t('theme.general.watermark.text')">
|
||||||
|
<NInput
|
||||||
|
v-model:value="themeStore.watermark.text"
|
||||||
|
autosize
|
||||||
|
type="text"
|
||||||
|
size="small"
|
||||||
|
class="w-120px"
|
||||||
|
placeholder="SoybeanAdmin"
|
||||||
|
/>
|
||||||
|
</SettingItem>
|
||||||
|
</TransitionGroup>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.setting-list-move,
|
||||||
|
.setting-list-enter-active,
|
||||||
|
.setting-list-leave-active {
|
||||||
|
--uno: transition-all-300;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting-list-enter-from,
|
||||||
|
.setting-list-leave-to {
|
||||||
|
--uno: opacity-0 -translate-x-30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting-list-leave-active {
|
||||||
|
--uno: absolute;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
29
src/layouts/modules/theme-drawer/modules/layout/index.vue
Normal file
29
src/layouts/modules/theme-drawer/modules/layout/index.vue
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { useThemeStore } from '@/store/modules/theme';
|
||||||
|
import LayoutMode from './modules/layout-mode.vue';
|
||||||
|
import TabSettings from './modules/tab-settings.vue';
|
||||||
|
import HeaderSettings from './modules/header-settings.vue';
|
||||||
|
import SiderSettings from './modules/sider-settings.vue';
|
||||||
|
import FooterSettings from './modules/footer-settings.vue';
|
||||||
|
import ContentSettings from './modules/content-settings.vue';
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'LayoutSettings'
|
||||||
|
});
|
||||||
|
|
||||||
|
const themeStore = useThemeStore();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="flex-col-stretch gap-16px">
|
||||||
|
<LayoutMode />
|
||||||
|
<TabSettings />
|
||||||
|
<HeaderSettings />
|
||||||
|
<!-- The top menu mode does not have a sidebar -->
|
||||||
|
<SiderSettings v-if="themeStore.layout.mode !== 'horizontal'" />
|
||||||
|
<FooterSettings />
|
||||||
|
<ContentSettings />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped></style>
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { computed } from 'vue';
|
||||||
|
import { themePageAnimationModeOptions, themeScrollModeOptions } from '@/constants/app';
|
||||||
|
import { useThemeStore } from '@/store/modules/theme';
|
||||||
|
import { translateOptions } from '@/utils/common';
|
||||||
|
import { $t } from '@/locales';
|
||||||
|
import SettingItem from '../../../components/setting-item.vue';
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'ContentSettings'
|
||||||
|
});
|
||||||
|
|
||||||
|
const themeStore = useThemeStore();
|
||||||
|
|
||||||
|
const isWrapperScrollMode = computed(() => themeStore.layout.scrollMode === 'wrapper');
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<NDivider>{{ $t('theme.layout.content.title') }}</NDivider>
|
||||||
|
<TransitionGroup tag="div" name="setting-list" class="flex-col-stretch gap-12px">
|
||||||
|
<SettingItem key="1" :label="$t('theme.layout.content.scrollMode.title')">
|
||||||
|
<NSelect
|
||||||
|
v-model:value="themeStore.layout.scrollMode"
|
||||||
|
:options="translateOptions(themeScrollModeOptions)"
|
||||||
|
size="small"
|
||||||
|
class="w-120px"
|
||||||
|
/>
|
||||||
|
</SettingItem>
|
||||||
|
<SettingItem key="2" :label="$t('theme.layout.content.page.animate')">
|
||||||
|
<NSwitch v-model:value="themeStore.page.animate" />
|
||||||
|
</SettingItem>
|
||||||
|
<SettingItem v-if="themeStore.page.animate" key="3" :label="$t('theme.layout.content.page.mode.title')">
|
||||||
|
<NSelect
|
||||||
|
v-model:value="themeStore.page.animateMode"
|
||||||
|
:options="translateOptions(themePageAnimationModeOptions)"
|
||||||
|
size="small"
|
||||||
|
class="w-120px"
|
||||||
|
/>
|
||||||
|
</SettingItem>
|
||||||
|
<SettingItem v-if="isWrapperScrollMode" key="4" :label="$t('theme.layout.content.fixedHeaderAndTab')">
|
||||||
|
<NSwitch v-model:value="themeStore.fixedHeaderAndTab" />
|
||||||
|
</SettingItem>
|
||||||
|
</TransitionGroup>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.setting-list-move,
|
||||||
|
.setting-list-enter-active,
|
||||||
|
.setting-list-leave-active {
|
||||||
|
--uno: transition-all-300;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting-list-enter-from,
|
||||||
|
.setting-list-leave-to {
|
||||||
|
--uno: opacity-0 -translate-x-30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting-list-leave-active {
|
||||||
|
--uno: absolute;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { computed } from 'vue';
|
||||||
|
import { useThemeStore } from '@/store/modules/theme';
|
||||||
|
import { $t } from '@/locales';
|
||||||
|
import SettingItem from '../../../components/setting-item.vue';
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'FooterSettings'
|
||||||
|
});
|
||||||
|
|
||||||
|
const themeStore = useThemeStore();
|
||||||
|
|
||||||
|
const layoutMode = computed(() => themeStore.layout.mode);
|
||||||
|
const isWrapperScrollMode = computed(() => themeStore.layout.scrollMode === 'wrapper');
|
||||||
|
const isMixHorizontalMode = computed(() =>
|
||||||
|
['top-hybrid-sidebar-first', 'top-hybrid-header-first'].includes(layoutMode.value)
|
||||||
|
);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<NDivider>{{ $t('theme.layout.footer.title') }}</NDivider>
|
||||||
|
<TransitionGroup tag="div" name="setting-list" class="flex-col-stretch gap-12px">
|
||||||
|
<SettingItem key="1" :label="$t('theme.layout.footer.visible')">
|
||||||
|
<NSwitch v-model:value="themeStore.footer.visible" />
|
||||||
|
</SettingItem>
|
||||||
|
<SettingItem
|
||||||
|
v-if="themeStore.footer.visible && isWrapperScrollMode"
|
||||||
|
key="2"
|
||||||
|
:label="$t('theme.layout.footer.fixed')"
|
||||||
|
>
|
||||||
|
<NSwitch v-model:value="themeStore.footer.fixed" />
|
||||||
|
</SettingItem>
|
||||||
|
<SettingItem v-if="themeStore.footer.visible" key="3" :label="$t('theme.layout.footer.height')">
|
||||||
|
<NInputNumber v-model:value="themeStore.footer.height" size="small" :step="1" class="w-120px" />
|
||||||
|
</SettingItem>
|
||||||
|
<SettingItem
|
||||||
|
v-if="themeStore.footer.visible && isMixHorizontalMode"
|
||||||
|
key="4"
|
||||||
|
:label="$t('theme.layout.footer.right')"
|
||||||
|
>
|
||||||
|
<NSwitch v-model:value="themeStore.footer.right" />
|
||||||
|
</SettingItem>
|
||||||
|
</TransitionGroup>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.setting-list-move,
|
||||||
|
.setting-list-enter-active,
|
||||||
|
.setting-list-leave-active {
|
||||||
|
--uno: transition-all-300;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting-list-enter-from,
|
||||||
|
.setting-list-leave-to {
|
||||||
|
--uno: opacity-0 -translate-x-30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting-list-leave-active {
|
||||||
|
--uno: absolute;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { useThemeStore } from '@/store/modules/theme';
|
||||||
|
import { $t } from '@/locales';
|
||||||
|
import SettingItem from '../../../components/setting-item.vue';
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'HeaderSettings'
|
||||||
|
});
|
||||||
|
|
||||||
|
const themeStore = useThemeStore();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<NDivider>{{ $t('theme.layout.header.title') }}</NDivider>
|
||||||
|
<TransitionGroup tag="div" name="setting-list" class="flex-col-stretch gap-12px">
|
||||||
|
<SettingItem key="1" :label="$t('theme.layout.header.height')">
|
||||||
|
<NInputNumber v-model:value="themeStore.header.height" size="small" :step="1" class="w-120px" />
|
||||||
|
</SettingItem>
|
||||||
|
<SettingItem key="2" :label="$t('theme.layout.header.breadcrumb.visible')">
|
||||||
|
<NSwitch v-model:value="themeStore.header.breadcrumb.visible" />
|
||||||
|
</SettingItem>
|
||||||
|
<SettingItem
|
||||||
|
v-if="themeStore.header.breadcrumb.visible"
|
||||||
|
key="3"
|
||||||
|
:label="$t('theme.layout.header.breadcrumb.showIcon')"
|
||||||
|
>
|
||||||
|
<NSwitch v-model:value="themeStore.header.breadcrumb.showIcon" />
|
||||||
|
</SettingItem>
|
||||||
|
</TransitionGroup>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.setting-list-move,
|
||||||
|
.setting-list-enter-active,
|
||||||
|
.setting-list-leave-active {
|
||||||
|
--uno: transition-all-300;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting-list-enter-from,
|
||||||
|
.setting-list-leave-to {
|
||||||
|
--uno: opacity-0 -translate-x-30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting-list-leave-active {
|
||||||
|
--uno: absolute;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -2,8 +2,7 @@
|
|||||||
import { useAppStore } from '@/store/modules/app';
|
import { useAppStore } from '@/store/modules/app';
|
||||||
import { useThemeStore } from '@/store/modules/theme';
|
import { useThemeStore } from '@/store/modules/theme';
|
||||||
import { $t } from '@/locales';
|
import { $t } from '@/locales';
|
||||||
import LayoutModeCard from '../components/layout-mode-card.vue';
|
import LayoutModeCard from '../../../components/layout-mode-card.vue';
|
||||||
import SettingItem from '../components/setting-item.vue';
|
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'LayoutMode'
|
name: 'LayoutMode'
|
||||||
@@ -11,56 +10,52 @@ defineOptions({
|
|||||||
|
|
||||||
const appStore = useAppStore();
|
const appStore = useAppStore();
|
||||||
const themeStore = useThemeStore();
|
const themeStore = useThemeStore();
|
||||||
|
|
||||||
function handleReverseHorizontalMixChange(value: boolean) {
|
|
||||||
themeStore.setLayoutReverseHorizontalMix(value);
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<NDivider>{{ $t('theme.layoutMode.title') }}</NDivider>
|
<NDivider>{{ $t('theme.layout.layoutMode.title') }}</NDivider>
|
||||||
<LayoutModeCard v-model:mode="themeStore.layout.mode" :disabled="appStore.isMobile">
|
<LayoutModeCard v-model:mode="themeStore.layout.mode" :disabled="appStore.isMobile">
|
||||||
<template #vertical>
|
<template #vertical>
|
||||||
<div class="layout-sider h-full w-18px"></div>
|
<div class="layout-sider h-full w-18px !bg-primary"></div>
|
||||||
<div class="vertical-wrapper">
|
<div class="vertical-wrapper">
|
||||||
<div class="layout-header"></div>
|
<div class="layout-header bg-primary-200"></div>
|
||||||
<div class="layout-main"></div>
|
<div class="layout-main"></div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template #vertical-mix>
|
<template #vertical-mix>
|
||||||
<div class="layout-sider h-full w-8px"></div>
|
<div class="layout-sider h-full w-8px !bg-primary"></div>
|
||||||
<div class="layout-sider h-full w-16px"></div>
|
<div class="layout-sider h-full w-16px !bg-primary-300"></div>
|
||||||
<div class="vertical-wrapper">
|
<div class="vertical-wrapper">
|
||||||
<div class="layout-header"></div>
|
<div class="layout-header bg-primary-200"></div>
|
||||||
<div class="layout-main"></div>
|
<div class="layout-main"></div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template #horizontal>
|
<template #horizontal>
|
||||||
<div class="layout-header"></div>
|
<div class="layout-header !bg-primary"></div>
|
||||||
<div class="horizontal-wrapper">
|
<div class="horizontal-wrapper">
|
||||||
<div class="layout-main"></div>
|
<div class="layout-main"></div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template #horizontal-mix>
|
<template #top-hybrid-sidebar-first>
|
||||||
<div class="layout-header"></div>
|
<div class="layout-header !bg-primary-300"></div>
|
||||||
|
<div class="horizontal-wrapper">
|
||||||
|
<div class="layout-sider w-18px !bg-primary"></div>
|
||||||
|
<div class="layout-main"></div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #top-hybrid-header-first>
|
||||||
|
<div class="layout-header bg-primary"></div>
|
||||||
<div class="horizontal-wrapper">
|
<div class="horizontal-wrapper">
|
||||||
<div class="layout-sider w-18px"></div>
|
<div class="layout-sider w-18px"></div>
|
||||||
<div class="layout-main"></div>
|
<div class="layout-main"></div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</LayoutModeCard>
|
</LayoutModeCard>
|
||||||
<SettingItem
|
|
||||||
v-if="themeStore.layout.mode === 'horizontal-mix'"
|
|
||||||
:label="$t('theme.layoutMode.reverseHorizontalMix')"
|
|
||||||
class="mt-16px"
|
|
||||||
>
|
|
||||||
<NSwitch :value="themeStore.layout.reverseHorizontalMix" @update:value="handleReverseHorizontalMixChange" />
|
|
||||||
</SettingItem>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.layout-header {
|
.layout-header {
|
||||||
--uno: h-16px bg-primary rd-4px;
|
--uno: h-16px rd-4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.layout-sider {
|
.layout-sider {
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { computed } from 'vue';
|
||||||
|
import { useThemeStore } from '@/store/modules/theme';
|
||||||
|
import { $t } from '@/locales';
|
||||||
|
import SettingItem from '../../../components/setting-item.vue';
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'SiderSettings'
|
||||||
|
});
|
||||||
|
|
||||||
|
const themeStore = useThemeStore();
|
||||||
|
|
||||||
|
const layoutMode = computed(() => themeStore.layout.mode);
|
||||||
|
const isMixLayoutMode = computed(() => layoutMode.value.includes('mix') || layoutMode.value.includes('hybrid'));
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<NDivider>{{ $t('theme.layout.sider.title') }}</NDivider>
|
||||||
|
<TransitionGroup tag="div" name="setting-list" class="flex-col-stretch gap-12px">
|
||||||
|
<SettingItem v-if="layoutMode === 'vertical'" key="1" :label="$t('theme.layout.sider.width')">
|
||||||
|
<NInputNumber v-model:value="themeStore.sider.width" size="small" :step="1" class="w-120px" />
|
||||||
|
</SettingItem>
|
||||||
|
<SettingItem v-if="layoutMode === 'vertical'" key="2" :label="$t('theme.layout.sider.collapsedWidth')">
|
||||||
|
<NInputNumber v-model:value="themeStore.sider.collapsedWidth" size="small" :step="1" class="w-120px" />
|
||||||
|
</SettingItem>
|
||||||
|
<SettingItem v-if="isMixLayoutMode" key="3" :label="$t('theme.layout.sider.mixWidth')">
|
||||||
|
<NInputNumber v-model:value="themeStore.sider.mixWidth" size="small" :step="1" class="w-120px" />
|
||||||
|
</SettingItem>
|
||||||
|
<SettingItem v-if="isMixLayoutMode" key="4" :label="$t('theme.layout.sider.mixCollapsedWidth')">
|
||||||
|
<NInputNumber v-model:value="themeStore.sider.mixCollapsedWidth" size="small" :step="1" class="w-120px" />
|
||||||
|
</SettingItem>
|
||||||
|
<SettingItem v-if="layoutMode === 'vertical-mix'" key="5" :label="$t('theme.layout.sider.mixChildMenuWidth')">
|
||||||
|
<NInputNumber v-model:value="themeStore.sider.mixChildMenuWidth" size="small" :step="1" class="w-120px" />
|
||||||
|
</SettingItem>
|
||||||
|
</TransitionGroup>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.setting-list-move,
|
||||||
|
.setting-list-enter-active,
|
||||||
|
.setting-list-leave-active {
|
||||||
|
--uno: transition-all-300;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting-list-enter-from,
|
||||||
|
.setting-list-leave-to {
|
||||||
|
--uno: opacity-0 -translate-x-30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting-list-leave-active {
|
||||||
|
--uno: absolute;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { resetCacheStrategyOptions, themeTabModeOptions } from '@/constants/app';
|
||||||
|
import { useThemeStore } from '@/store/modules/theme';
|
||||||
|
import { translateOptions } from '@/utils/common';
|
||||||
|
import { $t } from '@/locales';
|
||||||
|
import SettingItem from '../../../components/setting-item.vue';
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'TabSettings'
|
||||||
|
});
|
||||||
|
|
||||||
|
const themeStore = useThemeStore();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<NDivider>{{ $t('theme.layout.tab.title') }}</NDivider>
|
||||||
|
<TransitionGroup tag="div" name="setting-list" class="flex-col-stretch gap-12px">
|
||||||
|
<SettingItem key="0" :label="$t('theme.layout.resetCacheStrategy.title')">
|
||||||
|
<NSelect
|
||||||
|
v-model:value="themeStore.resetCacheStrategy"
|
||||||
|
:options="translateOptions(resetCacheStrategyOptions)"
|
||||||
|
size="small"
|
||||||
|
class="w-120px"
|
||||||
|
/>
|
||||||
|
</SettingItem>
|
||||||
|
<SettingItem key="1" :label="$t('theme.layout.tab.visible')">
|
||||||
|
<NSwitch v-model:value="themeStore.tab.visible" />
|
||||||
|
</SettingItem>
|
||||||
|
<SettingItem v-if="themeStore.tab.visible" key="2" :label="$t('theme.layout.tab.cache')">
|
||||||
|
<NSwitch v-model:value="themeStore.tab.cache" />
|
||||||
|
</SettingItem>
|
||||||
|
<SettingItem v-if="themeStore.tab.visible" key="3" :label="$t('theme.layout.tab.height')">
|
||||||
|
<NInputNumber v-model:value="themeStore.tab.height" size="small" :step="1" class="w-120px" />
|
||||||
|
</SettingItem>
|
||||||
|
<SettingItem v-if="themeStore.tab.visible" key="4" :label="$t('theme.layout.tab.mode.title')">
|
||||||
|
<NSelect
|
||||||
|
v-model:value="themeStore.tab.mode"
|
||||||
|
:options="translateOptions(themeTabModeOptions)"
|
||||||
|
size="small"
|
||||||
|
class="w-120px"
|
||||||
|
/>
|
||||||
|
</SettingItem>
|
||||||
|
</TransitionGroup>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.setting-list-move,
|
||||||
|
.setting-list-enter-active,
|
||||||
|
.setting-list-leave-active {
|
||||||
|
--uno: transition-all-300;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting-list-enter-from,
|
||||||
|
.setting-list-leave-to {
|
||||||
|
--uno: opacity-0 -translate-x-30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting-list-leave-active {
|
||||||
|
--uno: absolute;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,157 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import { computed } from 'vue';
|
|
||||||
import {
|
|
||||||
resetCacheStrategyOptions,
|
|
||||||
themePageAnimationModeOptions,
|
|
||||||
themeScrollModeOptions,
|
|
||||||
themeTabModeOptions
|
|
||||||
} from '@/constants/app';
|
|
||||||
import { useThemeStore } from '@/store/modules/theme';
|
|
||||||
import { translateOptions } from '@/utils/common';
|
|
||||||
import { $t } from '@/locales';
|
|
||||||
import SettingItem from '../components/setting-item.vue';
|
|
||||||
|
|
||||||
defineOptions({
|
|
||||||
name: 'PageFun'
|
|
||||||
});
|
|
||||||
|
|
||||||
const themeStore = useThemeStore();
|
|
||||||
|
|
||||||
const layoutMode = computed(() => themeStore.layout.mode);
|
|
||||||
|
|
||||||
const isMixLayoutMode = computed(() => layoutMode.value.includes('mix'));
|
|
||||||
|
|
||||||
const isWrapperScrollMode = computed(() => themeStore.layout.scrollMode === 'wrapper');
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<NDivider>{{ $t('theme.pageFunTitle') }}</NDivider>
|
|
||||||
<TransitionGroup tag="div" name="setting-list" class="flex-col-stretch gap-12px">
|
|
||||||
<SettingItem key="0" :label="$t('theme.resetCacheStrategy.title')">
|
|
||||||
<NSelect
|
|
||||||
v-model:value="themeStore.resetCacheStrategy"
|
|
||||||
:options="translateOptions(resetCacheStrategyOptions)"
|
|
||||||
size="small"
|
|
||||||
class="w-120px"
|
|
||||||
/>
|
|
||||||
</SettingItem>
|
|
||||||
<SettingItem key="1" :label="$t('theme.scrollMode.title')">
|
|
||||||
<NSelect
|
|
||||||
v-model:value="themeStore.layout.scrollMode"
|
|
||||||
:options="translateOptions(themeScrollModeOptions)"
|
|
||||||
size="small"
|
|
||||||
class="w-120px"
|
|
||||||
/>
|
|
||||||
</SettingItem>
|
|
||||||
<SettingItem key="1-1" :label="$t('theme.page.animate')">
|
|
||||||
<NSwitch v-model:value="themeStore.page.animate" />
|
|
||||||
</SettingItem>
|
|
||||||
<SettingItem v-if="themeStore.page.animate" key="1-2" :label="$t('theme.page.mode.title')">
|
|
||||||
<NSelect
|
|
||||||
v-model:value="themeStore.page.animateMode"
|
|
||||||
:options="translateOptions(themePageAnimationModeOptions)"
|
|
||||||
size="small"
|
|
||||||
class="w-120px"
|
|
||||||
/>
|
|
||||||
</SettingItem>
|
|
||||||
<SettingItem v-if="isWrapperScrollMode" key="2" :label="$t('theme.fixedHeaderAndTab')">
|
|
||||||
<NSwitch v-model:value="themeStore.fixedHeaderAndTab" />
|
|
||||||
</SettingItem>
|
|
||||||
<SettingItem key="3" :label="$t('theme.header.height')">
|
|
||||||
<NInputNumber v-model:value="themeStore.header.height" size="small" :step="1" class="w-120px" />
|
|
||||||
</SettingItem>
|
|
||||||
<SettingItem key="4" :label="$t('theme.header.breadcrumb.visible')">
|
|
||||||
<NSwitch v-model:value="themeStore.header.breadcrumb.visible" />
|
|
||||||
</SettingItem>
|
|
||||||
<SettingItem v-if="themeStore.header.breadcrumb.visible" key="4-1" :label="$t('theme.header.breadcrumb.showIcon')">
|
|
||||||
<NSwitch v-model:value="themeStore.header.breadcrumb.showIcon" />
|
|
||||||
</SettingItem>
|
|
||||||
<SettingItem key="5" :label="$t('theme.tab.visible')">
|
|
||||||
<NSwitch v-model:value="themeStore.tab.visible" />
|
|
||||||
</SettingItem>
|
|
||||||
<SettingItem v-if="themeStore.tab.visible" key="5-1" :label="$t('theme.tab.cache')">
|
|
||||||
<NSwitch v-model:value="themeStore.tab.cache" />
|
|
||||||
</SettingItem>
|
|
||||||
<SettingItem v-if="themeStore.tab.visible" key="5-2" :label="$t('theme.tab.height')">
|
|
||||||
<NInputNumber v-model:value="themeStore.tab.height" size="small" :step="1" class="w-120px" />
|
|
||||||
</SettingItem>
|
|
||||||
<SettingItem v-if="themeStore.tab.visible" key="5-3" :label="$t('theme.tab.mode.title')">
|
|
||||||
<NSelect
|
|
||||||
v-model:value="themeStore.tab.mode"
|
|
||||||
:options="translateOptions(themeTabModeOptions)"
|
|
||||||
size="small"
|
|
||||||
class="w-120px"
|
|
||||||
/>
|
|
||||||
</SettingItem>
|
|
||||||
<SettingItem v-if="layoutMode === 'vertical'" key="6-1" :label="$t('theme.sider.width')">
|
|
||||||
<NInputNumber v-model:value="themeStore.sider.width" size="small" :step="1" class="w-120px" />
|
|
||||||
</SettingItem>
|
|
||||||
<SettingItem v-if="layoutMode === 'vertical'" key="6-2" :label="$t('theme.sider.collapsedWidth')">
|
|
||||||
<NInputNumber v-model:value="themeStore.sider.collapsedWidth" size="small" :step="1" class="w-120px" />
|
|
||||||
</SettingItem>
|
|
||||||
<SettingItem v-if="isMixLayoutMode" key="6-3" :label="$t('theme.sider.mixWidth')">
|
|
||||||
<NInputNumber v-model:value="themeStore.sider.mixWidth" size="small" :step="1" class="w-120px" />
|
|
||||||
</SettingItem>
|
|
||||||
<SettingItem v-if="isMixLayoutMode" key="6-4" :label="$t('theme.sider.mixCollapsedWidth')">
|
|
||||||
<NInputNumber v-model:value="themeStore.sider.mixCollapsedWidth" size="small" :step="1" class="w-120px" />
|
|
||||||
</SettingItem>
|
|
||||||
<SettingItem v-if="layoutMode === 'vertical-mix'" key="6-5" :label="$t('theme.sider.mixChildMenuWidth')">
|
|
||||||
<NInputNumber v-model:value="themeStore.sider.mixChildMenuWidth" size="small" :step="1" class="w-120px" />
|
|
||||||
</SettingItem>
|
|
||||||
<SettingItem key="7" :label="$t('theme.footer.visible')">
|
|
||||||
<NSwitch v-model:value="themeStore.footer.visible" />
|
|
||||||
</SettingItem>
|
|
||||||
<SettingItem v-if="themeStore.footer.visible && isWrapperScrollMode" key="7-1" :label="$t('theme.footer.fixed')">
|
|
||||||
<NSwitch v-model:value="themeStore.footer.fixed" />
|
|
||||||
</SettingItem>
|
|
||||||
<SettingItem v-if="themeStore.footer.visible" key="7-2" :label="$t('theme.footer.height')">
|
|
||||||
<NInputNumber v-model:value="themeStore.footer.height" size="small" :step="1" class="w-120px" />
|
|
||||||
</SettingItem>
|
|
||||||
<SettingItem
|
|
||||||
v-if="themeStore.footer.visible && layoutMode === 'horizontal-mix'"
|
|
||||||
key="7-3"
|
|
||||||
:label="$t('theme.footer.right')"
|
|
||||||
>
|
|
||||||
<NSwitch v-model:value="themeStore.footer.right" />
|
|
||||||
</SettingItem>
|
|
||||||
<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.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
|
|
||||||
type="text"
|
|
||||||
size="small"
|
|
||||||
class="w-120px"
|
|
||||||
placeholder="SoybeanAdmin"
|
|
||||||
/>
|
|
||||||
</SettingItem>
|
|
||||||
<SettingItem key="9" :label="$t('theme.header.multilingual.visible')">
|
|
||||||
<NSwitch v-model:value="themeStore.header.multilingual.visible" />
|
|
||||||
</SettingItem>
|
|
||||||
<SettingItem key="10" :label="$t('theme.header.globalSearch.visible')">
|
|
||||||
<NSwitch v-model:value="themeStore.header.globalSearch.visible" />
|
|
||||||
</SettingItem>
|
|
||||||
</TransitionGroup>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.setting-list-move,
|
|
||||||
.setting-list-enter-active,
|
|
||||||
.setting-list-leave-active {
|
|
||||||
--uno: transition-all-300;
|
|
||||||
}
|
|
||||||
|
|
||||||
.setting-list-enter-from,
|
|
||||||
.setting-list-leave-to {
|
|
||||||
--uno: opacity-0 -translate-x-30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.setting-list-leave-active {
|
|
||||||
--uno: absolute;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -58,101 +58,132 @@ const local: App.I18n.Schema = {
|
|||||||
tokenExpired: 'The requested token has expired'
|
tokenExpired: 'The requested token has expired'
|
||||||
},
|
},
|
||||||
theme: {
|
theme: {
|
||||||
themeSchema: {
|
themeDrawerTitle: 'Theme Configuration',
|
||||||
title: 'Theme Schema',
|
tabs: {
|
||||||
light: 'Light',
|
appearance: 'Appearance',
|
||||||
dark: 'Dark',
|
layout: 'Layout',
|
||||||
auto: 'Follow System'
|
general: 'General'
|
||||||
},
|
},
|
||||||
grayscale: 'Grayscale',
|
appearance: {
|
||||||
colourWeakness: 'Colour Weakness',
|
themeSchema: {
|
||||||
layoutMode: {
|
title: 'Theme Schema',
|
||||||
title: 'Layout Mode',
|
light: 'Light',
|
||||||
vertical: 'Vertical Menu Mode',
|
dark: 'Dark',
|
||||||
horizontal: 'Horizontal Menu Mode',
|
auto: 'Follow System'
|
||||||
'vertical-mix': 'Vertical Mix Menu Mode',
|
},
|
||||||
'horizontal-mix': 'Horizontal Mix menu Mode',
|
grayscale: 'Grayscale',
|
||||||
reverseHorizontalMix: 'Reverse first level menus and child level menus position'
|
colourWeakness: 'Colour Weakness',
|
||||||
|
themeColor: {
|
||||||
|
title: 'Theme Color',
|
||||||
|
primary: 'Primary',
|
||||||
|
info: 'Info',
|
||||||
|
success: 'Success',
|
||||||
|
warning: 'Warning',
|
||||||
|
error: 'Error',
|
||||||
|
followPrimary: 'Follow Primary'
|
||||||
|
},
|
||||||
|
recommendColor: 'Apply Recommended Color Algorithm',
|
||||||
|
recommendColorDesc: 'The recommended color algorithm refers to'
|
||||||
},
|
},
|
||||||
recommendColor: 'Apply Recommended Color Algorithm',
|
layout: {
|
||||||
recommendColorDesc: 'The recommended color algorithm refers to',
|
layoutMode: {
|
||||||
themeColor: {
|
title: 'Layout Mode',
|
||||||
title: 'Theme Color',
|
vertical: 'Vertical Mode',
|
||||||
primary: 'Primary',
|
horizontal: 'Horizontal Mode',
|
||||||
info: 'Info',
|
'vertical-mix': 'Vertical Mix Mode',
|
||||||
success: 'Success',
|
'top-hybrid-sidebar-first': 'Top-Hybrid Sidebar-First',
|
||||||
warning: 'Warning',
|
'top-hybrid-header-first': 'Top-Hybrid Header-First',
|
||||||
error: 'Error',
|
vertical_detail: 'Vertical menu layout, with the menu on the left and content on the right.',
|
||||||
followPrimary: 'Follow Primary'
|
'vertical-mix_detail':
|
||||||
},
|
'Vertical mix-menu layout, with the primary menu on the dark left side and the secondary menu on the lighter right side.',
|
||||||
scrollMode: {
|
horizontal_detail: 'Horizontal menu layout, with the menu at the top and content below.',
|
||||||
title: 'Scroll Mode',
|
'top-hybrid-sidebar-first_detail':
|
||||||
wrapper: 'Wrapper',
|
'Top hybrid layout, with the primary menu on the left and the secondary menu at the top.',
|
||||||
content: 'Content'
|
'top-hybrid-header-first_detail':
|
||||||
},
|
'Top hybrid layout, with the primary menu at the top and the secondary menu on the left.'
|
||||||
page: {
|
},
|
||||||
animate: 'Page Animate',
|
tab: {
|
||||||
mode: {
|
title: 'Tab Settings',
|
||||||
title: 'Page Animate Mode',
|
visible: 'Tab Visible',
|
||||||
fade: 'Fade',
|
cache: 'Tag Bar Info Cache',
|
||||||
'fade-slide': 'Slide',
|
height: 'Tab Height',
|
||||||
'fade-bottom': 'Fade Zoom',
|
mode: {
|
||||||
'fade-scale': 'Fade Scale',
|
title: 'Tab Mode',
|
||||||
'zoom-fade': 'Zoom Fade',
|
chrome: 'Chrome',
|
||||||
'zoom-out': 'Zoom Out',
|
button: 'Button'
|
||||||
none: 'None'
|
}
|
||||||
|
},
|
||||||
|
header: {
|
||||||
|
title: 'Header Settings',
|
||||||
|
height: 'Header Height',
|
||||||
|
breadcrumb: {
|
||||||
|
visible: 'Breadcrumb Visible',
|
||||||
|
showIcon: 'Breadcrumb Icon Visible'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
sider: {
|
||||||
|
title: 'Sider Settings',
|
||||||
|
inverted: 'Dark Sider',
|
||||||
|
width: 'Sider Width',
|
||||||
|
collapsedWidth: 'Sider Collapsed Width',
|
||||||
|
mixWidth: 'Mix Sider Width',
|
||||||
|
mixCollapsedWidth: 'Mix Sider Collapse Width',
|
||||||
|
mixChildMenuWidth: 'Mix Child Menu Width'
|
||||||
|
},
|
||||||
|
footer: {
|
||||||
|
title: 'Footer Settings',
|
||||||
|
visible: 'Footer Visible',
|
||||||
|
fixed: 'Fixed Footer',
|
||||||
|
height: 'Footer Height',
|
||||||
|
right: 'Right Footer'
|
||||||
|
},
|
||||||
|
content: {
|
||||||
|
title: 'Content Area Settings',
|
||||||
|
scrollMode: {
|
||||||
|
title: 'Scroll Mode',
|
||||||
|
wrapper: 'Wrapper',
|
||||||
|
content: 'Content'
|
||||||
|
},
|
||||||
|
page: {
|
||||||
|
animate: 'Page Animate',
|
||||||
|
mode: {
|
||||||
|
title: 'Page Animate Mode',
|
||||||
|
fade: 'Fade',
|
||||||
|
'fade-slide': 'Slide',
|
||||||
|
'fade-bottom': 'Fade Zoom',
|
||||||
|
'fade-scale': 'Fade Scale',
|
||||||
|
'zoom-fade': 'Zoom Fade',
|
||||||
|
'zoom-out': 'Zoom Out',
|
||||||
|
none: 'None'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fixedHeaderAndTab: 'Fixed Header And Tab'
|
||||||
|
},
|
||||||
|
resetCacheStrategy: {
|
||||||
|
title: 'Reset Cache Strategy',
|
||||||
|
close: 'Close Page',
|
||||||
|
refresh: 'Refresh Page'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
fixedHeaderAndTab: 'Fixed Header And Tab',
|
general: {
|
||||||
header: {
|
title: 'General Settings',
|
||||||
height: 'Header Height',
|
watermark: {
|
||||||
breadcrumb: {
|
title: 'Watermark Settings',
|
||||||
visible: 'Breadcrumb Visible',
|
visible: 'Watermark Full Screen Visible',
|
||||||
showIcon: 'Breadcrumb Icon Visible'
|
text: 'Custom Watermark Text',
|
||||||
|
enableUserName: 'Enable User Name Watermark',
|
||||||
|
enableTime: 'Show Current Time',
|
||||||
|
timeFormat: 'Time Format'
|
||||||
},
|
},
|
||||||
multilingual: {
|
multilingual: {
|
||||||
|
title: 'Multilingual Settings',
|
||||||
visible: 'Display multilingual button'
|
visible: 'Display multilingual button'
|
||||||
},
|
},
|
||||||
globalSearch: {
|
globalSearch: {
|
||||||
|
title: 'Global Search Settings',
|
||||||
visible: 'Display GlobalSearch button'
|
visible: 'Display GlobalSearch button'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
tab: {
|
|
||||||
visible: 'Tab Visible',
|
|
||||||
cache: 'Tag Bar Info Cache',
|
|
||||||
height: 'Tab Height',
|
|
||||||
mode: {
|
|
||||||
title: 'Tab Mode',
|
|
||||||
chrome: 'Chrome',
|
|
||||||
button: 'Button'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
sider: {
|
|
||||||
inverted: 'Dark Sider',
|
|
||||||
width: 'Sider Width',
|
|
||||||
collapsedWidth: 'Sider Collapsed Width',
|
|
||||||
mixWidth: 'Mix Sider Width',
|
|
||||||
mixCollapsedWidth: 'Mix Sider Collapse Width',
|
|
||||||
mixChildMenuWidth: 'Mix Child Menu Width'
|
|
||||||
},
|
|
||||||
footer: {
|
|
||||||
visible: 'Footer Visible',
|
|
||||||
fixed: 'Fixed Footer',
|
|
||||||
height: 'Footer Height',
|
|
||||||
right: 'Right Footer'
|
|
||||||
},
|
|
||||||
watermark: {
|
|
||||||
visible: 'Watermark Full Screen Visible',
|
|
||||||
text: 'Watermark Text',
|
|
||||||
enableUserName: 'Enable User Name Watermark'
|
|
||||||
},
|
|
||||||
themeDrawerTitle: 'Theme Configuration',
|
|
||||||
pageFunTitle: 'Page Function',
|
|
||||||
resetCacheStrategy: {
|
|
||||||
title: 'Reset Cache Strategy',
|
|
||||||
close: 'Close Page',
|
|
||||||
refresh: 'Refresh Page'
|
|
||||||
},
|
|
||||||
configOperation: {
|
configOperation: {
|
||||||
copyConfig: 'Copy Config',
|
copyConfig: 'Copy Config',
|
||||||
copySuccessMsg: 'Copy Success, Please replace the variable "themeSettings" in "src/theme/settings.ts"',
|
copySuccessMsg: 'Copy Success, Please replace the variable "themeSettings" in "src/theme/settings.ts"',
|
||||||
|
|||||||
@@ -58,101 +58,129 @@ const local: App.I18n.Schema = {
|
|||||||
tokenExpired: 'token已过期'
|
tokenExpired: 'token已过期'
|
||||||
},
|
},
|
||||||
theme: {
|
theme: {
|
||||||
themeSchema: {
|
themeDrawerTitle: '主题配置',
|
||||||
title: '主题模式',
|
tabs: {
|
||||||
light: '亮色模式',
|
appearance: '外观',
|
||||||
dark: '暗黑模式',
|
layout: '布局',
|
||||||
auto: '跟随系统'
|
general: '通用'
|
||||||
},
|
},
|
||||||
grayscale: '灰色模式',
|
appearance: {
|
||||||
colourWeakness: '色弱模式',
|
themeSchema: {
|
||||||
layoutMode: {
|
title: '主题模式',
|
||||||
title: '布局模式',
|
light: '亮色模式',
|
||||||
vertical: '左侧菜单模式',
|
dark: '暗黑模式',
|
||||||
'vertical-mix': '左侧菜单混合模式',
|
auto: '跟随系统'
|
||||||
horizontal: '顶部菜单模式',
|
},
|
||||||
'horizontal-mix': '顶部菜单混合模式',
|
grayscale: '灰色模式',
|
||||||
reverseHorizontalMix: '一级菜单与子级菜单位置反转'
|
colourWeakness: '色弱模式',
|
||||||
|
themeColor: {
|
||||||
|
title: '主题颜色',
|
||||||
|
primary: '主色',
|
||||||
|
info: '信息色',
|
||||||
|
success: '成功色',
|
||||||
|
warning: '警告色',
|
||||||
|
error: '错误色',
|
||||||
|
followPrimary: '跟随主色'
|
||||||
|
},
|
||||||
|
recommendColor: '应用推荐算法的颜色',
|
||||||
|
recommendColorDesc: '推荐颜色的算法参照'
|
||||||
},
|
},
|
||||||
recommendColor: '应用推荐算法的颜色',
|
layout: {
|
||||||
recommendColorDesc: '推荐颜色的算法参照',
|
layoutMode: {
|
||||||
themeColor: {
|
title: '布局模式',
|
||||||
title: '主题颜色',
|
vertical: '左侧菜单模式',
|
||||||
primary: '主色',
|
'vertical-mix': '左侧菜单混合模式',
|
||||||
info: '信息色',
|
horizontal: '顶部菜单模式',
|
||||||
success: '成功色',
|
'top-hybrid-sidebar-first': '顶部混合-侧边优先',
|
||||||
warning: '警告色',
|
'top-hybrid-header-first': '顶部混合-顶部优先',
|
||||||
error: '错误色',
|
vertical_detail: '左侧菜单布局,菜单在左,内容在右。',
|
||||||
followPrimary: '跟随主色'
|
'vertical-mix_detail': '左侧双菜单布局,一级菜单在左侧深色区域,二级菜单在右侧浅色区域。',
|
||||||
},
|
horizontal_detail: '顶部菜单布局,菜单在顶部,内容在下方。',
|
||||||
scrollMode: {
|
'top-hybrid-sidebar-first_detail': '顶部混合布局,一级菜单在左侧,二级菜单在顶部。',
|
||||||
title: '滚动模式',
|
'top-hybrid-header-first_detail': '顶部混合布局,一级菜单在顶部,二级菜单在左侧。'
|
||||||
wrapper: '外层滚动',
|
},
|
||||||
content: '主体滚动'
|
tab: {
|
||||||
},
|
title: '标签栏设置',
|
||||||
page: {
|
visible: '显示标签栏',
|
||||||
animate: '页面切换动画',
|
cache: '标签栏信息缓存',
|
||||||
mode: {
|
height: '标签栏高度',
|
||||||
title: '页面切换动画类型',
|
mode: {
|
||||||
'fade-slide': '滑动',
|
title: '标签栏风格',
|
||||||
fade: '淡入淡出',
|
chrome: '谷歌风格',
|
||||||
'fade-bottom': '底部消退',
|
button: '按钮风格'
|
||||||
'fade-scale': '缩放消退',
|
}
|
||||||
'zoom-fade': '渐变',
|
},
|
||||||
'zoom-out': '闪现',
|
header: {
|
||||||
none: '无'
|
title: '头部设置',
|
||||||
|
height: '头部高度',
|
||||||
|
breadcrumb: {
|
||||||
|
visible: '显示面包屑',
|
||||||
|
showIcon: '显示面包屑图标'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
sider: {
|
||||||
|
title: '侧边栏设置',
|
||||||
|
inverted: '深色侧边栏',
|
||||||
|
width: '侧边栏宽度',
|
||||||
|
collapsedWidth: '侧边栏折叠宽度',
|
||||||
|
mixWidth: '混合布局侧边栏宽度',
|
||||||
|
mixCollapsedWidth: '混合布局侧边栏折叠宽度',
|
||||||
|
mixChildMenuWidth: '混合布局子菜单宽度'
|
||||||
|
},
|
||||||
|
footer: {
|
||||||
|
title: '底部设置',
|
||||||
|
visible: '显示底部',
|
||||||
|
fixed: '固定底部',
|
||||||
|
height: '底部高度',
|
||||||
|
right: '底部局右'
|
||||||
|
},
|
||||||
|
content: {
|
||||||
|
title: '内容区域设置',
|
||||||
|
scrollMode: {
|
||||||
|
title: '滚动模式',
|
||||||
|
wrapper: '外层滚动',
|
||||||
|
content: '主体滚动'
|
||||||
|
},
|
||||||
|
page: {
|
||||||
|
animate: '页面切换动画',
|
||||||
|
mode: {
|
||||||
|
title: '页面切换动画类型',
|
||||||
|
'fade-slide': '滑动',
|
||||||
|
fade: '淡入淡出',
|
||||||
|
'fade-bottom': '底部消退',
|
||||||
|
'fade-scale': '缩放消退',
|
||||||
|
'zoom-fade': '渐变',
|
||||||
|
'zoom-out': '闪现',
|
||||||
|
none: '无'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fixedHeaderAndTab: '固定头部和标签栏'
|
||||||
|
},
|
||||||
|
resetCacheStrategy: {
|
||||||
|
title: '重置缓存策略',
|
||||||
|
close: '关闭页面',
|
||||||
|
refresh: '刷新页面'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
fixedHeaderAndTab: '固定头部和标签栏',
|
general: {
|
||||||
header: {
|
title: '通用设置',
|
||||||
height: '头部高度',
|
watermark: {
|
||||||
breadcrumb: {
|
title: '水印设置',
|
||||||
visible: '显示面包屑',
|
visible: '显示全屏水印',
|
||||||
showIcon: '显示面包屑图标'
|
text: '自定义水印文本',
|
||||||
|
enableUserName: '启用用户名水印',
|
||||||
|
enableTime: '显示当前时间',
|
||||||
|
timeFormat: '时间格式'
|
||||||
},
|
},
|
||||||
multilingual: {
|
multilingual: {
|
||||||
|
title: '多语言设置',
|
||||||
visible: '显示多语言按钮'
|
visible: '显示多语言按钮'
|
||||||
},
|
},
|
||||||
globalSearch: {
|
globalSearch: {
|
||||||
|
title: '全局搜索设置',
|
||||||
visible: '显示全局搜索按钮'
|
visible: '显示全局搜索按钮'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
tab: {
|
|
||||||
visible: '显示标签栏',
|
|
||||||
cache: '标签栏信息缓存',
|
|
||||||
height: '标签栏高度',
|
|
||||||
mode: {
|
|
||||||
title: '标签栏风格',
|
|
||||||
chrome: '谷歌风格',
|
|
||||||
button: '按钮风格'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
sider: {
|
|
||||||
inverted: '深色侧边栏',
|
|
||||||
width: '侧边栏宽度',
|
|
||||||
collapsedWidth: '侧边栏折叠宽度',
|
|
||||||
mixWidth: '混合布局侧边栏宽度',
|
|
||||||
mixCollapsedWidth: '混合布局侧边栏折叠宽度',
|
|
||||||
mixChildMenuWidth: '混合布局子菜单宽度'
|
|
||||||
},
|
|
||||||
footer: {
|
|
||||||
visible: '显示底部',
|
|
||||||
fixed: '固定底部',
|
|
||||||
height: '底部高度',
|
|
||||||
right: '底部局右'
|
|
||||||
},
|
|
||||||
watermark: {
|
|
||||||
visible: '显示全屏水印',
|
|
||||||
text: '水印文本',
|
|
||||||
enableUserName: '启用用户名水印'
|
|
||||||
},
|
|
||||||
themeDrawerTitle: '主题配置',
|
|
||||||
pageFunTitle: '页面功能',
|
|
||||||
resetCacheStrategy: {
|
|
||||||
title: '重置缓存策略',
|
|
||||||
close: '关闭页面',
|
|
||||||
refresh: '刷新页面'
|
|
||||||
},
|
|
||||||
configOperation: {
|
configOperation: {
|
||||||
copyConfig: '复制配置',
|
copyConfig: '复制配置',
|
||||||
copySuccessMsg: '复制成功,请替换 src/theme/settings.ts 中的变量 themeSettings',
|
copySuccessMsg: '复制成功,请替换 src/theme/settings.ts 中的变量 themeSettings',
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
import { computed, effectScope, onScopeDispose, ref, toRefs, watch } from 'vue';
|
import { computed, effectScope, onScopeDispose, ref, toRefs, watch } from 'vue';
|
||||||
import type { Ref } from 'vue';
|
import type { Ref } from 'vue';
|
||||||
import { useEventListener, usePreferredColorScheme } from '@vueuse/core';
|
import { useDateFormat, useEventListener, useNow, usePreferredColorScheme } from '@vueuse/core';
|
||||||
import { defineStore } from 'pinia';
|
import { defineStore } from 'pinia';
|
||||||
import { getPaletteColorByNumber } from '@sa/color';
|
import { getPaletteColorByNumber } from '@sa/color';
|
||||||
import { localStg } from '@/utils/storage';
|
import { localStg } from '@/utils/storage';
|
||||||
import { SetupStoreId } from '@/enum';
|
import { SetupStoreId } from '@/enum';
|
||||||
|
import { useAuthStore } from '../auth';
|
||||||
import {
|
import {
|
||||||
addThemeVarsToGlobal,
|
addThemeVarsToGlobal,
|
||||||
createThemeToken,
|
createThemeToken,
|
||||||
@@ -18,10 +19,14 @@ import {
|
|||||||
export const useThemeStore = defineStore(SetupStoreId.Theme, () => {
|
export const useThemeStore = defineStore(SetupStoreId.Theme, () => {
|
||||||
const scope = effectScope();
|
const scope = effectScope();
|
||||||
const osTheme = usePreferredColorScheme();
|
const osTheme = usePreferredColorScheme();
|
||||||
|
const authStore = useAuthStore();
|
||||||
|
|
||||||
/** Theme settings */
|
/** Theme settings */
|
||||||
const settings: Ref<App.Theme.ThemeSetting> = ref(initThemeSettings());
|
const settings: Ref<App.Theme.ThemeSetting> = ref(initThemeSettings());
|
||||||
|
|
||||||
|
/** Watermark time instance with controls */
|
||||||
|
const { now: watermarkTime, pause: pauseWatermarkTime, resume: resumeWatermarkTime } = useNow({ controls: true });
|
||||||
|
|
||||||
/** Dark mode */
|
/** Dark mode */
|
||||||
const darkMode = computed(() => {
|
const darkMode = computed(() => {
|
||||||
if (settings.value.themeScheme === 'auto') {
|
if (settings.value.themeScheme === 'auto') {
|
||||||
@@ -57,6 +62,28 @@ export const useThemeStore = defineStore(SetupStoreId.Theme, () => {
|
|||||||
*/
|
*/
|
||||||
const settingsJson = computed(() => JSON.stringify(settings.value));
|
const settingsJson = computed(() => JSON.stringify(settings.value));
|
||||||
|
|
||||||
|
/** Watermark time date formatter */
|
||||||
|
const formattedWatermarkTime = computed(() => {
|
||||||
|
const { watermark } = settings.value;
|
||||||
|
const date = useDateFormat(watermarkTime, watermark.timeFormat);
|
||||||
|
return date.value;
|
||||||
|
});
|
||||||
|
|
||||||
|
/** Watermark content */
|
||||||
|
const watermarkContent = computed(() => {
|
||||||
|
const { watermark } = settings.value;
|
||||||
|
|
||||||
|
if (watermark.enableUserName && authStore.userInfo.userName) {
|
||||||
|
return authStore.userInfo.userName;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (watermark.enableTime) {
|
||||||
|
return formattedWatermarkTime.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return watermark.text;
|
||||||
|
});
|
||||||
|
|
||||||
/** Reset store */
|
/** Reset store */
|
||||||
function resetStore() {
|
function resetStore() {
|
||||||
const themeStore = useThemeStore();
|
const themeStore = useThemeStore();
|
||||||
@@ -144,13 +171,43 @@ export const useThemeStore = defineStore(SetupStoreId.Theme, () => {
|
|||||||
);
|
);
|
||||||
addThemeVarsToGlobal(themeTokens, darkThemeTokens);
|
addThemeVarsToGlobal(themeTokens, darkThemeTokens);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set layout reverse horizontal mix
|
* Set watermark enable user name
|
||||||
*
|
*
|
||||||
* @param reverse Reverse horizontal mix
|
* @param enable Whether to enable user name watermark
|
||||||
*/
|
*/
|
||||||
function setLayoutReverseHorizontalMix(reverse: boolean) {
|
function setWatermarkEnableUserName(enable: boolean) {
|
||||||
settings.value.layout.reverseHorizontalMix = reverse;
|
settings.value.watermark.enableUserName = enable;
|
||||||
|
|
||||||
|
if (enable) {
|
||||||
|
settings.value.watermark.enableTime = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set watermark enable time
|
||||||
|
*
|
||||||
|
* @param enable Whether to enable time watermark
|
||||||
|
*/
|
||||||
|
function setWatermarkEnableTime(enable: boolean) {
|
||||||
|
settings.value.watermark.enableTime = enable;
|
||||||
|
|
||||||
|
if (enable) {
|
||||||
|
settings.value.watermark.enableUserName = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Only run timer when watermark is visible and time display is enabled */
|
||||||
|
function updateWatermarkTimer() {
|
||||||
|
const { watermark } = settings.value;
|
||||||
|
const shouldRunTimer = watermark.visible && watermark.enableTime;
|
||||||
|
|
||||||
|
if (shouldRunTimer) {
|
||||||
|
resumeWatermarkTime();
|
||||||
|
} else {
|
||||||
|
pauseWatermarkTime();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Cache theme settings */
|
/** Cache theme settings */
|
||||||
@@ -196,6 +253,15 @@ export const useThemeStore = defineStore(SetupStoreId.Theme, () => {
|
|||||||
},
|
},
|
||||||
{ immediate: true }
|
{ immediate: true }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// watch watermark settings to control timer
|
||||||
|
watch(
|
||||||
|
() => [settings.value.watermark.visible, settings.value.watermark.enableTime],
|
||||||
|
() => {
|
||||||
|
updateWatermarkTimer();
|
||||||
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
/** On scope dispose */
|
/** On scope dispose */
|
||||||
@@ -209,6 +275,7 @@ export const useThemeStore = defineStore(SetupStoreId.Theme, () => {
|
|||||||
themeColors,
|
themeColors,
|
||||||
naiveTheme,
|
naiveTheme,
|
||||||
settingsJson,
|
settingsJson,
|
||||||
|
watermarkContent,
|
||||||
setGrayscale,
|
setGrayscale,
|
||||||
setColourWeakness,
|
setColourWeakness,
|
||||||
resetStore,
|
resetStore,
|
||||||
@@ -216,6 +283,7 @@ export const useThemeStore = defineStore(SetupStoreId.Theme, () => {
|
|||||||
toggleThemeScheme,
|
toggleThemeScheme,
|
||||||
updateThemeColors,
|
updateThemeColors,
|
||||||
setThemeLayout,
|
setThemeLayout,
|
||||||
setLayoutReverseHorizontalMix
|
setWatermarkEnableUserName,
|
||||||
|
setWatermarkEnableTime
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -15,8 +15,7 @@ export const themeSettings: App.Theme.ThemeSetting = {
|
|||||||
resetCacheStrategy: 'close',
|
resetCacheStrategy: 'close',
|
||||||
layout: {
|
layout: {
|
||||||
mode: 'vertical',
|
mode: 'vertical',
|
||||||
scrollMode: 'content',
|
scrollMode: 'content'
|
||||||
reverseHorizontalMix: false
|
|
||||||
},
|
},
|
||||||
page: {
|
page: {
|
||||||
animate: true,
|
animate: true,
|
||||||
@@ -59,7 +58,9 @@ export const themeSettings: App.Theme.ThemeSetting = {
|
|||||||
watermark: {
|
watermark: {
|
||||||
visible: false,
|
visible: false,
|
||||||
text: 'SoybeanAdmin',
|
text: 'SoybeanAdmin',
|
||||||
enableUserName: false
|
enableUserName: false,
|
||||||
|
enableTime: false,
|
||||||
|
timeFormat: 'YYYY-MM-DD HH:mm'
|
||||||
},
|
},
|
||||||
tokens: {
|
tokens: {
|
||||||
light: {
|
light: {
|
||||||
|
|||||||
37
src/typings/api.d.ts
vendored
37
src/typings/api.d.ts
vendored
@@ -47,41 +47,4 @@ declare namespace Api {
|
|||||||
status: EnableStatus | null;
|
status: EnableStatus | null;
|
||||||
} & T;
|
} & T;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* namespace Auth
|
|
||||||
*
|
|
||||||
* backend api module: "auth"
|
|
||||||
*/
|
|
||||||
namespace Auth {
|
|
||||||
interface LoginToken {
|
|
||||||
token: string;
|
|
||||||
refreshToken: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface UserInfo {
|
|
||||||
userId: string;
|
|
||||||
userName: string;
|
|
||||||
roles: string[];
|
|
||||||
buttons: string[];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* namespace Route
|
|
||||||
*
|
|
||||||
* backend api module: "route"
|
|
||||||
*/
|
|
||||||
namespace Route {
|
|
||||||
type ElegantConstRoute = import('@elegant-router/types').ElegantConstRoute;
|
|
||||||
|
|
||||||
interface MenuRoute extends ElegantConstRoute {
|
|
||||||
id: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface UserRoute {
|
|
||||||
routes: MenuRoute[];
|
|
||||||
home: import('@elegant-router/types').LastLevelRouteKey;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
20
src/typings/api/auth.d.ts
vendored
Normal file
20
src/typings/api/auth.d.ts
vendored
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
declare namespace Api {
|
||||||
|
/**
|
||||||
|
* namespace Auth
|
||||||
|
*
|
||||||
|
* backend api module: "auth"
|
||||||
|
*/
|
||||||
|
namespace Auth {
|
||||||
|
interface LoginToken {
|
||||||
|
token: string;
|
||||||
|
refreshToken: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface UserInfo {
|
||||||
|
userId: string;
|
||||||
|
userName: string;
|
||||||
|
roles: string[];
|
||||||
|
buttons: string[];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
19
src/typings/api/route.d.ts
vendored
Normal file
19
src/typings/api/route.d.ts
vendored
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
declare namespace Api {
|
||||||
|
/**
|
||||||
|
* namespace Route
|
||||||
|
*
|
||||||
|
* backend api module: "route"
|
||||||
|
*/
|
||||||
|
namespace Route {
|
||||||
|
type ElegantConstRoute = import('@elegant-router/types').ElegantConstRoute;
|
||||||
|
|
||||||
|
interface MenuRoute extends ElegantConstRoute {
|
||||||
|
id: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface UserRoute {
|
||||||
|
routes: MenuRoute[];
|
||||||
|
home: import('@elegant-router/types').LastLevelRouteKey;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
143
src/typings/app.d.ts
vendored
143
src/typings/app.d.ts
vendored
@@ -28,12 +28,6 @@ declare namespace App {
|
|||||||
mode: UnionKey.ThemeLayoutMode;
|
mode: UnionKey.ThemeLayoutMode;
|
||||||
/** Scroll mode */
|
/** Scroll mode */
|
||||||
scrollMode: UnionKey.ThemeScrollMode;
|
scrollMode: UnionKey.ThemeScrollMode;
|
||||||
/**
|
|
||||||
* Whether to reverse the horizontal mix
|
|
||||||
*
|
|
||||||
* if true, the vertical child level menus in left and horizontal first level menus in top
|
|
||||||
*/
|
|
||||||
reverseHorizontalMix: boolean;
|
|
||||||
};
|
};
|
||||||
/** Page */
|
/** Page */
|
||||||
page: {
|
page: {
|
||||||
@@ -88,11 +82,14 @@ declare namespace App {
|
|||||||
width: number;
|
width: number;
|
||||||
/** Collapsed sider width */
|
/** Collapsed sider width */
|
||||||
collapsedWidth: number;
|
collapsedWidth: number;
|
||||||
/** Sider width when the layout is 'vertical-mix' or 'horizontal-mix' */
|
/** Sider width when the layout is 'vertical-mix', 'top-hybrid-sidebar-first', or 'top-hybrid-header-first' */
|
||||||
mixWidth: number;
|
mixWidth: number;
|
||||||
/** Collapsed sider width when the layout is 'vertical-mix' or 'horizontal-mix' */
|
/**
|
||||||
|
* Collapsed sider width when the layout is 'vertical-mix', 'top-hybrid-sidebar-first', or
|
||||||
|
* 'top-hybrid-header-first'
|
||||||
|
*/
|
||||||
mixCollapsedWidth: number;
|
mixCollapsedWidth: number;
|
||||||
/** Child menu width when the layout is 'vertical-mix' or 'horizontal-mix' */
|
/** Child menu width when the layout is 'vertical-mix', 'top-hybrid-sidebar-first', or 'top-hybrid-header-first' */
|
||||||
mixChildMenuWidth: number;
|
mixChildMenuWidth: number;
|
||||||
};
|
};
|
||||||
/** Footer */
|
/** Footer */
|
||||||
@@ -103,7 +100,10 @@ declare namespace App {
|
|||||||
fixed: boolean;
|
fixed: boolean;
|
||||||
/** Footer height */
|
/** Footer height */
|
||||||
height: number;
|
height: number;
|
||||||
/** Whether float the footer to the right when the layout is 'horizontal-mix' */
|
/**
|
||||||
|
* Whether float the footer to the right when the layout is 'top-hybrid-sidebar-first' or
|
||||||
|
* 'top-hybrid-header-first'
|
||||||
|
*/
|
||||||
right: boolean;
|
right: boolean;
|
||||||
};
|
};
|
||||||
/** Watermark */
|
/** Watermark */
|
||||||
@@ -114,6 +114,10 @@ declare namespace App {
|
|||||||
text: string;
|
text: string;
|
||||||
/** Whether to use user name as watermark text */
|
/** Whether to use user name as watermark text */
|
||||||
enableUserName: boolean;
|
enableUserName: boolean;
|
||||||
|
/** Whether to use current time as watermark text */
|
||||||
|
enableTime: boolean;
|
||||||
|
/** Time format for watermark text */
|
||||||
|
timeFormat: string;
|
||||||
};
|
};
|
||||||
/** define some theme settings tokens, will transform to css variables */
|
/** define some theme settings tokens, will transform to css variables */
|
||||||
tokens: {
|
tokens: {
|
||||||
@@ -358,63 +362,88 @@ declare namespace App {
|
|||||||
tokenExpired: string;
|
tokenExpired: string;
|
||||||
};
|
};
|
||||||
theme: {
|
theme: {
|
||||||
themeSchema: { title: string } & Record<UnionKey.ThemeScheme, string>;
|
themeDrawerTitle: string;
|
||||||
grayscale: string;
|
tabs: {
|
||||||
colourWeakness: string;
|
appearance: string;
|
||||||
layoutMode: { title: string; reverseHorizontalMix: string } & Record<UnionKey.ThemeLayoutMode, string>;
|
layout: string;
|
||||||
recommendColor: string;
|
general: string;
|
||||||
recommendColorDesc: string;
|
|
||||||
themeColor: {
|
|
||||||
title: string;
|
|
||||||
followPrimary: string;
|
|
||||||
} & Theme.ThemeColor;
|
|
||||||
scrollMode: { title: string } & Record<UnionKey.ThemeScrollMode, string>;
|
|
||||||
page: {
|
|
||||||
animate: string;
|
|
||||||
mode: { title: string } & Record<UnionKey.ThemePageAnimateMode, string>;
|
|
||||||
};
|
};
|
||||||
fixedHeaderAndTab: string;
|
appearance: {
|
||||||
header: {
|
themeSchema: { title: string } & Record<UnionKey.ThemeScheme, string>;
|
||||||
height: string;
|
grayscale: string;
|
||||||
breadcrumb: {
|
colourWeakness: string;
|
||||||
|
themeColor: {
|
||||||
|
title: string;
|
||||||
|
followPrimary: string;
|
||||||
|
} & Theme.ThemeColor;
|
||||||
|
recommendColor: string;
|
||||||
|
recommendColorDesc: string;
|
||||||
|
};
|
||||||
|
layout: {
|
||||||
|
layoutMode: { title: string } & Record<UnionKey.ThemeLayoutMode, string> & {
|
||||||
|
[K in `${UnionKey.ThemeLayoutMode}_detail`]: string;
|
||||||
|
};
|
||||||
|
tab: {
|
||||||
|
title: string;
|
||||||
visible: string;
|
visible: string;
|
||||||
showIcon: string;
|
cache: string;
|
||||||
|
height: string;
|
||||||
|
mode: { title: string } & Record<UnionKey.ThemeTabMode, string>;
|
||||||
|
};
|
||||||
|
header: {
|
||||||
|
title: string;
|
||||||
|
height: string;
|
||||||
|
breadcrumb: {
|
||||||
|
visible: string;
|
||||||
|
showIcon: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
sider: {
|
||||||
|
title: string;
|
||||||
|
inverted: string;
|
||||||
|
width: string;
|
||||||
|
collapsedWidth: string;
|
||||||
|
mixWidth: string;
|
||||||
|
mixCollapsedWidth: string;
|
||||||
|
mixChildMenuWidth: string;
|
||||||
|
};
|
||||||
|
footer: {
|
||||||
|
title: string;
|
||||||
|
visible: string;
|
||||||
|
fixed: string;
|
||||||
|
height: string;
|
||||||
|
right: string;
|
||||||
|
};
|
||||||
|
content: {
|
||||||
|
title: string;
|
||||||
|
scrollMode: { title: string } & Record<UnionKey.ThemeScrollMode, string>;
|
||||||
|
page: {
|
||||||
|
animate: string;
|
||||||
|
mode: { title: string } & Record<UnionKey.ThemePageAnimateMode, string>;
|
||||||
|
};
|
||||||
|
fixedHeaderAndTab: string;
|
||||||
|
};
|
||||||
|
resetCacheStrategy: { title: string } & Record<UnionKey.ResetCacheStrategy, string>;
|
||||||
|
};
|
||||||
|
general: {
|
||||||
|
title: string;
|
||||||
|
watermark: {
|
||||||
|
title: string;
|
||||||
|
visible: string;
|
||||||
|
text: string;
|
||||||
|
enableUserName: string;
|
||||||
|
enableTime: string;
|
||||||
|
timeFormat: string;
|
||||||
};
|
};
|
||||||
multilingual: {
|
multilingual: {
|
||||||
|
title: string;
|
||||||
visible: string;
|
visible: string;
|
||||||
};
|
};
|
||||||
globalSearch: {
|
globalSearch: {
|
||||||
|
title: string;
|
||||||
visible: string;
|
visible: string;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
tab: {
|
|
||||||
visible: string;
|
|
||||||
cache: string;
|
|
||||||
height: string;
|
|
||||||
mode: { title: string } & Record<UnionKey.ThemeTabMode, string>;
|
|
||||||
};
|
|
||||||
sider: {
|
|
||||||
inverted: string;
|
|
||||||
width: string;
|
|
||||||
collapsedWidth: string;
|
|
||||||
mixWidth: string;
|
|
||||||
mixCollapsedWidth: string;
|
|
||||||
mixChildMenuWidth: string;
|
|
||||||
};
|
|
||||||
footer: {
|
|
||||||
visible: string;
|
|
||||||
fixed: string;
|
|
||||||
height: string;
|
|
||||||
right: string;
|
|
||||||
};
|
|
||||||
watermark: {
|
|
||||||
visible: string;
|
|
||||||
text: string;
|
|
||||||
enableUserName: string;
|
|
||||||
};
|
|
||||||
themeDrawerTitle: string;
|
|
||||||
pageFunTitle: string;
|
|
||||||
resetCacheStrategy: { title: string } & Record<UnionKey.ResetCacheStrategy, string>;
|
|
||||||
configOperation: {
|
configOperation: {
|
||||||
copyConfig: string;
|
copyConfig: string;
|
||||||
copySuccessMsg: string;
|
copySuccessMsg: string;
|
||||||
|
|||||||
20
src/typings/components.d.ts
vendored
20
src/typings/components.d.ts
vendored
@@ -17,23 +17,14 @@ declare module 'vue' {
|
|||||||
FullScreen: typeof import('./../components/common/full-screen.vue')['default']
|
FullScreen: typeof import('./../components/common/full-screen.vue')['default']
|
||||||
IconAntDesignEnterOutlined: typeof import('~icons/ant-design/enter-outlined')['default']
|
IconAntDesignEnterOutlined: typeof import('~icons/ant-design/enter-outlined')['default']
|
||||||
IconAntDesignReloadOutlined: typeof import('~icons/ant-design/reload-outlined')['default']
|
IconAntDesignReloadOutlined: typeof import('~icons/ant-design/reload-outlined')['default']
|
||||||
IconAntDesignSettingOutlined: typeof import('~icons/ant-design/setting-outlined')['default']
|
|
||||||
IconGridiconsFullscreen: typeof import('~icons/gridicons/fullscreen')['default']
|
IconGridiconsFullscreen: typeof import('~icons/gridicons/fullscreen')['default']
|
||||||
IconGridiconsFullscreenExit: typeof import('~icons/gridicons/fullscreen-exit')['default']
|
IconGridiconsFullscreenExit: typeof import('~icons/gridicons/fullscreen-exit')['default']
|
||||||
'IconIc:roundPlus': typeof import('~icons/ic/round-plus')['default']
|
|
||||||
IconIcRoundDelete: typeof import('~icons/ic/round-delete')['default']
|
|
||||||
IconIcRoundPlus: typeof import('~icons/ic/round-plus')['default']
|
|
||||||
IconIcRoundRefresh: typeof import('~icons/ic/round-refresh')['default']
|
|
||||||
IconIcRoundRemove: typeof import('~icons/ic/round-remove')['default']
|
|
||||||
IconIcRoundSearch: typeof import('~icons/ic/round-search')['default']
|
|
||||||
IconLocalBanner: typeof import('~icons/local/banner')['default']
|
IconLocalBanner: typeof import('~icons/local/banner')['default']
|
||||||
IconLocalLogo: typeof import('~icons/local/logo')['default']
|
IconLocalLogo: typeof import('~icons/local/logo')['default']
|
||||||
IconMdiArrowDownThin: typeof import('~icons/mdi/arrow-down-thin')['default']
|
IconMdiArrowDownThin: typeof import('~icons/mdi/arrow-down-thin')['default']
|
||||||
IconMdiArrowUpThin: typeof import('~icons/mdi/arrow-up-thin')['default']
|
IconMdiArrowUpThin: typeof import('~icons/mdi/arrow-up-thin')['default']
|
||||||
IconMdiDrag: typeof import('~icons/mdi/drag')['default']
|
|
||||||
IconMdiKeyboardEsc: typeof import('~icons/mdi/keyboard-esc')['default']
|
IconMdiKeyboardEsc: typeof import('~icons/mdi/keyboard-esc')['default']
|
||||||
IconMdiKeyboardReturn: typeof import('~icons/mdi/keyboard-return')['default']
|
IconMdiKeyboardReturn: typeof import('~icons/mdi/keyboard-return')['default']
|
||||||
IconMdiRefresh: typeof import('~icons/mdi/refresh')['default']
|
|
||||||
IconUilSearch: typeof import('~icons/uil/search')['default']
|
IconUilSearch: typeof import('~icons/uil/search')['default']
|
||||||
LangSwitch: typeof import('./../components/common/lang-switch.vue')['default']
|
LangSwitch: typeof import('./../components/common/lang-switch.vue')['default']
|
||||||
LookForward: typeof import('./../components/custom/look-forward.vue')['default']
|
LookForward: typeof import('./../components/custom/look-forward.vue')['default']
|
||||||
@@ -45,19 +36,14 @@ declare module 'vue' {
|
|||||||
NCard: typeof import('naive-ui')['NCard']
|
NCard: typeof import('naive-ui')['NCard']
|
||||||
NCheckbox: typeof import('naive-ui')['NCheckbox']
|
NCheckbox: typeof import('naive-ui')['NCheckbox']
|
||||||
NColorPicker: typeof import('naive-ui')['NColorPicker']
|
NColorPicker: typeof import('naive-ui')['NColorPicker']
|
||||||
NDataTable: typeof import('naive-ui')['NDataTable']
|
|
||||||
NDescriptions: typeof import('naive-ui')['NDescriptions']
|
|
||||||
NDescriptionsItem: typeof import('naive-ui')['NDescriptionsItem']
|
|
||||||
NDialogProvider: typeof import('naive-ui')['NDialogProvider']
|
NDialogProvider: typeof import('naive-ui')['NDialogProvider']
|
||||||
NDivider: typeof import('naive-ui')['NDivider']
|
NDivider: typeof import('naive-ui')['NDivider']
|
||||||
NDrawer: typeof import('naive-ui')['NDrawer']
|
NDrawer: typeof import('naive-ui')['NDrawer']
|
||||||
NDrawerContent: typeof import('naive-ui')['NDrawerContent']
|
NDrawerContent: typeof import('naive-ui')['NDrawerContent']
|
||||||
NDropdown: typeof import('naive-ui')['NDropdown']
|
NDropdown: typeof import('naive-ui')['NDropdown']
|
||||||
NDynamicInput: typeof import('naive-ui')['NDynamicInput']
|
|
||||||
NEmpty: typeof import('naive-ui')['NEmpty']
|
NEmpty: typeof import('naive-ui')['NEmpty']
|
||||||
NForm: typeof import('naive-ui')['NForm']
|
NForm: typeof import('naive-ui')['NForm']
|
||||||
NFormItem: typeof import('naive-ui')['NFormItem']
|
NFormItem: typeof import('naive-ui')['NFormItem']
|
||||||
NFormItemGi: typeof import('naive-ui')['NFormItemGi']
|
|
||||||
NGi: typeof import('naive-ui')['NGi']
|
NGi: typeof import('naive-ui')['NGi']
|
||||||
NGrid: typeof import('naive-ui')['NGrid']
|
NGrid: typeof import('naive-ui')['NGrid']
|
||||||
NInput: typeof import('naive-ui')['NInput']
|
NInput: typeof import('naive-ui')['NInput']
|
||||||
@@ -70,10 +56,6 @@ declare module 'vue' {
|
|||||||
NMessageProvider: typeof import('naive-ui')['NMessageProvider']
|
NMessageProvider: typeof import('naive-ui')['NMessageProvider']
|
||||||
NModal: typeof import('naive-ui')['NModal']
|
NModal: typeof import('naive-ui')['NModal']
|
||||||
NNotificationProvider: typeof import('naive-ui')['NNotificationProvider']
|
NNotificationProvider: typeof import('naive-ui')['NNotificationProvider']
|
||||||
NPopconfirm: typeof import('naive-ui')['NPopconfirm']
|
|
||||||
NPopover: typeof import('naive-ui')['NPopover']
|
|
||||||
NRadio: typeof import('naive-ui')['NRadio']
|
|
||||||
NRadioGroup: typeof import('naive-ui')['NRadioGroup']
|
|
||||||
NScrollbar: typeof import('naive-ui')['NScrollbar']
|
NScrollbar: typeof import('naive-ui')['NScrollbar']
|
||||||
NSelect: typeof import('naive-ui')['NSelect']
|
NSelect: typeof import('naive-ui')['NSelect']
|
||||||
NSpace: typeof import('naive-ui')['NSpace']
|
NSpace: typeof import('naive-ui')['NSpace']
|
||||||
@@ -81,10 +63,8 @@ declare module 'vue' {
|
|||||||
NSwitch: typeof import('naive-ui')['NSwitch']
|
NSwitch: typeof import('naive-ui')['NSwitch']
|
||||||
NTab: typeof import('naive-ui')['NTab']
|
NTab: typeof import('naive-ui')['NTab']
|
||||||
NTabs: typeof import('naive-ui')['NTabs']
|
NTabs: typeof import('naive-ui')['NTabs']
|
||||||
NTag: typeof import('naive-ui')['NTag']
|
|
||||||
NThing: typeof import('naive-ui')['NThing']
|
NThing: typeof import('naive-ui')['NThing']
|
||||||
NTooltip: typeof import('naive-ui')['NTooltip']
|
NTooltip: typeof import('naive-ui')['NTooltip']
|
||||||
NTree: typeof import('naive-ui')['NTree']
|
|
||||||
NWatermark: typeof import('naive-ui')['NWatermark']
|
NWatermark: typeof import('naive-ui')['NWatermark']
|
||||||
PinToggler: typeof import('./../components/common/pin-toggler.vue')['default']
|
PinToggler: typeof import('./../components/common/pin-toggler.vue')['default']
|
||||||
ReloadButton: typeof import('./../components/common/reload-button.vue')['default']
|
ReloadButton: typeof import('./../components/common/reload-button.vue')['default']
|
||||||
|
|||||||
10
src/typings/union-key.d.ts
vendored
10
src/typings/union-key.d.ts
vendored
@@ -28,9 +28,15 @@ declare namespace UnionKey {
|
|||||||
* - vertical: the vertical menu in left
|
* - vertical: the vertical menu in left
|
||||||
* - horizontal: the horizontal menu in top
|
* - horizontal: the horizontal menu in top
|
||||||
* - vertical-mix: two vertical mixed menus in left
|
* - vertical-mix: two vertical mixed menus in left
|
||||||
* - horizontal-mix: the vertical first level menus in left and horizontal child level menus in top
|
* - top-hybrid-sidebar-first: the vertical first level menus in left and horizontal child level menus in top
|
||||||
|
* - top-hybrid-header-first: the horizontal first level menus in top and vertical child level menus in left
|
||||||
*/
|
*/
|
||||||
type ThemeLayoutMode = 'vertical' | 'horizontal' | 'vertical-mix' | 'horizontal-mix';
|
type ThemeLayoutMode =
|
||||||
|
| 'vertical'
|
||||||
|
| 'horizontal'
|
||||||
|
| 'vertical-mix'
|
||||||
|
| 'top-hybrid-sidebar-first'
|
||||||
|
| 'top-hybrid-header-first';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The scroll mode when content overflow
|
* The scroll mode when content overflow
|
||||||
|
|||||||
Reference in New Issue
Block a user