mirror of
https://gitee.com/dapppp/ruoyi-plus-vben5.git
synced 2026-04-06 10:43:17 +08:00
Merge branch 'main' of https://github.com/vbenjs/vue-vben-admin into dev
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
export * from './config';
|
export * from './config';
|
||||||
export * from './options';
|
export * from './options';
|
||||||
export * from './plugins';
|
export * from './plugins';
|
||||||
|
export type * from './typing';
|
||||||
export { loadAndConvertEnv } from './utils/env';
|
export { loadAndConvertEnv } from './utils/env';
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ export {
|
|||||||
FoldHorizontal,
|
FoldHorizontal,
|
||||||
Fullscreen,
|
Fullscreen,
|
||||||
Github,
|
Github,
|
||||||
|
Grid,
|
||||||
Grip,
|
Grip,
|
||||||
GripVertical,
|
GripVertical,
|
||||||
Menu as IconDefault,
|
Menu as IconDefault,
|
||||||
@@ -36,6 +37,7 @@ export {
|
|||||||
Info,
|
Info,
|
||||||
InspectionPanel,
|
InspectionPanel,
|
||||||
Languages,
|
Languages,
|
||||||
|
LayoutGrid,
|
||||||
LoaderCircle,
|
LoaderCircle,
|
||||||
LockKeyhole,
|
LockKeyhole,
|
||||||
LogOut,
|
LogOut,
|
||||||
|
|||||||
34
packages/@core/base/typings/src/helper.d.ts
vendored
34
packages/@core/base/typings/src/helper.d.ts
vendored
@@ -1,20 +1,38 @@
|
|||||||
import type { ComputedRef, MaybeRef } from 'vue';
|
import type { ComputedRef, MaybeRef } from 'vue';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 类型级递归中增加深度计数
|
||||||
|
*/
|
||||||
|
type Increment<A extends unknown[]> = [...A, unknown];
|
||||||
/**
|
/**
|
||||||
* 深层递归所有属性为可选
|
* 深层递归所有属性为可选
|
||||||
*/
|
*/
|
||||||
type DeepPartial<T> = T extends object
|
type DeepPartial<
|
||||||
? {
|
T,
|
||||||
[P in keyof T]?: DeepPartial<T[P]>;
|
D extends number = 10,
|
||||||
}
|
C extends unknown[] = [],
|
||||||
: T;
|
> = C['length'] extends D
|
||||||
|
? T
|
||||||
|
: T extends object
|
||||||
|
? {
|
||||||
|
[P in keyof T]?: DeepPartial<T[P], D, Increment<C>>;
|
||||||
|
}
|
||||||
|
: T;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 深层递归所有属性为只读
|
* 深层递归所有属性为只读
|
||||||
*/
|
*/
|
||||||
type DeepReadonly<T> = {
|
type DeepReadonly<
|
||||||
readonly [P in keyof T]: T[P] extends object ? DeepReadonly<T[P]> : T[P];
|
T,
|
||||||
};
|
D extends number = 10,
|
||||||
|
C extends unknown[] = [],
|
||||||
|
> = C['length'] extends D
|
||||||
|
? T
|
||||||
|
: T extends object
|
||||||
|
? {
|
||||||
|
readonly [P in keyof T]: DeepReadonly<T[P], D, Increment<C>>;
|
||||||
|
}
|
||||||
|
: T;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 任意类型的异步函数
|
* 任意类型的异步函数
|
||||||
|
|||||||
@@ -104,6 +104,7 @@ exports[`defaultPreferences immutability test > should not modify the config obj
|
|||||||
"showIcon": true,
|
"showIcon": true,
|
||||||
"showMaximize": true,
|
"showMaximize": true,
|
||||||
"showMore": true,
|
"showMore": true,
|
||||||
|
"showRefresh": true,
|
||||||
"styleType": "chrome",
|
"styleType": "chrome",
|
||||||
"visitHistory": true,
|
"visitHistory": true,
|
||||||
"wheelable": true,
|
"wheelable": true,
|
||||||
@@ -119,6 +120,7 @@ exports[`defaultPreferences immutability test > should not modify the config obj
|
|||||||
"radius": "0.5",
|
"radius": "0.5",
|
||||||
"semiDarkHeader": false,
|
"semiDarkHeader": false,
|
||||||
"semiDarkSidebar": false,
|
"semiDarkSidebar": false,
|
||||||
|
"semiDarkSidebarSub": false,
|
||||||
},
|
},
|
||||||
"transition": {
|
"transition": {
|
||||||
"enable": true,
|
"enable": true,
|
||||||
|
|||||||
@@ -105,6 +105,7 @@ const defaultPreferences: Preferences = {
|
|||||||
showIcon: true,
|
showIcon: true,
|
||||||
showMaximize: true,
|
showMaximize: true,
|
||||||
showMore: true,
|
showMore: true,
|
||||||
|
showRefresh: true,
|
||||||
styleType: 'chrome',
|
styleType: 'chrome',
|
||||||
visitHistory: true,
|
visitHistory: true,
|
||||||
wheelable: true,
|
wheelable: true,
|
||||||
@@ -120,6 +121,7 @@ const defaultPreferences: Preferences = {
|
|||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
semiDarkHeader: false,
|
semiDarkHeader: false,
|
||||||
semiDarkSidebar: false,
|
semiDarkSidebar: false,
|
||||||
|
semiDarkSidebarSub: false,
|
||||||
},
|
},
|
||||||
transition: {
|
transition: {
|
||||||
enable: true,
|
enable: true,
|
||||||
|
|||||||
@@ -38,12 +38,10 @@ const BUILT_IN_THEME_PRESETS: BuiltinThemePreset[] = [
|
|||||||
primaryColor: 'hsl(240 5.9% 10%)',
|
primaryColor: 'hsl(240 5.9% 10%)',
|
||||||
type: 'zinc',
|
type: 'zinc',
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
color: 'hsl(181 84% 32%)',
|
color: 'hsl(181 84% 32%)',
|
||||||
type: 'deep-green',
|
type: 'deep-green',
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
color: 'hsl(211 91% 39%)',
|
color: 'hsl(211 91% 39%)',
|
||||||
type: 'deep-blue',
|
type: 'deep-blue',
|
||||||
@@ -56,7 +54,6 @@ const BUILT_IN_THEME_PRESETS: BuiltinThemePreset[] = [
|
|||||||
color: 'hsl(0 75% 42%)',
|
color: 'hsl(0 75% 42%)',
|
||||||
type: 'rose',
|
type: 'rose',
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
color: 'hsl(0 0% 25%)',
|
color: 'hsl(0 0% 25%)',
|
||||||
darkPrimaryColor: 'hsl(0 0% 98%)',
|
darkPrimaryColor: 'hsl(0 0% 98%)',
|
||||||
|
|||||||
@@ -222,6 +222,8 @@ interface TabbarPreferences {
|
|||||||
showMaximize: boolean;
|
showMaximize: boolean;
|
||||||
/** 显示更多按钮 */
|
/** 显示更多按钮 */
|
||||||
showMore: boolean;
|
showMore: boolean;
|
||||||
|
/** 显示刷新按钮 */
|
||||||
|
showRefresh: boolean;
|
||||||
/** 标签页风格 */
|
/** 标签页风格 */
|
||||||
styleType: TabsStyleType;
|
styleType: TabsStyleType;
|
||||||
/** 是否开启访问历史记录 */
|
/** 是否开启访问历史记录 */
|
||||||
@@ -251,6 +253,8 @@ interface ThemePreferences {
|
|||||||
semiDarkHeader: boolean;
|
semiDarkHeader: boolean;
|
||||||
/** 是否开启半深色菜单(只在theme='light'时生效) */
|
/** 是否开启半深色菜单(只在theme='light'时生效) */
|
||||||
semiDarkSidebar: boolean;
|
semiDarkSidebar: boolean;
|
||||||
|
/** 是否开启半深色子菜单(只在theme='light'时生效) */
|
||||||
|
semiDarkSidebarSub: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface TransitionPreferences {
|
interface TransitionPreferences {
|
||||||
|
|||||||
@@ -381,10 +381,10 @@ onUnmounted(() => {
|
|||||||
<div v-if="suffix" class="ml-1">
|
<div v-if="suffix" class="ml-1">
|
||||||
<VbenRenderContent :content="suffix" />
|
<VbenRenderContent :content="suffix" />
|
||||||
</div>
|
</div>
|
||||||
<FormDescription v-if="description" class="ml-1">
|
|
||||||
<VbenRenderContent :content="description" />
|
|
||||||
</FormDescription>
|
|
||||||
</div>
|
</div>
|
||||||
|
<FormDescription v-if="description" class="text-xs">
|
||||||
|
<VbenRenderContent :content="description" />
|
||||||
|
</FormDescription>
|
||||||
|
|
||||||
<Transition name="slide-up" v-if="!compact">
|
<Transition name="slide-up" v-if="!compact">
|
||||||
<FormMessage class="absolute bottom-[-4px]" />
|
<FormMessage class="absolute bottom-[-4px]" />
|
||||||
|
|||||||
@@ -77,7 +77,10 @@ interface Props {
|
|||||||
* 主题
|
* 主题
|
||||||
*/
|
*/
|
||||||
theme: string;
|
theme: string;
|
||||||
|
/**
|
||||||
|
* 子主题
|
||||||
|
*/
|
||||||
|
themeSub: string;
|
||||||
/**
|
/**
|
||||||
* 宽度
|
* 宽度
|
||||||
*/
|
*/
|
||||||
@@ -261,40 +264,48 @@ function handleMouseleave() {
|
|||||||
class="h-full transition-all duration-150"
|
class="h-full transition-all duration-150"
|
||||||
></div>
|
></div>
|
||||||
<aside
|
<aside
|
||||||
:class="[
|
|
||||||
theme,
|
|
||||||
{
|
|
||||||
'bg-sidebar-deep': isSidebarMixed,
|
|
||||||
'border-r border-border bg-sidebar': !isSidebarMixed,
|
|
||||||
},
|
|
||||||
]"
|
|
||||||
:style="style"
|
:style="style"
|
||||||
class="fixed left-0 top-0 h-full transition-all duration-150"
|
class="fixed left-0 top-0 h-full transition-all duration-150"
|
||||||
@mouseenter="handleMouseenter"
|
@mouseenter="handleMouseenter"
|
||||||
@mouseleave="handleMouseleave"
|
@mouseleave="handleMouseleave"
|
||||||
>
|
>
|
||||||
<SidebarFixedButton
|
<div
|
||||||
v-if="!collapse && !isSidebarMixed && showFixedButton"
|
class="h-full"
|
||||||
v-model:expand-on-hover="expandOnHover"
|
:class="[
|
||||||
/>
|
theme,
|
||||||
<div v-if="slots.logo" :style="headerStyle">
|
{
|
||||||
<slot name="logo"></slot>
|
'bg-sidebar-deep': isSidebarMixed,
|
||||||
</div>
|
'border-r border-border bg-sidebar': !isSidebarMixed,
|
||||||
<VbenScrollbar :style="contentStyle" shadow shadow-border>
|
},
|
||||||
<slot></slot>
|
]"
|
||||||
</VbenScrollbar>
|
:style="{ width: `${width}px` }"
|
||||||
|
>
|
||||||
|
<SidebarFixedButton
|
||||||
|
v-if="!collapse && !isSidebarMixed && showFixedButton"
|
||||||
|
v-model:expand-on-hover="expandOnHover"
|
||||||
|
/>
|
||||||
|
<div v-if="slots.logo" :style="headerStyle">
|
||||||
|
<slot name="logo"></slot>
|
||||||
|
</div>
|
||||||
|
<VbenScrollbar :style="contentStyle" shadow shadow-border>
|
||||||
|
<slot></slot>
|
||||||
|
</VbenScrollbar>
|
||||||
|
|
||||||
<div :style="collapseStyle"></div>
|
<div :style="collapseStyle"></div>
|
||||||
<SidebarCollapseButton
|
<SidebarCollapseButton
|
||||||
v-if="showCollapseButton && !isSidebarMixed"
|
v-if="showCollapseButton && !isSidebarMixed"
|
||||||
v-model:collapsed="collapse"
|
v-model:collapsed="collapse"
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="isSidebarMixed"
|
v-if="isSidebarMixed"
|
||||||
ref="asideRef"
|
ref="asideRef"
|
||||||
:class="{
|
:class="[
|
||||||
'border-l': extraVisible,
|
themeSub,
|
||||||
}"
|
{
|
||||||
|
'border-l': extraVisible,
|
||||||
|
},
|
||||||
|
]"
|
||||||
:style="extraStyle"
|
:style="extraStyle"
|
||||||
class="fixed top-0 h-full overflow-hidden border-r border-border bg-sidebar transition-all duration-200"
|
class="fixed top-0 h-full overflow-hidden border-r border-border bg-sidebar transition-all duration-200"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -146,6 +146,11 @@ interface VbenLayoutProps {
|
|||||||
* @default dark
|
* @default dark
|
||||||
*/
|
*/
|
||||||
sidebarTheme?: ThemeModeType;
|
sidebarTheme?: ThemeModeType;
|
||||||
|
/**
|
||||||
|
* 侧边栏子栏
|
||||||
|
* @default dark
|
||||||
|
*/
|
||||||
|
sidebarThemeSub?: ThemeModeType;
|
||||||
/**
|
/**
|
||||||
* 侧边栏宽度
|
* 侧边栏宽度
|
||||||
* @default 210
|
* @default 210
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ const props = withDefaults(defineProps<Props>(), {
|
|||||||
sidebarHidden: false,
|
sidebarHidden: false,
|
||||||
sidebarMixedWidth: 80,
|
sidebarMixedWidth: 80,
|
||||||
sidebarTheme: 'dark',
|
sidebarTheme: 'dark',
|
||||||
|
sidebarThemeSub: 'dark',
|
||||||
sidebarWidth: 180,
|
sidebarWidth: 180,
|
||||||
sideCollapseWidth: 60,
|
sideCollapseWidth: 60,
|
||||||
tabbarEnable: true,
|
tabbarEnable: true,
|
||||||
@@ -502,6 +503,7 @@ const idMainContent = ELEMENT_ID_MAIN_CONTENT;
|
|||||||
:mixed-width="sidebarMixedWidth"
|
:mixed-width="sidebarMixedWidth"
|
||||||
:show="showSidebar"
|
:show="showSidebar"
|
||||||
:theme="sidebarTheme"
|
:theme="sidebarTheme"
|
||||||
|
:theme-sub="sidebarThemeSub"
|
||||||
:width="getSidebarWidth"
|
:width="getSidebarWidth"
|
||||||
:z-index="sidebarZIndex"
|
:z-index="sidebarZIndex"
|
||||||
@leave="() => emit('sideMouseLeave')"
|
@leave="() => emit('sideMouseLeave')"
|
||||||
|
|||||||
@@ -463,33 +463,33 @@ $namespace: vben;
|
|||||||
&.is-dark {
|
&.is-dark {
|
||||||
--menu-background-color: hsl(var(--menu));
|
--menu-background-color: hsl(var(--menu));
|
||||||
// --menu-submenu-opened-background-color: hsl(var(--menu-opened-dark));
|
// --menu-submenu-opened-background-color: hsl(var(--menu-opened-dark));
|
||||||
--menu-item-background-color: var(--menu-background-color);
|
|
||||||
--menu-item-color: hsl(var(--foreground) / 80%);
|
--menu-item-color: hsl(var(--foreground) / 80%);
|
||||||
|
--menu-item-background-color: var(--menu-background-color);
|
||||||
--menu-item-hover-color: hsl(var(--accent-foreground));
|
--menu-item-hover-color: hsl(var(--accent-foreground));
|
||||||
--menu-item-hover-background-color: hsl(var(--accent));
|
--menu-item-hover-background-color: hsl(var(--accent));
|
||||||
--menu-item-active-color: hsl(var(--accent-foreground));
|
--menu-item-active-color: hsl(var(--accent-foreground));
|
||||||
--menu-item-active-background-color: hsl(var(--accent));
|
--menu-item-active-background-color: hsl(var(--accent));
|
||||||
--menu-submenu-hover-color: hsl(var(--foreground));
|
|
||||||
--menu-submenu-hover-background-color: hsl(var(--accent));
|
|
||||||
--menu-submenu-active-color: hsl(var(--foreground));
|
|
||||||
--menu-submenu-active-background-color: transparent;
|
|
||||||
--menu-submenu-background-color: var(--menu-background-color);
|
--menu-submenu-background-color: var(--menu-background-color);
|
||||||
|
--menu-submenu-hover-color: hsl(var(--accent-foreground));
|
||||||
|
--menu-submenu-hover-background-color: hsl(var(--accent));
|
||||||
|
--menu-submenu-active-color: hsl(var(--accent-foreground));
|
||||||
|
--menu-submenu-active-background-color: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.is-light {
|
&.is-light {
|
||||||
--menu-background-color: hsl(var(--menu));
|
--menu-background-color: hsl(var(--menu));
|
||||||
// --menu-submenu-opened-background-color: hsl(var(--menu-opened));
|
// --menu-submenu-opened-background-color: hsl(var(--menu-opened));
|
||||||
|
--menu-item-color: hsl(var(--accent-foreground));
|
||||||
--menu-item-background-color: var(--menu-background-color);
|
--menu-item-background-color: var(--menu-background-color);
|
||||||
--menu-item-color: hsl(var(--foreground));
|
|
||||||
--menu-item-hover-color: var(--menu-item-color);
|
--menu-item-hover-color: var(--menu-item-color);
|
||||||
--menu-item-hover-background-color: hsl(var(--accent));
|
--menu-item-hover-background-color: hsl(var(--accent));
|
||||||
--menu-item-active-color: hsl(var(--primary));
|
--menu-item-active-color: hsl(var(--primary));
|
||||||
--menu-item-active-background-color: hsl(var(--primary) / 15%);
|
--menu-item-active-background-color: hsl(var(--primary) / 15%);
|
||||||
|
--menu-submenu-background-color: var(--menu-background-color);
|
||||||
--menu-submenu-hover-color: hsl(var(--primary));
|
--menu-submenu-hover-color: hsl(var(--primary));
|
||||||
--menu-submenu-hover-background-color: hsl(var(--accent));
|
--menu-submenu-hover-background-color: hsl(var(--accent));
|
||||||
--menu-submenu-active-color: hsl(var(--primary));
|
--menu-submenu-active-color: hsl(var(--primary));
|
||||||
--menu-submenu-active-background-color: transparent;
|
--menu-submenu-active-background-color: transparent;
|
||||||
--menu-submenu-background-color: var(--menu-background-color);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&.is-rounded {
|
&.is-rounded {
|
||||||
@@ -518,25 +518,33 @@ $namespace: vben;
|
|||||||
--menu-background-color: transparent;
|
--menu-background-color: transparent;
|
||||||
|
|
||||||
&.is-dark {
|
&.is-dark {
|
||||||
|
--menu-background-color: hsl(var(--menu));
|
||||||
|
--menu-item-color: hsl(var(--foreground) / 80%);
|
||||||
|
--menu-item-background-color: var(--menu-background-color);
|
||||||
--menu-item-hover-color: hsl(var(--accent-foreground));
|
--menu-item-hover-color: hsl(var(--accent-foreground));
|
||||||
--menu-item-hover-background-color: hsl(var(--accent));
|
--menu-item-hover-background-color: hsl(var(--accent));
|
||||||
--menu-item-active-color: hsl(var(--accent-foreground));
|
--menu-item-active-color: hsl(var(--accent-foreground));
|
||||||
--menu-item-active-background-color: hsl(var(--accent));
|
--menu-item-active-background-color: hsl(var(--accent));
|
||||||
--menu-submenu-active-color: hsl(var(--foreground));
|
--menu-submenu-background-color: var(--menu-background-color);
|
||||||
--menu-submenu-active-background-color: hsl(var(--accent));
|
|
||||||
--menu-submenu-hover-color: hsl(var(--accent-foreground));
|
--menu-submenu-hover-color: hsl(var(--accent-foreground));
|
||||||
--menu-submenu-hover-background-color: hsl(var(--accent));
|
--menu-submenu-hover-background-color: hsl(var(--accent));
|
||||||
|
--menu-submenu-active-color: hsl(var(--accent-foreground));
|
||||||
|
--menu-submenu-active-background-color: hsl(var(--accent));
|
||||||
}
|
}
|
||||||
|
|
||||||
&.is-light {
|
&.is-light {
|
||||||
|
--menu-background-color: hsl(var(--menu));
|
||||||
|
--menu-item-color: hsl(var(--accent-foreground));
|
||||||
|
--menu-item-background-color: var(--menu-background-color);
|
||||||
|
--menu-item-hover-color: var(--menu-item-color);
|
||||||
|
--menu-item-hover-background-color: hsl(var(--accent));
|
||||||
--menu-item-active-color: hsl(var(--primary));
|
--menu-item-active-color: hsl(var(--primary));
|
||||||
--menu-item-active-background-color: hsl(var(--primary) / 15%);
|
--menu-item-active-background-color: hsl(var(--primary) / 15%);
|
||||||
--menu-item-hover-background-color: hsl(var(--accent));
|
--menu-submenu-background-color: var(--menu-background-color);
|
||||||
--menu-item-hover-color: hsl(var(--primary));
|
|
||||||
--menu-submenu-active-color: hsl(var(--primary));
|
|
||||||
--menu-submenu-active-background-color: hsl(var(--primary) / 15%);
|
|
||||||
--menu-submenu-hover-color: hsl(var(--primary));
|
--menu-submenu-hover-color: hsl(var(--primary));
|
||||||
--menu-submenu-hover-background-color: hsl(var(--accent));
|
--menu-submenu-hover-background-color: hsl(var(--accent));
|
||||||
|
--menu-submenu-active-color: hsl(var(--primary));
|
||||||
|
--menu-submenu-active-background-color: hsl(var(--primary) / 15%);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -862,9 +870,8 @@ $namespace: vben;
|
|||||||
padding-right: 12px !important;
|
padding-right: 12px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
// &:not(.is-active):hover {
|
&:not(.is-active):hover {
|
||||||
&:hover {
|
//color: var(--menu-submenu-hover-color);
|
||||||
color: var(--menu-submenu-hover-color);
|
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
background: var(--menu-submenu-hover-background-color) !important;
|
background: var(--menu-submenu-hover-background-color) !important;
|
||||||
|
|||||||
@@ -151,10 +151,12 @@ $namespace: vben;
|
|||||||
}
|
}
|
||||||
|
|
||||||
&__name {
|
&__name {
|
||||||
|
width: 100%;
|
||||||
margin-top: 8px;
|
margin-top: 8px;
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
font-size: calc(var(--font-size-base, 16px) * 0.75);
|
font-size: calc(var(--font-size-base, 16px) * 0.75);
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
|
text-align: center;
|
||||||
transition: all 0.25s ease;
|
transition: all 0.25s ease;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -210,6 +210,7 @@ onBeforeUnmount(() => {
|
|||||||
opened ? '' : 'hidden',
|
opened ? '' : 'hidden',
|
||||||
'overflow-auto',
|
'overflow-auto',
|
||||||
'max-h-[calc(var(--reka-hover-card-content-available-height)-20px)]',
|
'max-h-[calc(var(--reka-hover-card-content-available-height)-20px)]',
|
||||||
|
mode === 'horizontal' ? 'is-horizontal' : '',
|
||||||
]"
|
]"
|
||||||
:content-props="contentProps"
|
:content-props="contentProps"
|
||||||
:open="true"
|
:open="true"
|
||||||
|
|||||||
@@ -158,7 +158,7 @@ function onMouseDown(e: MouseEvent, tab: TabConfig) {
|
|||||||
<VbenIcon
|
<VbenIcon
|
||||||
v-if="showIcon"
|
v-if="showIcon"
|
||||||
:icon="tab.icon"
|
:icon="tab.icon"
|
||||||
class="mr-1 flex size-4 items-center overflow-hidden"
|
class="mr-1 flex size-4 items-center overflow-hidden group-hover:animate-[shrink_0.3s_ease-in-out]"
|
||||||
/>
|
/>
|
||||||
<span class="flex-1 overflow-hidden whitespace-nowrap text-sm">
|
<span class="flex-1 overflow-hidden whitespace-nowrap text-sm">
|
||||||
{{ tab.title }}
|
{{ tab.title }}
|
||||||
|
|||||||
@@ -132,7 +132,7 @@ function onMouseDown(e: MouseEvent, tab: TabConfig) {
|
|||||||
<VbenIcon
|
<VbenIcon
|
||||||
v-if="showIcon"
|
v-if="showIcon"
|
||||||
:icon="tab.icon"
|
:icon="tab.icon"
|
||||||
class="mr-2 flex size-4 items-center overflow-hidden"
|
class="mr-2 flex size-4 items-center overflow-hidden group-hover:animate-[shrink_0.3s_ease-in-out]"
|
||||||
fallback
|
fallback
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|||||||
@@ -1,2 +1,3 @@
|
|||||||
export { default as TabsToolMore } from './tool-more.vue';
|
export { default as TabsToolMore } from './tool-more.vue';
|
||||||
|
export { default as TabsToolRefresh } from './tool-refresh.vue';
|
||||||
export { default as TabsToolScreen } from './tool-screen.vue';
|
export { default as TabsToolScreen } from './tool-screen.vue';
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { DropdownMenuProps } from '@vben-core/shadcn-ui';
|
import type { DropdownMenuProps } from '@vben-core/shadcn-ui';
|
||||||
|
|
||||||
import { ChevronDown } from '@vben-core/icons';
|
import { LayoutGrid } from '@vben-core/icons';
|
||||||
import { VbenDropdownMenu } from '@vben-core/shadcn-ui';
|
import { VbenDropdownMenu } from '@vben-core/shadcn-ui';
|
||||||
|
|
||||||
defineProps<DropdownMenuProps>();
|
defineProps<DropdownMenuProps>();
|
||||||
@@ -12,7 +12,7 @@ defineProps<DropdownMenuProps>();
|
|||||||
<div
|
<div
|
||||||
class="flex-center h-full cursor-pointer border-l border-border px-2 text-lg font-semibold text-muted-foreground hover:bg-muted hover:text-foreground"
|
class="flex-center h-full cursor-pointer border-l border-border px-2 text-lg font-semibold text-muted-foreground hover:bg-muted hover:text-foreground"
|
||||||
>
|
>
|
||||||
<ChevronDown class="size-4" />
|
<LayoutGrid class="size-4" />
|
||||||
</div>
|
</div>
|
||||||
</VbenDropdownMenu>
|
</VbenDropdownMenu>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import { RotateCw } from '@vben-core/icons';
|
||||||
|
|
||||||
|
const emit = defineEmits(['refresh']);
|
||||||
|
|
||||||
|
const handleRefresh = () => {
|
||||||
|
emit('refresh');
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
class="flex-center h-full cursor-pointer border-l border-border px-2 text-lg font-semibold text-muted-foreground hover:bg-muted hover:text-foreground"
|
||||||
|
@click="handleRefresh"
|
||||||
|
>
|
||||||
|
<RotateCw class="size-4" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
import type { TabsEmits, TabsProps } from './types';
|
import type { TabsEmits, TabsProps } from './types';
|
||||||
|
|
||||||
import { useForwardPropsEmits } from '@vben-core/composables';
|
import { useForwardPropsEmits } from '@vben-core/composables';
|
||||||
import { ChevronLeft, ChevronRight } from '@vben-core/icons';
|
import { ChevronsLeft, ChevronsRight } from '@vben-core/icons';
|
||||||
import { VbenScrollbar } from '@vben-core/shadcn-ui';
|
import { VbenScrollbar } from '@vben-core/shadcn-ui';
|
||||||
|
|
||||||
import { Tabs, TabsChrome } from './components';
|
import { Tabs, TabsChrome } from './components';
|
||||||
@@ -60,7 +60,7 @@ useTabsDrag(props, emit);
|
|||||||
class="border-r px-2"
|
class="border-r px-2"
|
||||||
@click="scrollDirection('left')"
|
@click="scrollDirection('left')"
|
||||||
>
|
>
|
||||||
<ChevronLeft class="size-4 h-full" />
|
<ChevronsLeft class="size-4 h-full" />
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
@@ -101,7 +101,7 @@ useTabsDrag(props, emit);
|
|||||||
class="cursor-pointer border-l px-2 text-muted-foreground hover:bg-muted"
|
class="cursor-pointer border-l px-2 text-muted-foreground hover:bg-muted"
|
||||||
@click="scrollDirection('right')"
|
@click="scrollDirection('right')"
|
||||||
>
|
>
|
||||||
<ChevronRight class="size-4 h-full" />
|
<ChevronsRight class="size-4 h-full" />
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -60,6 +60,11 @@ const sidebarTheme = computed(() => {
|
|||||||
return dark ? 'dark' : 'light';
|
return dark ? 'dark' : 'light';
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const sidebarThemeSub = computed(() => {
|
||||||
|
const dark = isDark.value || preferences.theme.semiDarkSidebarSub;
|
||||||
|
return dark ? 'dark' : 'light';
|
||||||
|
});
|
||||||
|
|
||||||
const headerTheme = computed(() => {
|
const headerTheme = computed(() => {
|
||||||
const dark = isDark.value || preferences.theme.semiDarkHeader;
|
const dark = isDark.value || preferences.theme.semiDarkHeader;
|
||||||
return dark ? 'dark' : 'light';
|
return dark ? 'dark' : 'light';
|
||||||
@@ -240,6 +245,7 @@ const headerSlots = computed(() => {
|
|||||||
:sidebar-hidden="preferences.sidebar.hidden"
|
:sidebar-hidden="preferences.sidebar.hidden"
|
||||||
:sidebar-mixed-width="preferences.sidebar.mixedWidth"
|
:sidebar-mixed-width="preferences.sidebar.mixedWidth"
|
||||||
:sidebar-theme="sidebarTheme"
|
:sidebar-theme="sidebarTheme"
|
||||||
|
:sidebar-theme-sub="sidebarThemeSub"
|
||||||
:sidebar-width="preferences.sidebar.width"
|
:sidebar-width="preferences.sidebar.width"
|
||||||
:side-collapse-width="preferences.sidebar.collapseWidth"
|
:side-collapse-width="preferences.sidebar.collapseWidth"
|
||||||
:tabbar-enable="preferences.tabbar.enable"
|
:tabbar-enable="preferences.tabbar.enable"
|
||||||
@@ -355,7 +361,7 @@ const headerSlots = computed(() => {
|
|||||||
:collapse="preferences.sidebar.extraCollapse"
|
:collapse="preferences.sidebar.extraCollapse"
|
||||||
:menus="wrapperMenus(extraMenus)"
|
:menus="wrapperMenus(extraMenus)"
|
||||||
:rounded="isMenuRounded"
|
:rounded="isMenuRounded"
|
||||||
:theme="sidebarTheme"
|
:theme="sidebarThemeSub"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<template #side-extra-title>
|
<template #side-extra-title>
|
||||||
@@ -411,7 +417,7 @@ const headerSlots = computed(() => {
|
|||||||
|
|
||||||
<template v-if="preferencesButtonPosition.fixed">
|
<template v-if="preferencesButtonPosition.fixed">
|
||||||
<Preferences
|
<Preferences
|
||||||
class="z-100 fixed bottom-20 right-0"
|
class="z-100 fixed right-0 top-1/2 -translate-y-1/2 transform"
|
||||||
@clear-preferences-and-logout="clearPreferencesAndLogout"
|
@clear-preferences-and-logout="clearPreferencesAndLogout"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -6,7 +6,12 @@ import { useContentMaximize, useTabs } from '@vben/hooks';
|
|||||||
import { preferences } from '@vben/preferences';
|
import { preferences } from '@vben/preferences';
|
||||||
import { useTabbarStore } from '@vben/stores';
|
import { useTabbarStore } from '@vben/stores';
|
||||||
|
|
||||||
import { TabsToolMore, TabsToolScreen, TabsView } from '@vben-core/tabs-ui';
|
import {
|
||||||
|
TabsToolMore,
|
||||||
|
TabsToolRefresh,
|
||||||
|
TabsToolScreen,
|
||||||
|
TabsView,
|
||||||
|
} from '@vben-core/tabs-ui';
|
||||||
|
|
||||||
import { useTabbar } from './use-tabbar';
|
import { useTabbar } from './use-tabbar';
|
||||||
|
|
||||||
@@ -19,7 +24,7 @@ defineProps<{ showIcon?: boolean; theme?: string }>();
|
|||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const tabbarStore = useTabbarStore();
|
const tabbarStore = useTabbarStore();
|
||||||
const { contentIsMaximize, toggleMaximize } = useContentMaximize();
|
const { contentIsMaximize, toggleMaximize } = useContentMaximize();
|
||||||
const { unpinTab } = useTabs();
|
const { refreshTab, unpinTab } = useTabs();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
createContextMenus,
|
createContextMenus,
|
||||||
@@ -65,6 +70,10 @@ if (!preferences.tabbar.persist) {
|
|||||||
/>
|
/>
|
||||||
<div class="flex-center h-full">
|
<div class="flex-center h-full">
|
||||||
<TabsToolMore v-if="preferences.tabbar.showMore" :menus="menus" />
|
<TabsToolMore v-if="preferences.tabbar.showMore" :menus="menus" />
|
||||||
|
<TabsToolRefresh
|
||||||
|
v-if="preferences.tabbar.showRefresh"
|
||||||
|
@refresh="refreshTab"
|
||||||
|
/>
|
||||||
<TabsToolScreen
|
<TabsToolScreen
|
||||||
v-if="preferences.tabbar.showMaximize"
|
v-if="preferences.tabbar.showMaximize"
|
||||||
:screen="contentIsMaximize"
|
:screen="contentIsMaximize"
|
||||||
|
|||||||
@@ -58,7 +58,6 @@ function typeView(name: BuiltinThemeType) {
|
|||||||
case 'green': {
|
case 'green': {
|
||||||
return $t('preferences.theme.builtin.green');
|
return $t('preferences.theme.builtin.green');
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'neutral': {
|
case 'neutral': {
|
||||||
return $t('preferences.theme.builtin.neutral');
|
return $t('preferences.theme.builtin.neutral');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,11 @@
|
|||||||
import type { ThemeModeType } from '@vben/types';
|
import type { ThemeModeType } from '@vben/types';
|
||||||
import type { Component } from 'vue';
|
import type { Component } from 'vue';
|
||||||
|
|
||||||
|
import { watch } from 'vue';
|
||||||
|
|
||||||
import { MoonStar, Sun, SunMoon } from '@vben/icons';
|
import { MoonStar, Sun, SunMoon } from '@vben/icons';
|
||||||
import { $t } from '@vben/locales';
|
import { $t } from '@vben/locales';
|
||||||
|
import { usePreferences } from '@vben/preferences';
|
||||||
|
|
||||||
import SwitchItem from '../switch-item.vue';
|
import SwitchItem from '../switch-item.vue';
|
||||||
|
|
||||||
@@ -13,8 +16,20 @@ defineOptions({
|
|||||||
|
|
||||||
const modelValue = defineModel<string>({ default: 'auto' });
|
const modelValue = defineModel<string>({ default: 'auto' });
|
||||||
const themeSemiDarkSidebar = defineModel<boolean>('themeSemiDarkSidebar');
|
const themeSemiDarkSidebar = defineModel<boolean>('themeSemiDarkSidebar');
|
||||||
|
const themeSemiDarkSidebarSub = defineModel<boolean>('themeSemiDarkSidebarSub');
|
||||||
const themeSemiDarkHeader = defineModel<boolean>('themeSemiDarkHeader');
|
const themeSemiDarkHeader = defineModel<boolean>('themeSemiDarkHeader');
|
||||||
|
|
||||||
|
const { layout } = usePreferences();
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => themeSemiDarkSidebar.value,
|
||||||
|
() => {
|
||||||
|
if (!themeSemiDarkSidebar.value) {
|
||||||
|
themeSemiDarkSidebarSub.value = themeSemiDarkSidebar.value;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
const THEME_PRESET: Array<{ icon: Component; name: ThemeModeType }> = [
|
const THEME_PRESET: Array<{ icon: Component; name: ThemeModeType }> = [
|
||||||
{
|
{
|
||||||
icon: Sun,
|
icon: Sun,
|
||||||
@@ -70,11 +85,27 @@ function nameView(name: string) {
|
|||||||
|
|
||||||
<SwitchItem
|
<SwitchItem
|
||||||
v-model="themeSemiDarkSidebar"
|
v-model="themeSemiDarkSidebar"
|
||||||
:disabled="modelValue === 'dark'"
|
:disabled="
|
||||||
|
modelValue === 'dark' ||
|
||||||
|
layout === 'header-nav' ||
|
||||||
|
layout === 'full-content'
|
||||||
|
"
|
||||||
|
:tip="$t('preferences.theme.darkSidebarTip')"
|
||||||
class="mt-6"
|
class="mt-6"
|
||||||
>
|
>
|
||||||
{{ $t('preferences.theme.darkSidebar') }}
|
{{ $t('preferences.theme.darkSidebar') }}
|
||||||
</SwitchItem>
|
</SwitchItem>
|
||||||
|
<SwitchItem
|
||||||
|
v-model="themeSemiDarkSidebarSub"
|
||||||
|
:disabled="
|
||||||
|
modelValue === 'dark' ||
|
||||||
|
(layout !== 'header-mixed-nav' && layout !== 'sidebar-mixed-nav') ||
|
||||||
|
!themeSemiDarkSidebar
|
||||||
|
"
|
||||||
|
:tip="$t('preferences.theme.darkSidebarSubTip')"
|
||||||
|
>
|
||||||
|
{{ $t('preferences.theme.darkSidebarSub') }}
|
||||||
|
</SwitchItem>
|
||||||
<SwitchItem v-model="themeSemiDarkHeader" :disabled="modelValue === 'dark'">
|
<SwitchItem v-model="themeSemiDarkHeader" :disabled="modelValue === 'dark'">
|
||||||
{{ $t('preferences.theme.darkHeader') }}
|
{{ $t('preferences.theme.darkHeader') }}
|
||||||
</SwitchItem>
|
</SwitchItem>
|
||||||
|
|||||||
@@ -88,6 +88,7 @@ const themeMode = defineModel<ThemeModeType>('themeMode');
|
|||||||
const themeRadius = defineModel<string>('themeRadius');
|
const themeRadius = defineModel<string>('themeRadius');
|
||||||
const themeFontSize = defineModel<number>('themeFontSize');
|
const themeFontSize = defineModel<number>('themeFontSize');
|
||||||
const themeSemiDarkSidebar = defineModel<boolean>('themeSemiDarkSidebar');
|
const themeSemiDarkSidebar = defineModel<boolean>('themeSemiDarkSidebar');
|
||||||
|
const themeSemiDarkSidebarSub = defineModel<boolean>('themeSemiDarkSidebarSub');
|
||||||
const themeSemiDarkHeader = defineModel<boolean>('themeSemiDarkHeader');
|
const themeSemiDarkHeader = defineModel<boolean>('themeSemiDarkHeader');
|
||||||
|
|
||||||
const sidebarEnable = defineModel<boolean>('sidebarEnable');
|
const sidebarEnable = defineModel<boolean>('sidebarEnable');
|
||||||
@@ -319,6 +320,7 @@ async function handleReset() {
|
|||||||
v-model="themeMode"
|
v-model="themeMode"
|
||||||
v-model:theme-semi-dark-header="themeSemiDarkHeader"
|
v-model:theme-semi-dark-header="themeSemiDarkHeader"
|
||||||
v-model:theme-semi-dark-sidebar="themeSemiDarkSidebar"
|
v-model:theme-semi-dark-sidebar="themeSemiDarkSidebar"
|
||||||
|
v-model:theme-semi-dark-sidebar-sub="themeSemiDarkSidebarSub"
|
||||||
/>
|
/>
|
||||||
</Block>
|
</Block>
|
||||||
<Block :title="$t('preferences.theme.builtin.title')">
|
<Block :title="$t('preferences.theme.builtin.title')">
|
||||||
|
|||||||
@@ -6,7 +6,17 @@ import type { Nullable } from '@vben/types';
|
|||||||
|
|
||||||
import type EchartsUI from './echarts-ui.vue';
|
import type EchartsUI from './echarts-ui.vue';
|
||||||
|
|
||||||
import { computed, nextTick, watch } from 'vue';
|
import {
|
||||||
|
computed,
|
||||||
|
nextTick,
|
||||||
|
onActivated,
|
||||||
|
onBeforeUnmount,
|
||||||
|
onDeactivated,
|
||||||
|
onMounted,
|
||||||
|
ref,
|
||||||
|
unref,
|
||||||
|
watch,
|
||||||
|
} from 'vue';
|
||||||
|
|
||||||
import { usePreferences } from '@vben/preferences';
|
import { usePreferences } from '@vben/preferences';
|
||||||
|
|
||||||
@@ -27,6 +37,8 @@ type EchartsThemeType = 'dark' | 'light' | null;
|
|||||||
function useEcharts(chartRef: Ref<EchartsUIType>) {
|
function useEcharts(chartRef: Ref<EchartsUIType>) {
|
||||||
let chartInstance: echarts.ECharts | null = null;
|
let chartInstance: echarts.ECharts | null = null;
|
||||||
let cacheOptions: EChartsOption = {};
|
let cacheOptions: EChartsOption = {};
|
||||||
|
// echart是否处于激活状态
|
||||||
|
const isActiveRef = ref(false);
|
||||||
|
|
||||||
const { isDark } = usePreferences();
|
const { isDark } = usePreferences();
|
||||||
const { height, width } = useWindowSize();
|
const { height, width } = useWindowSize();
|
||||||
@@ -42,6 +54,11 @@ function useEcharts(chartRef: Ref<EchartsUIType>) {
|
|||||||
return maybeComponent.$el ?? null;
|
return maybeComponent.$el ?? null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
onMounted(() => (isActiveRef.value = true));
|
||||||
|
onActivated(() => (isActiveRef.value = true));
|
||||||
|
onDeactivated(() => (isActiveRef.value = false));
|
||||||
|
onBeforeUnmount(() => (isActiveRef.value = false));
|
||||||
|
|
||||||
const isElHidden = (el: HTMLElement | null): boolean => {
|
const isElHidden = (el: HTMLElement | null): boolean => {
|
||||||
if (!el) return true;
|
if (!el) return true;
|
||||||
return el.offsetHeight === 0 || el.offsetWidth === 0;
|
return el.offsetHeight === 0 || el.offsetWidth === 0;
|
||||||
@@ -71,6 +88,9 @@ function useEcharts(chartRef: Ref<EchartsUIType>) {
|
|||||||
options: EChartsOption,
|
options: EChartsOption,
|
||||||
clear = true,
|
clear = true,
|
||||||
): Promise<Nullable<echarts.ECharts>> => {
|
): Promise<Nullable<echarts.ECharts>> => {
|
||||||
|
if (!unref(isActiveRef)) {
|
||||||
|
return Promise.resolve(null);
|
||||||
|
}
|
||||||
cacheOptions = options;
|
cacheOptions = options;
|
||||||
const currentOptions = {
|
const currentOptions = {
|
||||||
...options,
|
...options,
|
||||||
@@ -105,7 +125,7 @@ function useEcharts(chartRef: Ref<EchartsUIType>) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateDate = (
|
const updateData = (
|
||||||
option: EChartsOption,
|
option: EChartsOption,
|
||||||
notMerge = false, // false = 合并(保留动画),true = 完全替换
|
notMerge = false, // false = 合并(保留动画),true = 完全替换
|
||||||
lazyUpdate = false, // true 时不立即重绘,适合短时间内多次调用
|
lazyUpdate = false, // true 时不立即重绘,适合短时间内多次调用
|
||||||
@@ -156,8 +176,8 @@ function useEcharts(chartRef: Ref<EchartsUIType>) {
|
|||||||
|
|
||||||
useResizeObserver(chartRef as never, resizeHandler);
|
useResizeObserver(chartRef as never, resizeHandler);
|
||||||
|
|
||||||
watch(isDark, () => {
|
watch([isDark, isActiveRef], () => {
|
||||||
if (chartInstance) {
|
if (chartInstance && unref(isActiveRef)) {
|
||||||
chartInstance.dispose();
|
chartInstance.dispose();
|
||||||
initCharts();
|
initCharts();
|
||||||
renderEcharts(cacheOptions);
|
renderEcharts(cacheOptions);
|
||||||
@@ -170,9 +190,10 @@ function useEcharts(chartRef: Ref<EchartsUIType>) {
|
|||||||
chartInstance?.dispose();
|
chartInstance?.dispose();
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
|
isActive: isActiveRef,
|
||||||
renderEcharts,
|
renderEcharts,
|
||||||
resize,
|
resize,
|
||||||
updateDate,
|
updateData,
|
||||||
getChartInstance: () => chartInstance,
|
getChartInstance: () => chartInstance,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -384,9 +384,11 @@ onUnmounted(() => {
|
|||||||
<!-- 左侧操作区域或者title -->
|
<!-- 左侧操作区域或者title -->
|
||||||
<template v-if="showToolbar" #toolbar-actions="slotProps">
|
<template v-if="showToolbar" #toolbar-actions="slotProps">
|
||||||
<slot v-if="showTableTitle" name="table-title">
|
<slot v-if="showTableTitle" name="table-title">
|
||||||
<div class="mr-1 pl-1 text-[1rem]">
|
<div
|
||||||
|
class="flex items-center justify-center gap-1 text-[1rem] font-bold"
|
||||||
|
>
|
||||||
{{ tableTitle }}
|
{{ tableTitle }}
|
||||||
<VbenHelpTooltip v-if="tableTitleHelp" trigger-class="pb-1">
|
<VbenHelpTooltip v-if="tableTitleHelp">
|
||||||
{{ tableTitleHelp }}
|
{{ tableTitleHelp }}
|
||||||
</VbenHelpTooltip>
|
</VbenHelpTooltip>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -127,6 +127,9 @@
|
|||||||
"light": "Light",
|
"light": "Light",
|
||||||
"dark": "Dark",
|
"dark": "Dark",
|
||||||
"darkSidebar": "Semi Dark Sidebar",
|
"darkSidebar": "Semi Dark Sidebar",
|
||||||
|
"darkSidebarTip": "It can be enabled when the theme is light, and the layout is neither 'Horizontal' nor 'Full Content'.",
|
||||||
|
"darkSidebarSub": "Semi Dark Sidebar Sub",
|
||||||
|
"darkSidebarSubTip": "It can be enabled when the theme is light, the semi dark sidebar is enabled, and the layout uses 'Two-Column' menu mode.",
|
||||||
"darkHeader": "Semi Dark Header",
|
"darkHeader": "Semi Dark Header",
|
||||||
"weakMode": "Weak Mode",
|
"weakMode": "Weak Mode",
|
||||||
"grayMode": "Gray Mode",
|
"grayMode": "Gray Mode",
|
||||||
|
|||||||
@@ -127,6 +127,9 @@
|
|||||||
"light": "浅色",
|
"light": "浅色",
|
||||||
"dark": "深色",
|
"dark": "深色",
|
||||||
"darkSidebar": "深色侧边栏",
|
"darkSidebar": "深色侧边栏",
|
||||||
|
"darkSidebarTip": "当主题为浅色,布局不为水平菜单或不为内容全屏时可开启",
|
||||||
|
"darkSidebarSub": "深色侧边栏子栏",
|
||||||
|
"darkSidebarSubTip": "当主题为浅色,开启深色侧边栏且布局使用双列菜单模式时可开启",
|
||||||
"darkHeader": "深色顶栏",
|
"darkHeader": "深色顶栏",
|
||||||
"weakMode": "色弱模式",
|
"weakMode": "色弱模式",
|
||||||
"grayMode": "灰色模式",
|
"grayMode": "灰色模式",
|
||||||
|
|||||||
@@ -583,6 +583,23 @@ export const useTabbarStore = defineStore('core-tabbar', {
|
|||||||
{
|
{
|
||||||
pick: ['tabs', 'visitHistory'],
|
pick: ['tabs', 'visitHistory'],
|
||||||
storage: sessionStorage,
|
storage: sessionStorage,
|
||||||
|
serializer: {
|
||||||
|
serialize: JSON.stringify,
|
||||||
|
deserialize(value: string) {
|
||||||
|
const parsed = JSON.parse(value);
|
||||||
|
// Stack 类实例经 JSON 序列化后会变成普通对象 {dedup, items, maxSize},
|
||||||
|
// 丢失所有方法和 getter,需要重新构建 Stack 实例
|
||||||
|
if (parsed.visitHistory && !(parsed.visitHistory instanceof Stack)) {
|
||||||
|
const raw = parsed.visitHistory;
|
||||||
|
const stack = createStack<string>(true, MAX_VISIT_HISTORY);
|
||||||
|
if (Array.isArray(raw.items)) {
|
||||||
|
stack.push(...raw.items);
|
||||||
|
}
|
||||||
|
parsed.visitHistory = stack;
|
||||||
|
}
|
||||||
|
return parsed;
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
state: (): TabbarState => ({
|
state: (): TabbarState => ({
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ const routes: RouteRecordRaw[] = [
|
|||||||
affixTab: true,
|
affixTab: true,
|
||||||
icon: 'lucide:area-chart',
|
icon: 'lucide:area-chart',
|
||||||
title: $t('page.dashboard.analytics'),
|
title: $t('page.dashboard.analytics'),
|
||||||
|
keepAlive: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import type { RouteRecordRaw } from 'vue-router';
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
VBEN_ANT_PREVIEW_URL,
|
VBEN_ANT_PREVIEW_URL,
|
||||||
|
VBEN_ANTDV_NEXT_PREVIEW_URL,
|
||||||
VBEN_DOC_URL,
|
VBEN_DOC_URL,
|
||||||
VBEN_ELE_PREVIEW_URL,
|
VBEN_ELE_PREVIEW_URL,
|
||||||
VBEN_GITHUB_URL,
|
VBEN_GITHUB_URL,
|
||||||
@@ -9,7 +10,11 @@ import {
|
|||||||
VBEN_NAIVE_PREVIEW_URL,
|
VBEN_NAIVE_PREVIEW_URL,
|
||||||
VBEN_TD_PREVIEW_URL,
|
VBEN_TD_PREVIEW_URL,
|
||||||
} from '@vben/constants';
|
} from '@vben/constants';
|
||||||
import { SvgAntdvLogoIcon, SvgTDesignIcon } from '@vben/icons';
|
import {
|
||||||
|
SvgAntdvLogoIcon,
|
||||||
|
SvgAntdvNextLogoIcon,
|
||||||
|
SvgTDesignIcon,
|
||||||
|
} from '@vben/icons';
|
||||||
|
|
||||||
import { IFrameView } from '#/layouts';
|
import { IFrameView } from '#/layouts';
|
||||||
import { $t } from '#/locales';
|
import { $t } from '#/locales';
|
||||||
@@ -56,6 +61,18 @@ const routes: RouteRecordRaw[] = [
|
|||||||
title: $t('demos.vben.antdv'),
|
title: $t('demos.vben.antdv'),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'VbenAntdVNext',
|
||||||
|
path: '/vben-admin/antdv-next',
|
||||||
|
component: IFrameView,
|
||||||
|
meta: {
|
||||||
|
badgeType: 'dot',
|
||||||
|
icon: SvgAntdvNextLogoIcon,
|
||||||
|
link: VBEN_ANTDV_NEXT_PREVIEW_URL,
|
||||||
|
title: $t('demos.vben.antdv-next'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
name: 'VbenNaive',
|
name: 'VbenNaive',
|
||||||
path: '/vben-admin/naive',
|
path: '/vben-admin/naive',
|
||||||
|
|||||||
@@ -67,6 +67,13 @@ const [BaseForm, baseFormApi] = useVbenForm({
|
|||||||
label: '字符串',
|
label: '字符串',
|
||||||
rules: 'required',
|
rules: 'required',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
component: 'Input',
|
||||||
|
fieldName: 'desc',
|
||||||
|
// 界面显示的description
|
||||||
|
description: '这是表单描述',
|
||||||
|
label: '字符串(带描述)',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
// 组件需要在 #/adapter.ts内注册,并加上类型
|
// 组件需要在 #/adapter.ts内注册,并加上类型
|
||||||
component: 'ApiSelect',
|
component: 'ApiSelect',
|
||||||
|
|||||||
Reference in New Issue
Block a user