feat: Feature/pro docs (#70)
* chore: merge main * feat: update docs * feat: remove coze-assistant * feat: add watermark plugin * feat: update preferences * feat: update docs --------- Co-authored-by: vince <vince292007@gmail.com>
This commit is contained in:
@@ -40,7 +40,7 @@
|
||||
"@vueuse/core": "^10.11.0",
|
||||
"radix-vue": "^1.9.2",
|
||||
"sortablejs": "^1.15.2",
|
||||
"vue": "^3.4.33"
|
||||
"vue": "^3.4.34"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/sortablejs": "^1.15.8"
|
||||
|
@@ -31,6 +31,6 @@
|
||||
"@vben-core/toolkit": "workspace:*",
|
||||
"@vben-core/typings": "workspace:*",
|
||||
"@vueuse/core": "^10.11.0",
|
||||
"vue": "^3.4.33"
|
||||
"vue": "^3.4.34"
|
||||
}
|
||||
}
|
||||
|
@@ -9,14 +9,15 @@ const defaultPreferences: Preferences = {
|
||||
compact: false,
|
||||
contentCompact: 'wide',
|
||||
defaultAvatar:
|
||||
'https://cdn.jsdelivr.net/npm/@vbenjs/static-source@0.1.3/source/avatar-v1.webp',
|
||||
'https://unpkg.com/@vbenjs/static-source@0.1.5/source/avatar-v1.webp',
|
||||
dynamicTitle: true,
|
||||
enablePreferences: true,
|
||||
isMobile: false,
|
||||
layout: 'sidebar-nav',
|
||||
locale: 'zh-CN',
|
||||
loginExpiredMode: 'page',
|
||||
loginExpiredMode: 'modal',
|
||||
name: 'Vben Admin',
|
||||
watermark: false,
|
||||
},
|
||||
breadcrumb: {
|
||||
enable: true,
|
||||
@@ -44,8 +45,7 @@ const defaultPreferences: Preferences = {
|
||||
},
|
||||
logo: {
|
||||
enable: true,
|
||||
source:
|
||||
'https://cdn.jsdelivr.net/npm/@vbenjs/static-source@0.1.3/source/logo-v1.webp',
|
||||
source: 'https://unpkg.com/@vbenjs/static-source@0.1.5/source/logo-v1.webp',
|
||||
},
|
||||
navigation: {
|
||||
accordion: true,
|
||||
@@ -75,6 +75,9 @@ const defaultPreferences: Preferences = {
|
||||
keepAlive: true,
|
||||
persist: true,
|
||||
showIcon: true,
|
||||
showMaximize: true,
|
||||
showMore: true,
|
||||
showRefresh: true,
|
||||
styleType: 'chrome',
|
||||
},
|
||||
theme: {
|
||||
@@ -94,7 +97,6 @@ const defaultPreferences: Preferences = {
|
||||
progress: true,
|
||||
},
|
||||
widget: {
|
||||
aiAssistant: true,
|
||||
fullscreen: true,
|
||||
globalSearch: true,
|
||||
languageToggle: true,
|
||||
|
@@ -1,12 +1,4 @@
|
||||
import type {
|
||||
BuiltinThemeType,
|
||||
SupportedLanguagesType,
|
||||
} from '@vben-core/typings';
|
||||
|
||||
interface Language {
|
||||
key: SupportedLanguagesType;
|
||||
text: string;
|
||||
}
|
||||
import type { BuiltinThemeType } from '@vben-core/typings';
|
||||
|
||||
interface BuiltinThemePreset {
|
||||
color: string;
|
||||
@@ -15,25 +7,11 @@ interface BuiltinThemePreset {
|
||||
type: BuiltinThemeType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Supported languages
|
||||
*/
|
||||
const SUPPORT_LANGUAGES: Language[] = [
|
||||
{
|
||||
key: 'zh-CN',
|
||||
text: '简体中文',
|
||||
},
|
||||
{
|
||||
key: 'en-US',
|
||||
text: 'English',
|
||||
},
|
||||
];
|
||||
|
||||
const BUILT_IN_THEME_PRESETS: BuiltinThemePreset[] = [
|
||||
{
|
||||
color: 'hsl(231 98% 65%)',
|
||||
type: 'default',
|
||||
},
|
||||
// {
|
||||
// color: 'hsl(231 98% 65%)',
|
||||
// type: 'default',
|
||||
// },
|
||||
{
|
||||
color: 'hsl(245 82% 67%)',
|
||||
type: 'violet',
|
||||
@@ -102,6 +80,6 @@ const BUILT_IN_THEME_PRESETS: BuiltinThemePreset[] = [
|
||||
|
||||
export const COLOR_PRESETS = [...BUILT_IN_THEME_PRESETS].slice(0, 7);
|
||||
|
||||
export { BUILT_IN_THEME_PRESETS, SUPPORT_LANGUAGES };
|
||||
export { BUILT_IN_THEME_PRESETS };
|
||||
|
||||
export type { BuiltinThemePreset };
|
||||
|
@@ -24,17 +24,6 @@ describe('preferences', () => {
|
||||
preferenceManager = new PreferenceManager();
|
||||
});
|
||||
|
||||
it('initPreferences should initialize preferences with overrides and namespace', async () => {
|
||||
const overrides = { theme: { colorPrimary: 'hsl(231 98% 65%)' } };
|
||||
const namespace = 'testNamespace';
|
||||
|
||||
await preferenceManager.initPreferences({ namespace, overrides });
|
||||
|
||||
expect(preferenceManager.getPreferences().theme.colorPrimary).toBe(
|
||||
overrides.theme.colorPrimary,
|
||||
);
|
||||
});
|
||||
|
||||
it('loads default preferences if no saved preferences found', () => {
|
||||
const preferences = preferenceManager.getPreferences();
|
||||
expect(preferences).toEqual(defaultPreferences);
|
||||
|
@@ -41,7 +41,7 @@ class PreferenceManager {
|
||||
|
||||
this.savePreferences = useDebounceFn(
|
||||
(preference: Preferences) => this._savePreferences(preference),
|
||||
100,
|
||||
150,
|
||||
);
|
||||
}
|
||||
|
||||
|
@@ -10,11 +10,12 @@ import type {
|
||||
LoginExpiredModeType,
|
||||
NavigationStyleType,
|
||||
PageTransitionType,
|
||||
SupportedLanguagesType,
|
||||
TabsStyleType,
|
||||
ThemeModeType,
|
||||
} from '@vben-core/typings';
|
||||
|
||||
type SupportedLanguagesType = 'en-US' | 'zh-CN';
|
||||
|
||||
interface AppPreferences {
|
||||
/** 权限模式 */
|
||||
accessMode: AccessModeType;
|
||||
@@ -44,6 +45,10 @@ interface AppPreferences {
|
||||
loginExpiredMode: LoginExpiredModeType;
|
||||
/** 应用名 */
|
||||
name: string;
|
||||
/**
|
||||
* @zh_CN 是否开启水印
|
||||
*/
|
||||
watermark: boolean;
|
||||
}
|
||||
|
||||
interface BreadcrumbPreferences {
|
||||
@@ -149,6 +154,12 @@ interface TabbarPreferences {
|
||||
persist: boolean;
|
||||
/** 是否开启多标签页图标 */
|
||||
showIcon: boolean;
|
||||
/** 显示最大化按钮 */
|
||||
showMaximize: boolean;
|
||||
/** 显示更多按钮 */
|
||||
showMore: boolean;
|
||||
/** 显示刷新按钮 */
|
||||
showRefresh: boolean;
|
||||
/** 标签页风格 */
|
||||
styleType: TabsStyleType;
|
||||
}
|
||||
@@ -184,8 +195,6 @@ interface TransitionPreferences {
|
||||
}
|
||||
|
||||
interface WidgetPreferences {
|
||||
/** 是否开启vben助手部件 */
|
||||
aiAssistant: boolean;
|
||||
/** 是否启用全屏部件 */
|
||||
fullscreen: boolean;
|
||||
/** 是否启用全局搜索部件 */
|
||||
@@ -249,6 +258,7 @@ export type {
|
||||
PreferencesKeys,
|
||||
ShortcutKeyPreferences,
|
||||
SidebarPreferences,
|
||||
SupportedLanguagesType,
|
||||
TabbarPreferences,
|
||||
ThemePreferences,
|
||||
TransitionPreferences,
|
||||
|
@@ -5,7 +5,7 @@ import {
|
||||
generatorColorVariables,
|
||||
} from '@vben-core/toolkit';
|
||||
|
||||
import { BUILT_IN_THEME_PRESETS } from './constants';
|
||||
import { BUILT_IN_THEME_PRESETS, type BuiltinThemePreset } from './constants';
|
||||
|
||||
/**
|
||||
* 更新主题的 CSS 变量以及其他 CSS 变量
|
||||
@@ -37,9 +37,13 @@ function updateCSSVariables(preferences: Preferences) {
|
||||
}
|
||||
|
||||
// 获取当前的内置主题
|
||||
const currentBuiltType = BUILT_IN_THEME_PRESETS.find(
|
||||
(item) => item.type === builtinType,
|
||||
);
|
||||
const currentBuiltType = [
|
||||
{
|
||||
color: preferences.theme.colorPrimary,
|
||||
type: 'default',
|
||||
} as BuiltinThemePreset,
|
||||
...BUILT_IN_THEME_PRESETS,
|
||||
].find((item) => item.type === builtinType);
|
||||
|
||||
let builtinTypeColorPrimary: string | undefined = '';
|
||||
|
||||
|
@@ -12,7 +12,7 @@ const VBEN_DOC_URL = 'https://doc.vben.pro';
|
||||
* @zh_CN Vben Logo
|
||||
*/
|
||||
const VBEN_LOGO_URL =
|
||||
'https://cdn.jsdelivr.net/npm/@vbenjs/static-source@0.1.3/source/logo-v1.webp';
|
||||
'https://unpkg.com/@vbenjs/static-source@0.1.5/source/logo-v1.webp';
|
||||
|
||||
/**
|
||||
* @zh_CN Vben Admin 首页地址
|
||||
|
@@ -85,17 +85,21 @@
|
||||
|
||||
/* 只有非mac下才进行调整,mac下使用默认滚动条 */
|
||||
html:not([data-platform='macOs']) {
|
||||
*::-webkit-scrollbar {
|
||||
::-webkit-scrollbar {
|
||||
@apply h-[1px] w-[10px];
|
||||
}
|
||||
|
||||
*::-webkit-scrollbar-thumb {
|
||||
::-webkit-scrollbar-thumb {
|
||||
@apply bg-border rounded-sm border-none;
|
||||
}
|
||||
|
||||
*::-webkit-scrollbar-track {
|
||||
::-webkit-scrollbar-track {
|
||||
@apply rounded-sm border-none bg-transparent shadow-none;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-button {
|
||||
@apply hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -35,7 +35,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@iconify/vue": "^4.1.2",
|
||||
"lucide-vue-next": "^0.414.0",
|
||||
"vue": "^3.4.33"
|
||||
"lucide-vue-next": "^0.416.0",
|
||||
"vue": "^3.4.34"
|
||||
}
|
||||
}
|
||||
|
@@ -36,7 +36,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@ctrl/tinycolor": "^4.1.0",
|
||||
"@vue/shared": "^3.4.33",
|
||||
"@vue/shared": "^3.4.34",
|
||||
"clsx": "^2.1.1",
|
||||
"defu": "^6.1.4",
|
||||
"lodash.clonedeep": "^4.5.0",
|
||||
|
@@ -38,7 +38,7 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"vue": "^3.4.33",
|
||||
"vue": "^3.4.34",
|
||||
"vue-router": "^4.4.0"
|
||||
}
|
||||
}
|
||||
|
42
packages/@core/shared/typings/src/app.d.ts
vendored
42
packages/@core/shared/typings/src/app.d.ts
vendored
@@ -1,5 +1,3 @@
|
||||
type SupportedLanguagesType = 'en-US' | 'zh-CN';
|
||||
|
||||
type LayoutType =
|
||||
| 'full-content'
|
||||
| 'header-nav'
|
||||
@@ -26,7 +24,8 @@ type BuiltinThemeType =
|
||||
| 'stone'
|
||||
| 'violet'
|
||||
| 'yellow'
|
||||
| 'zinc';
|
||||
| 'zinc'
|
||||
| (Record<never, never> & string);
|
||||
|
||||
type ContentCompactType = 'compact' | 'wide';
|
||||
|
||||
@@ -34,20 +33,52 @@ type LayoutHeaderModeType = 'auto' | 'auto-scroll' | 'fixed' | 'static';
|
||||
|
||||
/**
|
||||
* 登录过期模式
|
||||
* 'modal' 弹窗模式 | 'page' 页面模式
|
||||
* modal 弹窗模式
|
||||
* page 页面模式
|
||||
*/
|
||||
type LoginExpiredModeType = 'modal' | 'page';
|
||||
|
||||
/**
|
||||
* 面包屑样式
|
||||
* background 背景
|
||||
* normal 默认
|
||||
*/
|
||||
type BreadcrumbStyleType = 'background' | 'normal';
|
||||
|
||||
type AccessModeType = 'allow-all' | 'backend' | 'frontend';
|
||||
/**
|
||||
* 权限模式
|
||||
* backend 后端权限模式
|
||||
* frontend 前端权限模式
|
||||
*/
|
||||
type AccessModeType = 'backend' | 'frontend';
|
||||
|
||||
/**
|
||||
* 导航风格
|
||||
* plain 朴素
|
||||
* rounded 圆润
|
||||
*/
|
||||
type NavigationStyleType = 'plain' | 'rounded';
|
||||
|
||||
/**
|
||||
* 标签栏风格
|
||||
* brisk 轻快
|
||||
* card 卡片
|
||||
* chrome 谷歌
|
||||
* plain 朴素
|
||||
*/
|
||||
type TabsStyleType = 'brisk' | 'card' | 'chrome' | 'plain';
|
||||
|
||||
/**
|
||||
* 页面切换动画
|
||||
*/
|
||||
type PageTransitionType = 'fade' | 'fade-down' | 'fade-slide' | 'fade-up';
|
||||
|
||||
/**
|
||||
* 页面切换动画
|
||||
* panel-center 居中布局
|
||||
* panel-left 居左布局
|
||||
* panel-right 居右布局
|
||||
*/
|
||||
type AuthPageLayoutType = 'panel-center' | 'panel-left' | 'panel-right';
|
||||
|
||||
export type {
|
||||
@@ -61,7 +92,6 @@ export type {
|
||||
LoginExpiredModeType,
|
||||
NavigationStyleType,
|
||||
PageTransitionType,
|
||||
SupportedLanguagesType,
|
||||
TabsStyleType,
|
||||
ThemeModeType,
|
||||
};
|
||||
|
@@ -28,6 +28,10 @@ interface MenuRecordBadgeRaw {
|
||||
* 菜单原始对象
|
||||
*/
|
||||
interface MenuRecordRaw extends MenuRecordBadgeRaw {
|
||||
/**
|
||||
* 激活时的图标名
|
||||
*/
|
||||
activeIcon?: string;
|
||||
/**
|
||||
* 子菜单
|
||||
*/
|
||||
|
@@ -3,6 +3,10 @@ import type { Router, RouteRecordRaw } from 'vue-router';
|
||||
import type { Component } from 'vue';
|
||||
|
||||
interface RouteMeta {
|
||||
/**
|
||||
* 激活图标(菜单/tab)
|
||||
*/
|
||||
activeIcon?: string;
|
||||
/**
|
||||
* 当前激活的菜单,有时候不想激活现有菜单,需要激活父级菜单时使用
|
||||
* @default false
|
||||
@@ -13,6 +17,11 @@ interface RouteMeta {
|
||||
* @default false
|
||||
*/
|
||||
affixTab?: boolean;
|
||||
/**
|
||||
* 固定标签页的顺序
|
||||
* @default 0
|
||||
*/
|
||||
affixTabOrder?: number;
|
||||
/**
|
||||
* 需要特定的角色标识才可以访问
|
||||
* @default []
|
||||
@@ -56,10 +65,6 @@ interface RouteMeta {
|
||||
* @default false
|
||||
*/
|
||||
hideInTab?: boolean;
|
||||
/**
|
||||
* 路由跳转地址
|
||||
*/
|
||||
href?: string;
|
||||
/**
|
||||
* 图标(菜单/tab)
|
||||
*/
|
||||
@@ -87,7 +92,7 @@ interface RouteMeta {
|
||||
loaded?: boolean;
|
||||
/**
|
||||
* 标签页最大打开数量
|
||||
* @default false
|
||||
* @default -1
|
||||
*/
|
||||
maxNumOfOpenTab?: number;
|
||||
/**
|
||||
@@ -126,5 +131,6 @@ export type {
|
||||
ComponentRecordType,
|
||||
GenerateMenuAndRoutesOptions,
|
||||
RouteMeta,
|
||||
RouteRecordRaw,
|
||||
RouteRecordStringComponent,
|
||||
};
|
||||
|
@@ -42,6 +42,6 @@
|
||||
"@vben-core/shadcn-ui": "workspace:*",
|
||||
"@vben-core/typings": "workspace:*",
|
||||
"@vueuse/core": "^10.11.0",
|
||||
"vue": "^3.4.33"
|
||||
"vue": "^3.4.34"
|
||||
}
|
||||
}
|
||||
|
@@ -43,6 +43,6 @@
|
||||
"@vben-core/toolkit": "workspace:*",
|
||||
"@vben-core/typings": "workspace:*",
|
||||
"@vueuse/core": "^10.11.0",
|
||||
"vue": "^3.4.33"
|
||||
"vue": "^3.4.34"
|
||||
}
|
||||
}
|
||||
|
@@ -26,6 +26,10 @@ const subMenu = useSubMenuContext();
|
||||
const { parentMenu, parentPaths } = useMenu();
|
||||
|
||||
const active = computed(() => props.path === rootMenu?.activePath);
|
||||
const menuIcon = computed(() =>
|
||||
active.value ? props.activeIcon || props.icon : props.icon,
|
||||
);
|
||||
|
||||
const isTopLevelMenuItem = computed(
|
||||
() => parentMenu.value?.type.name === 'Menu',
|
||||
);
|
||||
@@ -94,7 +98,7 @@ onBeforeUnmount(() => {
|
||||
>
|
||||
<template #trigger>
|
||||
<div :class="[nsMenu.be('tooltip', 'trigger')]">
|
||||
<VbenIcon :class="nsMenu.e('icon')" :icon="icon" fallback />
|
||||
<VbenIcon :class="nsMenu.e('icon')" :icon="menuIcon" fallback />
|
||||
<slot></slot>
|
||||
<span v-if="collapseShowTitle" :class="nsMenu.e('name')">
|
||||
<slot name="title"></slot>
|
||||
@@ -109,7 +113,7 @@ onBeforeUnmount(() => {
|
||||
class="right-2"
|
||||
v-bind="props"
|
||||
/>
|
||||
<VbenIcon :class="nsMenu.e('icon')" :icon="icon" fallback />
|
||||
<VbenIcon :class="nsMenu.e('icon')" :icon="menuIcon" fallback />
|
||||
<slot></slot>
|
||||
<slot name="title"></slot>
|
||||
</div>
|
||||
|
@@ -12,7 +12,7 @@ defineOptions({
|
||||
name: 'NormalMenu',
|
||||
});
|
||||
|
||||
withDefaults(defineProps<Props>(), {
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
activePath: '',
|
||||
collapse: false,
|
||||
menus: () => [],
|
||||
@@ -25,6 +25,12 @@ const emit = defineEmits<{
|
||||
}>();
|
||||
|
||||
const { b, e, is } = useNamespace('normal-menu');
|
||||
|
||||
function menuIcon(menu: MenuRecordRaw) {
|
||||
return props.activePath === menu.path
|
||||
? menu.activeIcon || menu.icon
|
||||
: menu.icon;
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -44,7 +50,8 @@ const { b, e, is } = useNamespace('normal-menu');
|
||||
@click="() => emit('select', menu)"
|
||||
@mouseenter="() => emit('enter', menu)"
|
||||
>
|
||||
<VbenIcon :class="e('icon')" :icon="menu.icon" fallback />
|
||||
<VbenIcon :class="e('icon')" :icon="menuIcon(menu)" fallback />
|
||||
|
||||
<span :class="e('name')" class="truncate"> {{ menu.name }}</span>
|
||||
</li>
|
||||
</template>
|
||||
|
@@ -172,6 +172,10 @@ function handleMouseleave(deepDispatch = false) {
|
||||
}
|
||||
}
|
||||
|
||||
const menuIcon = computed(() =>
|
||||
active.value ? props.activeIcon || props.icon : props.icon,
|
||||
);
|
||||
|
||||
const item = reactive({
|
||||
active,
|
||||
parentPaths,
|
||||
@@ -215,7 +219,7 @@ onBeforeUnmount(() => {
|
||||
<template #trigger>
|
||||
<SubMenuContent
|
||||
:class="is('active', active)"
|
||||
:icon="icon"
|
||||
:icon="menuIcon"
|
||||
:is-menu-more="isSubMenuMore"
|
||||
:is-top-level-menu-submenu="isTopLevelMenuSubmenu"
|
||||
:level="currentLevel"
|
||||
@@ -246,7 +250,7 @@ onBeforeUnmount(() => {
|
||||
<template v-else>
|
||||
<SubMenuContent
|
||||
:class="is('active', active)"
|
||||
:icon="icon"
|
||||
:icon="menuIcon"
|
||||
:is-menu-more="isSubMenuMore"
|
||||
:is-top-level-menu-submenu="isTopLevelMenuSubmenu"
|
||||
:level="currentLevel"
|
||||
|
@@ -50,6 +50,10 @@ interface MenuProps {
|
||||
}
|
||||
|
||||
interface SubMenuProps extends MenuRecordBadgeRaw {
|
||||
/**
|
||||
* @zh_CN 激活图标
|
||||
*/
|
||||
activeIcon?: string;
|
||||
/**
|
||||
* @zh_CN 是否禁用
|
||||
*/
|
||||
@@ -65,6 +69,10 @@ interface SubMenuProps extends MenuRecordBadgeRaw {
|
||||
}
|
||||
|
||||
interface MenuItemProps extends MenuRecordBadgeRaw {
|
||||
/**
|
||||
* @zh_CN 图标
|
||||
*/
|
||||
activeIcon?: string;
|
||||
/**
|
||||
* @zh_CN 是否禁用
|
||||
*/
|
||||
|
@@ -31,12 +31,19 @@ const hasChildren = computed(() => {
|
||||
Reflect.has(menu, 'children') && !!menu.children && menu.children.length > 0
|
||||
);
|
||||
});
|
||||
|
||||
// function menuIcon(menu: MenuRecordRaw) {
|
||||
// return props.activePath === menu.path
|
||||
// ? menu.activeIcon || menu.icon
|
||||
// : menu.icon;
|
||||
// }
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<MenuItem
|
||||
v-if="!hasChildren"
|
||||
:key="menu.path"
|
||||
:active-icon="menu.activeIcon"
|
||||
:badge="menu.badge"
|
||||
:badge-type="menu.badgeType"
|
||||
:badge-variants="menu.badgeVariants"
|
||||
@@ -48,6 +55,7 @@ const hasChildren = computed(() => {
|
||||
<SubMenuComp
|
||||
v-else
|
||||
:key="`${menu.path}_sub`"
|
||||
:active-icon="menu.activeIcon"
|
||||
:icon="menu.icon"
|
||||
:path="menu.path"
|
||||
>
|
||||
|
@@ -48,8 +48,8 @@
|
||||
"@vben-core/typings": "workspace:*",
|
||||
"@vueuse/core": "^10.11.0",
|
||||
"class-variance-authority": "^0.7.0",
|
||||
"lucide-vue-next": "^0.414.0",
|
||||
"lucide-vue-next": "^0.416.0",
|
||||
"radix-vue": "^1.9.2",
|
||||
"vue": "^3.4.33"
|
||||
"vue": "^3.4.34"
|
||||
}
|
||||
}
|
||||
|
@@ -32,14 +32,14 @@ function handleItemClick(menu: IDropdownMenuItem) {
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="start">
|
||||
<DropdownMenuGroup>
|
||||
<template v-for="menu in menus" :key="menu.key">
|
||||
<template v-for="menu in menus" :key="menu.value">
|
||||
<DropdownMenuItem
|
||||
:disabled="menu.disabled"
|
||||
class="data-[state=checked]:bg-accent data-[state=checked]:text-accent-foreground text-foreground/80 mb-1 cursor-pointer"
|
||||
@click="handleItemClick(menu)"
|
||||
>
|
||||
<component :is="menu.icon" v-if="menu.icon" class="mr-2 size-4" />
|
||||
{{ menu.text }}
|
||||
{{ menu.label }}
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator v-if="menu.separator" class="bg-border" />
|
||||
</template>
|
||||
|
@@ -30,18 +30,20 @@ function handleItemClick(value: string) {
|
||||
<template v-for="menu in menus" :key="menu.key">
|
||||
<DropdownMenuItem
|
||||
:class="
|
||||
menu.key === modelValue ? 'bg-accent text-accent-foreground' : ''
|
||||
menu.value === modelValue
|
||||
? 'bg-accent text-accent-foreground'
|
||||
: ''
|
||||
"
|
||||
class="data-[state=checked]:bg-accent data-[state=checked]:text-accent-foreground text-foreground/80 mb-1 cursor-pointer"
|
||||
@click="handleItemClick(menu.key)"
|
||||
@click="handleItemClick(menu.value)"
|
||||
>
|
||||
<component :is="menu.icon" v-if="menu.icon" class="mr-2 size-4" />
|
||||
<span
|
||||
v-if="!menu.icon"
|
||||
:class="menu.key === modelValue ? 'bg-foreground' : ''"
|
||||
:class="menu.value === modelValue ? 'bg-foreground' : ''"
|
||||
class="mr-2 size-1.5 rounded-full"
|
||||
></span>
|
||||
{{ menu.text }}
|
||||
{{ menu.label }}
|
||||
</DropdownMenuItem>
|
||||
</template>
|
||||
</DropdownMenuGroup>
|
||||
|
@@ -12,17 +12,17 @@ interface VbenDropdownMenuItem {
|
||||
*/
|
||||
icon?: Component;
|
||||
/**
|
||||
* @zh_CN 唯一标识
|
||||
* @zh_CN 标题
|
||||
*/
|
||||
key: string;
|
||||
label: string;
|
||||
/**
|
||||
* @zh_CN 是否是分割线
|
||||
*/
|
||||
separator?: boolean;
|
||||
/**
|
||||
* @zh_CN 标题
|
||||
* @zh_CN 唯一标识
|
||||
*/
|
||||
text: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
interface DropdownMenuProps {
|
||||
|
@@ -41,6 +41,6 @@
|
||||
"@vben-core/icons": "workspace:*",
|
||||
"@vben-core/shadcn-ui": "workspace:*",
|
||||
"@vben-core/typings": "workspace:*",
|
||||
"vue": "^3.4.33"
|
||||
"vue": "^3.4.34"
|
||||
}
|
||||
}
|
||||
|
@@ -1,2 +1,3 @@
|
||||
export { default as TabsToolMore } from './tool-more.vue';
|
||||
export { default as TabsToolRefresh } from './tool-refresh.vue';
|
||||
export { default as TabsToolScreen } from './tool-screen.vue';
|
||||
|
@@ -0,0 +1,31 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { RotateCw } from '@vben-core/icons';
|
||||
|
||||
const emit = defineEmits<{ refresh: [] }>();
|
||||
|
||||
const loading = ref(false);
|
||||
function handleClick() {
|
||||
loading.value = true;
|
||||
|
||||
setTimeout(() => {
|
||||
loading.value = false;
|
||||
}, 1000);
|
||||
emit('refresh');
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="flex-center hover:bg-muted hover:text-foreground text-muted-foreground border-border h-full cursor-pointer border-l px-[9px] text-lg font-semibold"
|
||||
@click="handleClick"
|
||||
>
|
||||
<RotateCw
|
||||
:class="{
|
||||
'animate-spin duration-1000': loading,
|
||||
}"
|
||||
class="size-4"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
Reference in New Issue
Block a user