chore: update common-ui to universal-ui
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
<script setup lang="ts">
|
||||
interface Props {
|
||||
title?: string;
|
||||
}
|
||||
|
||||
defineOptions({
|
||||
name: 'PreferenceBlock',
|
||||
});
|
||||
|
||||
withDefaults(defineProps<Props>(), {
|
||||
title: '',
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col py-4">
|
||||
<h3 class="mb-3 font-semibold leading-none tracking-tight">
|
||||
{{ title }}
|
||||
</h3>
|
||||
<slot></slot>
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,47 @@
|
||||
<script setup lang="ts">
|
||||
import { $t } from '@vben/locales';
|
||||
|
||||
import SwitchItem from '../switch-item.vue';
|
||||
|
||||
defineOptions({
|
||||
name: 'PreferenceAnimation',
|
||||
});
|
||||
const transitionProgress = defineModel<boolean>('transitionProgress', {
|
||||
// 默认值
|
||||
default: false,
|
||||
});
|
||||
|
||||
const transitionName = defineModel<string>('transitionName');
|
||||
const transitionEnable = defineModel<boolean>('transitionEnable');
|
||||
|
||||
const transitionPreset = ['fade', 'fade-slide', 'fade-up', 'fade-down'];
|
||||
|
||||
function handleClick(value: string) {
|
||||
transitionName.value = value;
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<SwitchItem v-model="transitionProgress">
|
||||
{{ $t('preferences.page-progress') }}
|
||||
</SwitchItem>
|
||||
<SwitchItem v-model="transitionEnable">
|
||||
{{ $t('preferences.page-transition') }}
|
||||
</SwitchItem>
|
||||
<div
|
||||
v-if="transitionEnable"
|
||||
class="mb-2 mt-3 flex justify-between gap-3 px-2"
|
||||
>
|
||||
<div
|
||||
v-for="item in transitionPreset"
|
||||
:key="item"
|
||||
:class="{
|
||||
'outline-box-active': transitionName === item,
|
||||
}"
|
||||
class="outline-box p-2"
|
||||
@click="handleClick(item)"
|
||||
>
|
||||
<div :class="`${item}-slow`" class="bg-accent h-10 w-12 rounded-md"></div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,34 @@
|
||||
<script setup lang="ts">
|
||||
import type { SelectListItem } from '@vben/types';
|
||||
|
||||
import { $t } from '@vben/locales';
|
||||
import { SUPPORT_LANGUAGES } from '@vben-core/preferences';
|
||||
|
||||
import SelectItem from '../select-item.vue';
|
||||
import SwitchItem from '../switch-item.vue';
|
||||
|
||||
defineOptions({
|
||||
name: 'PreferenceGeneralConfig',
|
||||
});
|
||||
|
||||
const appLocale = defineModel<string>('appLocale');
|
||||
const appDynamicTitle = defineModel<boolean>('appDynamicTitle');
|
||||
const appAiAssistant = defineModel<boolean>('appAiAssistant');
|
||||
|
||||
const localeItems: SelectListItem[] = SUPPORT_LANGUAGES.map((item) => ({
|
||||
label: item.text,
|
||||
value: item.key,
|
||||
}));
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<SelectItem v-model="appLocale" :items="localeItems">
|
||||
{{ $t('preferences.language') }}
|
||||
</SelectItem>
|
||||
<SwitchItem v-model="appDynamicTitle">
|
||||
{{ $t('preferences.dynamic-title') }}
|
||||
</SwitchItem>
|
||||
<SwitchItem v-model="appAiAssistant">
|
||||
{{ $t('preferences.ai-assistant') }}
|
||||
</SwitchItem>
|
||||
</template>
|
||||
@@ -0,0 +1,16 @@
|
||||
export { default as Block } from './block.vue';
|
||||
export { default as Animation } from './general/animation.vue';
|
||||
export { default as General } from './general/general.vue';
|
||||
export { default as Breadcrumb } from './layout/breadcrumb.vue';
|
||||
export { default as Content } from './layout/content.vue';
|
||||
export { default as Footer } from './layout/footer.vue';
|
||||
export { default as Header } from './layout/header.vue';
|
||||
export { default as Layout } from './layout/layout.vue';
|
||||
export { default as Navigation } from './layout/navigation.vue';
|
||||
export { default as Sidebar } from './layout/sidebar.vue';
|
||||
export { default as Tabbar } from './layout/tabbar.vue';
|
||||
export { default as GlobalShortcutKeys } from './shortcut-keys/global.vue';
|
||||
export { default as SwitchItem } from './switch-item.vue';
|
||||
export { default as ThemeColor } from './theme/color.vue';
|
||||
export { default as ColorMode } from './theme/color-mode.vue';
|
||||
export { default as Theme } from './theme/theme.vue';
|
||||
@@ -0,0 +1,53 @@
|
||||
<script setup lang="ts">
|
||||
import type { SelectListItem } from '@vben/types';
|
||||
|
||||
import { computed } from 'vue';
|
||||
|
||||
import { $t } from '@vben/locales';
|
||||
|
||||
import SwitchItem from '../switch-item.vue';
|
||||
import ToggleItem from '../toggle-item.vue';
|
||||
|
||||
defineOptions({
|
||||
name: 'PreferenceBreadcrumbConfig',
|
||||
});
|
||||
|
||||
const props = defineProps<{ disabled?: boolean }>();
|
||||
|
||||
const breadcrumbEnable = defineModel<boolean>('breadcrumbEnable');
|
||||
const breadcrumbShowIcon = defineModel<boolean>('breadcrumbShowIcon');
|
||||
const breadcrumbStyleType = defineModel<string>('breadcrumbStyleType');
|
||||
const breadcrumbShowHome = defineModel<boolean>('breadcrumbShowHome');
|
||||
const breadcrumbHideOnlyOne = defineModel<boolean>('breadcrumbHideOnlyOne');
|
||||
|
||||
const typeItems: SelectListItem[] = [
|
||||
{ label: $t('preferences.normal'), value: 'normal' },
|
||||
{ label: $t('preferences.breadcrumb-background'), value: 'background' },
|
||||
];
|
||||
|
||||
const disableItem = computed(() => {
|
||||
return !breadcrumbEnable.value || props.disabled;
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<SwitchItem v-model="breadcrumbEnable" :disabled="disabled">
|
||||
{{ $t('preferences.breadcrumb-enable') }}
|
||||
</SwitchItem>
|
||||
<SwitchItem v-model="breadcrumbHideOnlyOne" :disabled="disableItem">
|
||||
{{ $t('preferences.breadcrumb-hide-only-one') }}
|
||||
</SwitchItem>
|
||||
<SwitchItem v-model="breadcrumbShowHome" :disabled="disableItem">
|
||||
{{ $t('preferences.breadcrumb-home') }}
|
||||
</SwitchItem>
|
||||
<SwitchItem v-model="breadcrumbShowIcon" :disabled="disableItem">
|
||||
{{ $t('preferences.breadcrumb-icon') }}
|
||||
</SwitchItem>
|
||||
<ToggleItem
|
||||
v-model="breadcrumbStyleType"
|
||||
:disabled="disableItem"
|
||||
:items="typeItems"
|
||||
>
|
||||
{{ $t('preferences.breadcrumb-style') }}
|
||||
</ToggleItem>
|
||||
</template>
|
||||
@@ -0,0 +1,51 @@
|
||||
<script setup lang="ts">
|
||||
import { type Component, computed } from 'vue';
|
||||
|
||||
import { $t } from '@vben/locales';
|
||||
|
||||
import { ContentCompact, ContentWide } from '../../icons';
|
||||
|
||||
defineOptions({
|
||||
name: 'PreferenceLayoutContent',
|
||||
});
|
||||
|
||||
const modelValue = defineModel<string>({ default: 'wide' });
|
||||
|
||||
const components: Record<string, Component> = {
|
||||
compact: ContentCompact,
|
||||
wide: ContentWide,
|
||||
};
|
||||
|
||||
const PRESET = computed(() => [
|
||||
{
|
||||
name: $t('preferences.wide'),
|
||||
type: 'wide',
|
||||
},
|
||||
{
|
||||
name: '定宽',
|
||||
type: 'compact',
|
||||
},
|
||||
]);
|
||||
|
||||
function activeClass(theme: string): string[] {
|
||||
return theme === modelValue.value ? ['outline-box-active'] : [];
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex w-full gap-5">
|
||||
<template v-for="theme in PRESET" :key="theme.name">
|
||||
<div
|
||||
class="flex w-[100px] cursor-pointer flex-col"
|
||||
@click="modelValue = theme.type"
|
||||
>
|
||||
<div :class="activeClass(theme.type)" class="outline-box flex-center">
|
||||
<component :is="components[theme.type]" />
|
||||
</div>
|
||||
<div class="text-muted-foreground mt-2 text-center text-xs">
|
||||
{{ theme.name }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,21 @@
|
||||
<script setup lang="ts">
|
||||
import { $t } from '@vben/locales';
|
||||
|
||||
import SwitchItem from '../switch-item.vue';
|
||||
|
||||
defineOptions({
|
||||
name: 'PreferenceBreadcrumbConfig',
|
||||
});
|
||||
|
||||
const footerEnable = defineModel<boolean>('footerEnable');
|
||||
const footerFixed = defineModel<boolean>('footerFixed');
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<SwitchItem v-model="footerEnable">
|
||||
{{ $t('preferences.footer-visible') }}
|
||||
</SwitchItem>
|
||||
<SwitchItem v-model="footerFixed" :disabled="!footerEnable">
|
||||
{{ $t('preferences.footer-fixed') }}
|
||||
</SwitchItem>
|
||||
</template>
|
||||
@@ -0,0 +1,49 @@
|
||||
<script setup lang="ts">
|
||||
import type { LayoutHeaderModeType, SelectListItem } from '@vben/types';
|
||||
|
||||
import { $t } from '@vben/locales';
|
||||
|
||||
import SelectItem from '../select-item.vue';
|
||||
import SwitchItem from '../switch-item.vue';
|
||||
|
||||
defineOptions({
|
||||
name: 'PreferenceBreadcrumbConfig',
|
||||
});
|
||||
|
||||
defineProps<{ disabled: boolean }>();
|
||||
|
||||
const headerEnable = defineModel<boolean>('headerEnable');
|
||||
const headerMode = defineModel<LayoutHeaderModeType>('headerMode');
|
||||
|
||||
const localeItems: SelectListItem[] = [
|
||||
{
|
||||
label: $t('preferences.header-mode-static'),
|
||||
value: 'static',
|
||||
},
|
||||
{
|
||||
label: $t('preferences.header-mode-fixed'),
|
||||
value: 'fixed',
|
||||
},
|
||||
{
|
||||
label: $t('preferences.header-mode-auto'),
|
||||
value: 'auto',
|
||||
},
|
||||
{
|
||||
label: $t('preferences.header-mode-auto-scroll'),
|
||||
value: 'auto-scroll',
|
||||
},
|
||||
];
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<SwitchItem v-model="headerEnable" :disabled="disabled">
|
||||
{{ $t('preferences.header-visible') }}
|
||||
</SwitchItem>
|
||||
<SelectItem
|
||||
v-model="headerMode"
|
||||
:disabled="!headerEnable"
|
||||
:items="localeItems"
|
||||
>
|
||||
{{ $t('preferences.mode') }}
|
||||
</SelectItem>
|
||||
</template>
|
||||
@@ -0,0 +1,21 @@
|
||||
<script setup lang="ts">
|
||||
import { $t } from '@vben/locales';
|
||||
|
||||
import SwitchItem from '../switch-item.vue';
|
||||
|
||||
defineOptions({
|
||||
name: 'PreferenceInterfaceControl',
|
||||
});
|
||||
|
||||
const tabsVisible = defineModel<boolean>('tabsVisible');
|
||||
const logoVisible = defineModel<boolean>('logoVisible');
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<SwitchItem v-model="tabsVisible">
|
||||
{{ $t('preferences.tabs-visible') }}
|
||||
</SwitchItem>
|
||||
<SwitchItem v-model="logoVisible">
|
||||
{{ $t('preferences.logo-visible') }}
|
||||
</SwitchItem>
|
||||
</template>
|
||||
@@ -0,0 +1,95 @@
|
||||
<script setup lang="ts">
|
||||
import type { LayoutType } from '@vben/types';
|
||||
|
||||
import { type Component, computed } from 'vue';
|
||||
|
||||
import { $t } from '@vben/locales';
|
||||
import { MdiQuestionMarkCircleOutline } from '@vben-core/iconify';
|
||||
import { VbenTooltip } from '@vben-core/shadcn-ui';
|
||||
|
||||
import {
|
||||
FullContent,
|
||||
HeaderNav,
|
||||
MixedNav,
|
||||
SidebarMixedNav,
|
||||
SidebarNav,
|
||||
} from '../../icons';
|
||||
|
||||
interface PresetItem {
|
||||
name: string;
|
||||
tip: string;
|
||||
type: LayoutType;
|
||||
}
|
||||
|
||||
defineOptions({
|
||||
name: 'PreferenceLayout',
|
||||
});
|
||||
|
||||
const modelValue = defineModel<LayoutType>({ default: 'sidebar-nav' });
|
||||
|
||||
const components: Record<LayoutType, Component> = {
|
||||
'full-content': FullContent,
|
||||
'header-nav': HeaderNav,
|
||||
'mixed-nav': MixedNav,
|
||||
'sidebar-mixed-nav': SidebarMixedNav,
|
||||
'sidebar-nav': SidebarNav,
|
||||
};
|
||||
|
||||
const PRESET = computed((): PresetItem[] => [
|
||||
{
|
||||
name: $t('preferences.vertical'),
|
||||
tip: $t('preferences.vertical-tip'),
|
||||
type: 'sidebar-nav',
|
||||
},
|
||||
{
|
||||
name: $t('preferences.two-column'),
|
||||
tip: $t('preferences.two-column-tip'),
|
||||
type: 'sidebar-mixed-nav',
|
||||
},
|
||||
{
|
||||
name: $t('preferences.horizontal'),
|
||||
tip: $t('preferences.vertical-tip'),
|
||||
type: 'header-nav',
|
||||
},
|
||||
{
|
||||
name: $t('preferences.mixed-menu'),
|
||||
tip: $t('preferences.mixed-menu-tip'),
|
||||
type: 'mixed-nav',
|
||||
},
|
||||
{
|
||||
name: $t('preferences.full-content'),
|
||||
tip: $t('preferences.full-content-tip'),
|
||||
type: 'full-content',
|
||||
},
|
||||
]);
|
||||
|
||||
function activeClass(theme: string): string[] {
|
||||
return theme === modelValue.value ? ['outline-box-active'] : [];
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex w-full flex-wrap gap-5">
|
||||
<template v-for="theme in PRESET" :key="theme.name">
|
||||
<div
|
||||
class="flex w-[100px] cursor-pointer flex-col"
|
||||
@click="modelValue = theme.type"
|
||||
>
|
||||
<div :class="activeClass(theme.type)" class="outline-box flex-center">
|
||||
<component :is="components[theme.type]" />
|
||||
</div>
|
||||
<div
|
||||
class="text-muted-foreground flex-center hover:text-foreground mt-2 text-center text-xs"
|
||||
>
|
||||
{{ theme.name }}
|
||||
<VbenTooltip v-if="theme.tip" side="bottom">
|
||||
<template #trigger>
|
||||
<MdiQuestionMarkCircleOutline class="ml-1 cursor-help" />
|
||||
</template>
|
||||
{{ theme.tip }}
|
||||
</VbenTooltip>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,45 @@
|
||||
<script setup lang="ts">
|
||||
import type { SelectListItem } from '@vben/types';
|
||||
|
||||
import { $t } from '@vben/locales';
|
||||
|
||||
import SwitchItem from '../switch-item.vue';
|
||||
import ToggleItem from '../toggle-item.vue';
|
||||
|
||||
defineOptions({
|
||||
name: 'PreferenceNavigationConfig',
|
||||
});
|
||||
|
||||
defineProps<{ disabled?: boolean; disabledNavigationSplit?: boolean }>();
|
||||
|
||||
const navigationStyleType = defineModel<string>('navigationStyleType');
|
||||
const navigationSplit = defineModel<boolean>('navigationSplit');
|
||||
const navigationAccordion = defineModel<boolean>('navigationAccordion');
|
||||
|
||||
const stylesItems: SelectListItem[] = [
|
||||
{ label: $t('preferences.rounded'), value: 'rounded' },
|
||||
{ label: $t('preferences.plain'), value: 'plain' },
|
||||
];
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ToggleItem
|
||||
v-model="navigationStyleType"
|
||||
:disabled="disabled"
|
||||
:items="stylesItems"
|
||||
>
|
||||
{{ $t('preferences.navigation-style') }}
|
||||
</ToggleItem>
|
||||
<SwitchItem
|
||||
v-model="navigationSplit"
|
||||
:disabled="disabledNavigationSplit || disabled"
|
||||
>
|
||||
{{ $t('preferences.navigation-split') }}
|
||||
<template #tip>
|
||||
{{ $t('preferences.navigation-split-tip') }}
|
||||
</template>
|
||||
</SwitchItem>
|
||||
<SwitchItem v-model="navigationAccordion" :disabled="disabled">
|
||||
{{ $t('preferences.navigation-accordion') }}
|
||||
</SwitchItem>
|
||||
</template>
|
||||
@@ -0,0 +1,32 @@
|
||||
<script setup lang="ts">
|
||||
import { $t } from '@vben/locales';
|
||||
|
||||
import SwitchItem from '../switch-item.vue';
|
||||
|
||||
defineOptions({
|
||||
name: 'PreferenceBreadcrumbConfig',
|
||||
});
|
||||
|
||||
defineProps<{ disabled: boolean }>();
|
||||
|
||||
const sidebarEnable = defineModel<boolean>('sidebarEnable');
|
||||
const sidebarCollapsedShowTitle = defineModel<boolean>(
|
||||
'sidebarCollapsedShowTitle',
|
||||
);
|
||||
const sidebarCollapsed = defineModel<boolean>('sidebarCollapsed');
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<SwitchItem v-model="sidebarEnable" :disabled="disabled">
|
||||
{{ $t('preferences.side-visible') }}
|
||||
</SwitchItem>
|
||||
<SwitchItem v-model="sidebarCollapsed" :disabled="!sidebarEnable || disabled">
|
||||
{{ $t('preferences.collapse') }}
|
||||
</SwitchItem>
|
||||
<SwitchItem
|
||||
v-model="sidebarCollapsedShowTitle"
|
||||
:disabled="!sidebarEnable || disabled"
|
||||
>
|
||||
{{ $t('preferences.collapse-show-title') }}
|
||||
</SwitchItem>
|
||||
</template>
|
||||
@@ -0,0 +1,26 @@
|
||||
<script setup lang="ts">
|
||||
import { $t } from '@vben/locales';
|
||||
|
||||
import SwitchItem from '../switch-item.vue';
|
||||
|
||||
defineOptions({
|
||||
name: 'PreferenceTabsConfig',
|
||||
});
|
||||
|
||||
defineProps<{ disabled?: boolean }>();
|
||||
|
||||
const tabbarEnable = defineModel<boolean>('tabbarEnable');
|
||||
const tabbarShowIcon = defineModel<boolean>('tabbarShowIcon');
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<SwitchItem v-model="tabbarEnable" :disabled="disabled">
|
||||
{{ $t('preferences.tabs-visible') }}
|
||||
</SwitchItem>
|
||||
<SwitchItem v-model="tabbarShowIcon" :disabled="!tabbarEnable">
|
||||
{{ $t('preferences.tabs-icon') }}
|
||||
</SwitchItem>
|
||||
<!-- <SwitchItem v-model="sideCollapseShowTitle" :disabled="!tabsVisible">
|
||||
{{ $t('preferences.collapse-show-title') }}
|
||||
</SwitchItem> -->
|
||||
</template>
|
||||
@@ -0,0 +1,67 @@
|
||||
<script setup lang="ts">
|
||||
import type { SelectListItem } from '@vben/types';
|
||||
|
||||
import { useSlots } from 'vue';
|
||||
|
||||
import { MdiQuestionMarkCircleOutline } from '@vben-core/iconify';
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
VbenTooltip,
|
||||
} from '@vben-core/shadcn-ui';
|
||||
|
||||
defineOptions({
|
||||
name: 'PreferenceSelectItem',
|
||||
});
|
||||
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
disabled?: boolean;
|
||||
items?: SelectListItem[];
|
||||
placeholder?: string;
|
||||
}>(),
|
||||
{
|
||||
disabled: false,
|
||||
placeholder: '',
|
||||
items: () => [],
|
||||
},
|
||||
);
|
||||
|
||||
const selectValue = defineModel<string>();
|
||||
|
||||
const slots = useSlots();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
:class="{
|
||||
'hover:bg-accent': !slots.tip,
|
||||
'pointer-events-none opacity-50': disabled,
|
||||
}"
|
||||
class="my-1 flex w-full items-center justify-between rounded-md px-2 py-1"
|
||||
>
|
||||
<span class="flex items-center text-sm">
|
||||
<slot></slot>
|
||||
|
||||
<VbenTooltip v-if="slots.tip" side="bottom">
|
||||
<template #trigger>
|
||||
<MdiQuestionMarkCircleOutline class="ml-1 cursor-help" />
|
||||
</template>
|
||||
<slot name="tip"></slot>
|
||||
</VbenTooltip>
|
||||
</span>
|
||||
<Select v-model="selectValue">
|
||||
<SelectTrigger class="h-7 w-[140px]">
|
||||
<SelectValue :placeholder="placeholder" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<template v-for="item in items" :key="item.value">
|
||||
<SelectItem :value="item.value"> {{ item.label }} </SelectItem>
|
||||
</template>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,42 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
|
||||
import { $t } from '@vben/locales';
|
||||
import { isWindowsOs } from '@vben-core/toolkit';
|
||||
|
||||
import SwitchItem from '../switch-item.vue';
|
||||
|
||||
defineOptions({
|
||||
name: 'PreferenceGeneralConfig',
|
||||
});
|
||||
|
||||
const shortcutKeysEnable = defineModel<boolean>('shortcutKeysEnable');
|
||||
const shortcutKeysGlobalSearch = defineModel<boolean>(
|
||||
'shortcutKeysGlobalSearch',
|
||||
);
|
||||
const shortcutKeysLogout = defineModel<boolean>('shortcutKeysLogout');
|
||||
const shortcutKeysPreferences = defineModel<boolean>('shortcutKeysPreferences');
|
||||
|
||||
const altView = computed(() => (isWindowsOs() ? 'Alt' : '⌥'));
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<SwitchItem v-model="shortcutKeysEnable">
|
||||
{{ $t('preferences.shortcut-keys.title') }}
|
||||
</SwitchItem>
|
||||
<SwitchItem v-if="shortcutKeysEnable" v-model="shortcutKeysGlobalSearch">
|
||||
{{ $t('preferences.shortcut-keys.search') }}
|
||||
<template #shortcut>
|
||||
{{ isWindowsOs() ? 'Ctrl' : '⌘' }}
|
||||
<kbd> K </kbd>
|
||||
</template>
|
||||
</SwitchItem>
|
||||
<SwitchItem v-if="shortcutKeysEnable" v-model="shortcutKeysLogout">
|
||||
{{ $t('preferences.shortcut-keys.logout') }}
|
||||
<template #shortcut> {{ altView }} Q </template>
|
||||
</SwitchItem>
|
||||
<SwitchItem v-if="shortcutKeysEnable" v-model="shortcutKeysPreferences">
|
||||
{{ $t('preferences.shortcut-keys.preferences') }}
|
||||
<template #shortcut> {{ altView }} , </template>
|
||||
</SwitchItem>
|
||||
</template>
|
||||
@@ -0,0 +1,47 @@
|
||||
<script setup lang="ts">
|
||||
import { useSlots } from 'vue';
|
||||
|
||||
import { MdiQuestionMarkCircleOutline } from '@vben-core/iconify';
|
||||
import { Switch, VbenTooltip } from '@vben-core/shadcn-ui';
|
||||
|
||||
defineOptions({
|
||||
name: 'PreferenceSwitchItem',
|
||||
});
|
||||
|
||||
withDefaults(defineProps<{ disabled?: boolean }>(), {
|
||||
disabled: false,
|
||||
});
|
||||
|
||||
const checked = defineModel<boolean>();
|
||||
|
||||
const slots = useSlots();
|
||||
|
||||
function handleClick() {
|
||||
checked.value = !checked.value;
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
:class="{
|
||||
'pointer-events-none opacity-50': disabled,
|
||||
}"
|
||||
class="hover:bg-accent my-1 flex w-full items-center justify-between rounded-md px-2 py-2"
|
||||
@click="handleClick"
|
||||
>
|
||||
<span class="flex items-center text-sm">
|
||||
<slot></slot>
|
||||
|
||||
<VbenTooltip v-if="slots.tip" side="bottom">
|
||||
<template #trigger>
|
||||
<MdiQuestionMarkCircleOutline class="ml-1 cursor-help" />
|
||||
</template>
|
||||
<slot name="tip"></slot>
|
||||
</VbenTooltip>
|
||||
</span>
|
||||
<span v-if="$slots.shortcut" class="ml-auto mr-2 text-xs opacity-60">
|
||||
<slot name="shortcut"></slot>
|
||||
</span>
|
||||
<Switch v-model:checked="checked" @click.stop />
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,26 @@
|
||||
<script setup lang="ts">
|
||||
import { $t } from '@vben/locales';
|
||||
|
||||
import SwitchItem from '../switch-item.vue';
|
||||
|
||||
defineOptions({
|
||||
name: 'PreferenceColorMode',
|
||||
});
|
||||
|
||||
const appColorWeakMode = defineModel<boolean>('appColorWeakMode', {
|
||||
default: false,
|
||||
});
|
||||
|
||||
const appColorGrayMode = defineModel<boolean>('appColorGrayMode', {
|
||||
default: false,
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<SwitchItem v-model="appColorWeakMode">
|
||||
{{ $t('preferences.weak-mode') }}
|
||||
</SwitchItem>
|
||||
<SwitchItem v-model="appColorGrayMode">
|
||||
{{ $t('preferences.gray-mode') }}
|
||||
</SwitchItem>
|
||||
</template>
|
||||
@@ -0,0 +1,90 @@
|
||||
<script setup lang="ts">
|
||||
import type { CSSProperties } from 'vue';
|
||||
import { computed, ref, watch, watchEffect } from 'vue';
|
||||
|
||||
import { MdiEditBoxOutline } from '@vben-core/iconify';
|
||||
import { TinyColor, convertToHsl } from '@vben-core/toolkit';
|
||||
|
||||
defineOptions({
|
||||
name: 'PreferenceColor',
|
||||
});
|
||||
|
||||
const props = withDefaults(defineProps<{ colorPrimaryPresets: string[] }>(), {
|
||||
colorPrimaryPresets: () => [],
|
||||
});
|
||||
|
||||
const colorInput = ref();
|
||||
const currentColor = ref(props.colorPrimaryPresets?.[0]);
|
||||
|
||||
const modelValue = defineModel<string>();
|
||||
|
||||
const activeColor = computed((): CSSProperties => {
|
||||
return {
|
||||
outlineColor: currentColor.value,
|
||||
outlineWidth: '2px',
|
||||
};
|
||||
});
|
||||
|
||||
function isActive(color: string): string[] {
|
||||
return color === currentColor.value ? ['outline-box-active'] : [];
|
||||
}
|
||||
|
||||
const inputStyle = computed((): CSSProperties => {
|
||||
return props.colorPrimaryPresets.includes(currentColor.value)
|
||||
? {}
|
||||
: activeColor.value;
|
||||
});
|
||||
|
||||
const inputValue = computed(() => {
|
||||
return new TinyColor(modelValue.value).toHexString();
|
||||
});
|
||||
|
||||
function selectColor() {
|
||||
colorInput.value.click();
|
||||
}
|
||||
|
||||
function handleInputChange(e: Event) {
|
||||
const target = e.target as HTMLInputElement;
|
||||
modelValue.value = convertToHsl(target.value);
|
||||
}
|
||||
|
||||
// 监听颜色变化,转成系统可识别的 hsl 格式
|
||||
watch(currentColor, (val) => {
|
||||
modelValue.value = convertToHsl(val);
|
||||
});
|
||||
|
||||
watchEffect(() => {
|
||||
if (modelValue.value) {
|
||||
currentColor.value = modelValue.value;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex w-full flex-wrap justify-between">
|
||||
<template v-for="color in colorPrimaryPresets" :key="color">
|
||||
<div
|
||||
:class="isActive(color)"
|
||||
class="outline-box p-2"
|
||||
@click="currentColor = color"
|
||||
>
|
||||
<div
|
||||
:style="{ backgroundColor: color }"
|
||||
class="h-6 w-6 rounded-md"
|
||||
></div>
|
||||
</div>
|
||||
</template>
|
||||
<div :style="inputStyle" class="outline-box p-2" @click="selectColor">
|
||||
<div class="flex-center bg-accent relative h-6 w-6 rounded-md">
|
||||
<MdiEditBoxOutline class="absolute z-10" />
|
||||
<input
|
||||
ref="colorInput"
|
||||
:value="inputValue"
|
||||
class="absolute inset-0 opacity-0"
|
||||
type="color"
|
||||
@input="handleInputChange"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,81 @@
|
||||
<script setup lang="ts">
|
||||
import { $t } from '@vben/locales';
|
||||
import {
|
||||
IcRoundMotionPhotosAuto,
|
||||
IcRoundWbSunny,
|
||||
MdiMoonAndStars,
|
||||
} from '@vben-core/iconify';
|
||||
|
||||
import SwitchItem from '../switch-item.vue';
|
||||
|
||||
defineOptions({
|
||||
name: 'PreferenceTheme',
|
||||
});
|
||||
|
||||
const modelValue = defineModel<string>({ default: 'auto' });
|
||||
const appSemiDarkMenu = defineModel<boolean>('appSemiDarkMenu', {
|
||||
default: true,
|
||||
});
|
||||
|
||||
const THEME_PRESET = [
|
||||
{
|
||||
icon: IcRoundWbSunny,
|
||||
name: 'light',
|
||||
},
|
||||
{
|
||||
icon: MdiMoonAndStars,
|
||||
name: 'dark',
|
||||
},
|
||||
{
|
||||
icon: IcRoundMotionPhotosAuto,
|
||||
name: 'auto',
|
||||
},
|
||||
];
|
||||
|
||||
function activeClass(theme: string): string[] {
|
||||
return theme === modelValue.value ? ['outline-box-active'] : [];
|
||||
}
|
||||
|
||||
function nameView(name: string) {
|
||||
switch (name) {
|
||||
case 'light': {
|
||||
return $t('preferences.light');
|
||||
}
|
||||
case 'dark': {
|
||||
return $t('preferences.dark');
|
||||
}
|
||||
case 'auto': {
|
||||
return $t('preferences.follow-system');
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex w-full flex-wrap justify-between">
|
||||
<template v-for="theme in THEME_PRESET" :key="theme.name">
|
||||
<div
|
||||
class="flex cursor-pointer flex-col"
|
||||
@click="modelValue = theme.name"
|
||||
>
|
||||
<div
|
||||
:class="activeClass(theme.name)"
|
||||
class="outline-box flex-center py-4"
|
||||
>
|
||||
<component :is="theme.icon" class="mx-9 size-5" />
|
||||
</div>
|
||||
<div class="text-muted-foreground mt-2 text-center text-xs">
|
||||
{{ nameView(theme.name) }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<SwitchItem
|
||||
v-model="appSemiDarkMenu"
|
||||
:disabled="modelValue !== 'light'"
|
||||
class="mt-6"
|
||||
>
|
||||
{{ $t('preferences.dark-menu') }}
|
||||
</SwitchItem>
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,44 @@
|
||||
<script setup lang="ts">
|
||||
import type { SelectListItem } from '@vben/types';
|
||||
|
||||
import { ToggleGroup, ToggleGroupItem } from '@vben-core/shadcn-ui';
|
||||
|
||||
defineOptions({
|
||||
name: 'PreferenceToggleItem',
|
||||
});
|
||||
|
||||
withDefaults(defineProps<{ disabled?: boolean; items: SelectListItem[] }>(), {
|
||||
disabled: false,
|
||||
items: () => [],
|
||||
});
|
||||
|
||||
const modelValue = defineModel<string>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
:class="{
|
||||
'pointer-events-none opacity-50': disabled,
|
||||
}"
|
||||
class="hover:bg-accent flex w-full items-center justify-between rounded-md px-2 py-2"
|
||||
disabled
|
||||
>
|
||||
<span class="text-sm"><slot></slot></span>
|
||||
<ToggleGroup
|
||||
v-model="modelValue"
|
||||
class="gap-2"
|
||||
size="sm"
|
||||
type="single"
|
||||
variant="outline"
|
||||
>
|
||||
<template v-for="item in items" :key="item.value">
|
||||
<ToggleGroupItem
|
||||
:value="item.value"
|
||||
class="data-[state=on]:bg-primary data-[state=on]:text-primary-foreground h-7 rounded-sm"
|
||||
>
|
||||
{{ item.label }}
|
||||
</ToggleGroupItem>
|
||||
</template>
|
||||
</ToggleGroup>
|
||||
</div>
|
||||
</template>
|
||||
Reference in New Issue
Block a user