refactor: refacotr preference

This commit is contained in:
vben
2024-06-01 23:15:29 +08:00
parent f7b97e8a83
commit fed47f5e05
139 changed files with 2205 additions and 1450 deletions

View File

@@ -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>

View File

@@ -0,0 +1,47 @@
<script setup lang="ts">
import { $t } from '@vben/locales';
import SwitchItem from '../switch-item.vue';
defineOptions({
name: 'PreferenceAnimation',
});
const pageProgress = defineModel<boolean>('pageProgress', {
// 默认值
default: false,
});
const pageTransition = defineModel<string>('pageTransition');
const pageTransitionEnable = defineModel<boolean>('pageTransitionEnable');
const transitionPreset = ['fade', 'fade-slide', 'fade-up', 'fade-down'];
function handleClick(value: string) {
pageTransition.value = value;
}
</script>
<template>
<SwitchItem v-model="pageProgress">
{{ $t('preference.page-progress') }}
</SwitchItem>
<SwitchItem v-model="pageTransitionEnable">
{{ $t('preference.page-transition') }}
</SwitchItem>
<div
v-if="pageTransitionEnable"
class="mb-2 mt-3 flex justify-between gap-3 px-2"
>
<div
v-for="item in transitionPreset"
:key="item"
class="outline-box p-2"
:class="{
'outline-box-active': pageTransition === item,
}"
@click="handleClick(item)"
>
<div class="bg-accent h-10 w-12 rounded-md" :class="`${item}-slow`"></div>
</div>
</div>
</template>

View File

@@ -0,0 +1,35 @@
<script setup lang="ts">
import type { SelectListItem } from '@vben/types';
import { SUPPORT_LANGUAGES } from '@vben-core/preferences';
import { $t } from '@vben/locales';
import SelectItem from '../select-item.vue';
import SwitchItem from '../switch-item.vue';
defineOptions({
name: 'PreferenceGeneralConfig',
});
const locale = defineModel<string>('locale');
const dynamicTitle = defineModel<boolean>('dynamicTitle');
const shortcutKeys = defineModel<boolean>('shortcutKeys');
const localeItems: SelectListItem[] = SUPPORT_LANGUAGES.map((item) => ({
label: item.text,
value: item.key,
}));
</script>
<template>
<SelectItem v-model="locale" :items="localeItems">
{{ $t('preference.language') }}
</SelectItem>
<SwitchItem v-model="dynamicTitle">
{{ $t('preference.dynamic-title') }}
</SwitchItem>
<SwitchItem v-model="shortcutKeys">
{{ $t('preference.shortcut-key') }}
</SwitchItem>
</template>

View File

@@ -0,0 +1,15 @@
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 Tabs } from './layout/tabs.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';

View File

@@ -0,0 +1,56 @@
<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: 'PreferenceBreadcrumbConfig',
});
defineProps<{ disabled: boolean }>();
const breadcrumbVisible = defineModel<boolean>('breadcrumbVisible');
const breadcrumbIcon = defineModel<boolean>('breadcrumbIcon');
const breadcrumbStyle = defineModel<string>('breadcrumbStyle');
const breadcrumbHome = defineModel<boolean>('breadcrumbHome');
const breadcrumbHideOnlyOne = defineModel<boolean>('breadcrumbHideOnlyOne');
const typeItems: SelectListItem[] = [
{ label: $t('preference.normal'), value: 'normal' },
{ label: $t('preference.breadcrumb-background'), value: 'background' },
];
</script>
<template>
<SwitchItem v-model="breadcrumbVisible" :disabled="disabled">
{{ $t('preference.breadcrumb-enable') }}
</SwitchItem>
<SwitchItem
v-model="breadcrumbHideOnlyOne"
:disabled="!breadcrumbVisible || disabled"
>
{{ $t('preference.breadcrumb-hide-only-one') }}
</SwitchItem>
<SwitchItem
v-model="breadcrumbHome"
:disabled="!breadcrumbVisible || disabled"
>
{{ $t('preference.breadcrumb-home') }}
</SwitchItem>
<SwitchItem
v-model="breadcrumbIcon"
:disabled="!breadcrumbVisible || disabled"
>
{{ $t('preference.breadcrumb-icon') }}
</SwitchItem>
<ToggleItem
v-model="breadcrumbStyle"
:items="typeItems"
:disabled="!breadcrumbVisible || disabled"
>
{{ $t('preference.breadcrumb-style') }}
</ToggleItem>
</template>

View File

@@ -0,0 +1,50 @@
<script setup lang="ts">
import { $t } from '@vben/locales';
import { type Component, computed } from 'vue';
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('preference.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>

View File

@@ -0,0 +1,21 @@
<script setup lang="ts">
import { $t } from '@vben/locales';
import SwitchItem from '../switch-item.vue';
defineOptions({
name: 'PreferenceBreadcrumbConfig',
});
const footerVisible = defineModel<boolean>('footerVisible');
const footerFixed = defineModel<boolean>('footerFixed');
</script>
<template>
<SwitchItem v-model="footerVisible">
{{ $t('preference.footer-visible') }}
</SwitchItem>
<SwitchItem v-model="footerFixed" :disabled="!footerVisible">
{{ $t('preference.footer-fixed') }}
</SwitchItem>
</template>

View File

@@ -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 headerVisible = defineModel<boolean>('headerVisible');
const headerMode = defineModel<LayoutHeaderModeType>('headerMode');
const localeItems: SelectListItem[] = [
{
label: $t('preference.header-mode-static'),
value: 'static',
},
{
label: $t('preference.header-mode-fixed'),
value: 'fixed',
},
{
label: $t('preference.header-mode-auto'),
value: 'auto',
},
{
label: $t('preference.header-mode-auto-scroll'),
value: 'auto-scroll',
},
];
</script>
<template>
<SwitchItem v-model="headerVisible" :disabled="disabled">
{{ $t('preference.header-visible') }}
</SwitchItem>
<SelectItem
v-model="headerMode"
:items="localeItems"
:disabled="!headerVisible"
>
{{ $t('preference.mode') }}
</SelectItem>
</template>

View File

@@ -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('preference.tabs-visible') }}
</SwitchItem>
<SwitchItem v-model="logoVisible">
{{ $t('preference.logo-visible') }}
</SwitchItem>
</template>

View File

@@ -0,0 +1,95 @@
<script setup lang="ts">
import type { LayoutType } from '@vben/types';
import { MdiQuestionMarkCircleOutline } from '@vben-core/iconify';
import { VbenTooltip } from '@vben-core/shadcn-ui';
import { $t } from '@vben/locales';
import { type Component, computed } from 'vue';
import {
FullContent,
HeaderNav,
MixedNav,
SideMixedNav,
SideNav,
} from '../../icons';
interface PresetItem {
name: string;
tip: string;
type: LayoutType;
}
defineOptions({
name: 'PreferenceLayout',
});
const modelValue = defineModel<LayoutType>({ default: 'side-nav' });
const components: Record<LayoutType, Component> = {
'full-content': FullContent,
'header-nav': HeaderNav,
'mixed-nav': MixedNav,
'side-mixed-nav': SideMixedNav,
'side-nav': SideNav,
};
const PRESET = computed((): PresetItem[] => [
{
name: $t('preference.vertical'),
tip: $t('preference.vertical-tip'),
type: 'side-nav',
},
{
name: $t('preference.two-column'),
tip: $t('preference.two-column-tip'),
type: 'side-mixed-nav',
},
{
name: $t('preference.horizontal'),
tip: $t('preference.vertical-tip'),
type: 'header-nav',
},
{
name: $t('preference.mixed-menu'),
tip: $t('preference.mixed-menu-tip'),
type: 'mixed-nav',
},
{
name: $t('preference.full-content'),
tip: $t('preference.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>

View File

@@ -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 navigationStyle = defineModel<string>('navigationStyle');
const navigationSplit = defineModel<boolean>('navigationSplit');
const navigationAccordion = defineModel<boolean>('navigationAccordion');
const stylesItems: SelectListItem[] = [
{ label: $t('preference.rounded'), value: 'rounded' },
{ label: $t('preference.plain'), value: 'plain' },
];
</script>
<template>
<ToggleItem
v-model="navigationStyle"
:items="stylesItems"
:disabled="disabled"
>
{{ $t('preference.navigation-style') }}
</ToggleItem>
<SwitchItem
v-model="navigationSplit"
:disabled="disabledNavigationSplit || disabled"
>
{{ $t('preference.navigation-split') }}
<template #tip>
{{ $t('preference.navigation-split-tip') }}
</template>
</SwitchItem>
<SwitchItem v-model="navigationAccordion" :disabled="disabled">
{{ $t('preference.navigation-accordion') }}
</SwitchItem>
</template>

View File

@@ -0,0 +1,30 @@
<script setup lang="ts">
import { $t } from '@vben/locales';
import SwitchItem from '../switch-item.vue';
defineOptions({
name: 'PreferenceBreadcrumbConfig',
});
defineProps<{ disabled: boolean }>();
const sideVisible = defineModel<boolean>('sideVisible');
const sideCollapseShowTitle = defineModel<boolean>('sideCollapseShowTitle');
const sideCollapse = defineModel<boolean>('sideCollapse');
</script>
<template>
<SwitchItem v-model="sideVisible" :disabled="disabled">
{{ $t('preference.side-visible') }}
</SwitchItem>
<SwitchItem v-model="sideCollapse" :disabled="!sideVisible || disabled">
{{ $t('preference.collapse') }}
</SwitchItem>
<SwitchItem
v-model="sideCollapseShowTitle"
:disabled="!sideVisible || disabled"
>
{{ $t('preference.collapse-show-title') }}
</SwitchItem>
</template>

View File

@@ -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 tabsVisible = defineModel<boolean>('tabsVisible');
const tabsIcon = defineModel<boolean>('tabsIcon');
</script>
<template>
<SwitchItem v-model="tabsVisible" :disabled="disabled">
{{ $t('preference.tabs-visible') }}
</SwitchItem>
<SwitchItem v-model="tabsIcon" :disabled="!tabsVisible">
{{ $t('preference.tabs-icon') }}
</SwitchItem>
<!-- <SwitchItem v-model="sideCollapseShowTitle" :disabled="!tabsVisible">
{{ $t('preference.collapse-show-title') }}
</SwitchItem> -->
</template>

View File

@@ -0,0 +1,67 @@
<script setup lang="ts">
import type { SelectListItem } from '@vben/types';
import { MdiQuestionMarkCircleOutline } from '@vben-core/iconify';
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
VbenTooltip,
} from '@vben-core/shadcn-ui';
import { useSlots } from 'vue';
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="my-1 flex w-full items-center justify-between rounded-md px-2 py-1"
:class="{
'hover:bg-accent': !slots.tip,
'pointer-events-none opacity-50': disabled,
}"
>
<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>

View File

@@ -0,0 +1,44 @@
<script setup lang="ts">
import { MdiQuestionMarkCircleOutline } from '@vben-core/iconify';
import { Switch, VbenTooltip } from '@vben-core/shadcn-ui';
import { useSlots } from 'vue';
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="hover:bg-accent my-1 flex w-full items-center justify-between rounded-md px-2 py-2"
:class="{
'pointer-events-none opacity-50': disabled,
}"
@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>
<Switch v-model:checked="checked" @click.stop />
</div>
</template>

View File

@@ -0,0 +1,26 @@
<script setup lang="ts">
import { $t } from '@vben/locales';
import SwitchItem from '../switch-item.vue';
defineOptions({
name: 'PreferenceColorMode',
});
const colorWeakMode = defineModel<boolean>('colorWeakMode', {
default: false,
});
const colorGrayMode = defineModel<boolean>('colorGrayMode', {
default: false,
});
</script>
<template>
<SwitchItem v-model="colorWeakMode">
{{ $t('preference.weak-mode') }}
</SwitchItem>
<SwitchItem v-model="colorGrayMode">
{{ $t('preference.gray-mode') }}
</SwitchItem>
</template>

View File

@@ -0,0 +1,91 @@
<script setup lang="ts">
import { MdiEditBoxOutline } from '@vben-core/iconify';
import { TinyColor, convertToHsl } from '@vben-core/toolkit';
import type { CSSProperties } from 'vue';
import { computed, ref, watch, watchEffect } from 'vue';
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>

View File

@@ -0,0 +1,82 @@
<script setup lang="ts">
import {
IcRoundMotionPhotosAuto,
IcRoundWbSunny,
MdiMoonAndStars,
} from '@vben-core/iconify';
import { $t } from '@vben/locales';
import SwitchItem from '../switch-item.vue';
defineOptions({
name: 'PreferenceTheme',
});
const modelValue = defineModel<string>({ default: 'auto' });
const semiDarkMenu = defineModel<boolean>('semiDarkMenu', {
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('preference.light');
}
case 'dark': {
return $t('preference.dark');
}
case 'auto': {
return $t('preference.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="semiDarkMenu"
:disabled="modelValue !== 'light'"
class="mt-6"
>
{{ $t('preference.dark-menu') }}
</SwitchItem>
</div>
</template>

View File

@@ -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
disabled
class="hover:bg-accent flex w-full items-center justify-between rounded-md px-2 py-2"
:class="{
'pointer-events-none opacity-50': disabled,
}"
>
<span class="text-sm"><slot></slot></span>
<ToggleGroup
v-model="modelValue"
type="single"
variant="outline"
size="sm"
class="gap-2"
>
<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>

View File

@@ -0,0 +1,119 @@
<template>
<svg
class="custom-radio-image"
fill="none"
height="66"
width="104"
xmlns="http://www.w3.org/2000/svg"
>
<g>
<rect
id="svg_1"
fill="currentColor"
fill-opacity="0.02"
height="66"
rx="4"
stroke="null"
width="104"
x="0.13514"
y="0.13514"
/>
<rect
id="svg_8"
fill="hsl(var(--color-primary))"
height="9.07027"
stroke="null"
width="104.07934"
x="-0.07419"
y="-0.05773"
/>
<rect
id="svg_3"
fill="#e5e5e5"
height="2.789"
rx="1.395"
stroke="null"
width="7.52486"
x="15.58168"
y="3.20832"
/>
<path
id="svg_12"
d="m98.19822,2.872c0,-0.54338 0.45662,-1 1,-1l1.925,0c0.54338,0 1,0.45662 1,1l0,2.4c0,0.54338 -0.45662,1 -1,1l-1.925,0c-0.54338,0 -1,-0.45662 -1,-1l0,-2.4z"
fill="#ffffff"
opacity="undefined"
stroke="null"
/>
<rect
id="svg_13"
fill="currentColor"
fill-opacity="0.08"
height="21.51892"
rx="2"
stroke="null"
width="41.98275"
x="45.37589"
y="13.53192"
/>
<path
id="svg_14"
d="m16.4123,15.53192c0,-1.08676 0.74096,-2 1.62271,-2l21.74653,0c0.88175,0 1.62271,0.91324 1.62271,2l0,17.24865c0,1.08676 -0.74096,2 -1.62271,2l-21.74653,0c-0.88175,0 -1.62271,-0.91324 -1.62271,-2l0,-17.24865z"
fill="currentColor"
fill-opacity="0.08"
opacity="undefined"
stroke="null"
/>
<rect
id="svg_15"
fill="currentColor"
fill-opacity="0.08"
height="21.65405"
rx="2"
stroke="null"
width="71.10636"
x="16.54743"
y="39.34689"
/>
<rect
id="svg_21"
fill="#e5e5e5"
height="2.789"
rx="1.395"
stroke="null"
width="7.52486"
x="28.14924"
y="3.07319"
/>
<rect
id="svg_22"
fill="#e5e5e5"
height="2.789"
rx="1.395"
stroke="null"
width="7.52486"
x="41.25735"
y="3.20832"
/>
<rect
id="svg_23"
fill="#e5e5e5"
height="2.789"
rx="1.395"
stroke="null"
width="7.52486"
x="54.23033"
y="3.07319"
/>
<rect
id="svg_4"
fill="#ffffff"
height="7.13843"
rx="2"
stroke="null"
width="7.78397"
x="1.5327"
y="0.881"
/>
</g>
</svg>
</template>

View File

@@ -0,0 +1,50 @@
<template>
<svg
class="custom-radio-image"
fill="none"
height="66"
width="104"
xmlns="http://www.w3.org/2000/svg"
>
<g>
<path
id="svg_1"
d="m0.13514,4.13514c0,-2.17352 1.82648,-4 4,-4l96,0c2.17352,0 4,1.82648 4,4l0,58c0,2.17352 -1.82648,4 -4,4l-96,0c-2.17352,0 -4,-1.82648 -4,-4l0,-58z"
fill="currentColor"
fill-opacity="0.02"
opacity="undefined"
stroke="null"
/>
<rect
id="svg_13"
fill="currentColor"
fill-opacity="0.08"
height="26.57155"
rx="2"
stroke="null"
width="53.18333"
x="45.79979"
y="3.77232"
/>
<path
id="svg_14"
d="m4.28142,5.96169c0,-1.37748 1.06465,-2.53502 2.33158,-2.53502l31.2463,0c1.26693,0 2.33158,1.15754 2.33158,2.53502l0,21.86282c0,1.37748 -1.06465,2.53502 -2.33158,2.53502l-31.2463,0c-1.26693,0 -2.33158,-1.15754 -2.33158,-2.53502l0,-21.86282z"
fill="currentColor"
fill-opacity="0.08"
opacity="undefined"
stroke="null"
/>
<rect
id="svg_15"
fill="currentColor"
fill-opacity="0.08"
height="25.02247"
rx="2"
stroke="null"
width="94.39371"
x="4.56735"
y="34.92584"
/>
</g>
</svg>
</template>

View File

@@ -0,0 +1,119 @@
<template>
<svg
class="custom-radio-image"
fill="none"
height="66"
width="104"
xmlns="http://www.w3.org/2000/svg"
>
<g>
<rect
id="svg_1"
fill="currentColor"
fill-opacity="0.02"
height="66"
rx="4"
stroke="null"
width="104"
x="0.13514"
y="0.13514"
/>
<rect
id="svg_8"
fill="hsl(var(--color-primary))"
height="9.07027"
stroke="null"
width="104.07934"
x="-0.07419"
y="-0.05773"
/>
<rect
id="svg_3"
fill="#e5e5e5"
height="2.789"
rx="1.395"
stroke="null"
width="7.52486"
x="15.58168"
y="3.20832"
/>
<path
id="svg_12"
d="m98.19822,2.872c0,-0.54338 0.45662,-1 1,-1l1.925,0c0.54338,0 1,0.45662 1,1l0,2.4c0,0.54338 -0.45662,1 -1,1l-1.925,0c-0.54338,0 -1,-0.45662 -1,-1l0,-2.4z"
fill="#ffffff"
opacity="undefined"
stroke="null"
/>
<rect
id="svg_13"
fill="currentColor"
fill-opacity="0.08"
height="21.51892"
rx="2"
stroke="null"
width="53.60438"
x="43.484"
y="13.66705"
/>
<path
id="svg_14"
d="m3.43932,15.53192c0,-1.08676 1.03344,-2 2.26323,-2l30.33036,0c1.22979,0 2.26323,0.91324 2.26323,2l0,17.24865c0,1.08676 -1.03344,2 -2.26323,2l-30.33036,0c-1.22979,0 -2.26323,-0.91324 -2.26323,-2l0,-17.24865z"
fill="currentColor"
fill-opacity="0.08"
opacity="undefined"
stroke="null"
/>
<rect
id="svg_15"
fill="currentColor"
fill-opacity="0.08"
height="21.65405"
rx="2"
stroke="null"
width="95.02528"
x="3.30419"
y="39.34689"
/>
<rect
id="svg_21"
fill="#e5e5e5"
height="2.789"
rx="1.395"
stroke="null"
width="7.52486"
x="28.14924"
y="3.07319"
/>
<rect
id="svg_22"
fill="#e5e5e5"
height="2.789"
rx="1.395"
stroke="null"
width="7.52486"
x="41.25735"
y="3.20832"
/>
<rect
id="svg_23"
fill="#e5e5e5"
height="2.789"
rx="1.395"
stroke="null"
width="7.52486"
x="54.23033"
y="3.07319"
/>
<rect
id="svg_4"
fill="#ffffff"
height="7.13843"
rx="2"
stroke="null"
width="7.78397"
x="1.5327"
y="0.881"
/>
</g>
</svg>
</template>

View File

@@ -0,0 +1,10 @@
import HeaderNav from './header-nav.vue';
export { default as ContentCompact } from './content-compact.vue';
export { default as FullContent } from './full-content.vue';
export { default as MixedNav } from './mixed-nav.vue';
export { default as SideMixedNav } from './side-mixed-nav.vue';
export { default as SideNav } from './side-nav.vue';
const ContentWide = HeaderNav;
export { ContentWide, HeaderNav };

View File

@@ -0,0 +1,161 @@
<template>
<svg
class="custom-radio-image"
fill="none"
height="66"
width="104"
xmlns="http://www.w3.org/2000/svg"
>
<g>
<rect
id="svg_1"
fill="currentColor"
fill-opacity="0.02"
height="66"
rx="4"
stroke="null"
width="104"
x="0.13514"
y="0.13514"
/>
<rect
id="svg_8"
fill="hsl(var(--color-primary))"
height="9.07027"
stroke="null"
width="104.07934"
x="-0.07419"
y="-0.05773"
/>
<rect
id="svg_3"
fill="#e5e5e5"
height="2.789"
rx="1.395"
stroke="null"
width="7.52486"
x="15.58168"
y="3.20832"
/>
<path
id="svg_12"
d="m98.19822,2.872c0,-0.54338 0.45662,-1 1,-1l1.925,0c0.54338,0 1,0.45662 1,1l0,2.4c0,0.54338 -0.45662,1 -1,1l-1.925,0c-0.54338,0 -1,-0.45662 -1,-1l0,-2.4z"
fill="#ffffff"
opacity="undefined"
stroke="null"
/>
<rect
id="svg_13"
fill="currentColor"
fill-opacity="0.08"
height="21.51892"
rx="2"
stroke="null"
width="44.13071"
x="53.37873"
y="13.45652"
/>
<path
id="svg_14"
d="m19.4393,15.74245c0,-1.08676 0.79001,-2 1.73013,-2l23.18605,0c0.94011,0 1.73013,0.91324 1.73013,2l0,17.24865c0,1.08676 -0.79001,2 -1.73013,2l-23.18605,0c-0.94011,0 -1.73013,-0.91324 -1.73013,-2l0,-17.24865z"
fill="currentColor"
fill-opacity="0.08"
opacity="undefined"
stroke="null"
/>
<rect
id="svg_15"
fill="currentColor"
fill-opacity="0.08"
height="21.65405"
rx="2"
stroke="null"
width="78.39372"
x="19.93575"
y="39.34689"
/>
<rect
id="svg_21"
fill="#e5e5e5"
height="2.789"
rx="1.395"
stroke="null"
width="7.52486"
x="28.14924"
y="3.07319"
/>
<rect
id="svg_22"
fill="#e5e5e5"
height="2.789"
rx="1.395"
stroke="null"
width="7.52486"
x="41.25735"
y="3.20832"
/>
<rect
id="svg_23"
fill="#e5e5e5"
height="2.789"
rx="1.395"
stroke="null"
width="7.52486"
x="54.23033"
y="3.07319"
/>
<rect
id="svg_4"
fill="#ffffff"
height="7.13843"
rx="2"
stroke="null"
width="7.78397"
x="1.5327"
y="0.881"
/>
<rect
id="svg_5"
fill="currentColor"
fill-opacity="0.08"
height="56.81191"
stroke="null"
width="15.44642"
x="-0.06423"
y="9.03113"
/>
<path
id="svg_2"
d="m2.38669,15.38074c0,-0.20384 0.27195,-0.37513 0.59557,-0.37513l7.98149,0c0.32362,0 0.59557,0.17129 0.59557,0.37513l0,3.23525c0,0.20384 -0.27195,0.37513 -0.59557,0.37513l-7.98149,0c-0.32362,0 -0.59557,-0.17129 -0.59557,-0.37513l0,-3.23525z"
fill="currentColor"
fill-opacity="0.08"
opacity="undefined"
stroke="null"
/>
<path
id="svg_6"
d="m2.38669,28.43336c0,-0.20384 0.27195,-0.37513 0.59557,-0.37513l7.98149,0c0.32362,0 0.59557,0.17129 0.59557,0.37513l0,3.23525c0,0.20384 -0.27195,0.37513 -0.59557,0.37513l-7.98149,0c-0.32362,0 -0.59557,-0.17129 -0.59557,-0.37513l0,-3.23525z"
fill="currentColor"
fill-opacity="0.08"
opacity="undefined"
stroke="null"
/>
<path
id="svg_7"
d="m2.17616,41.27545c0,-0.20384 0.27195,-0.37513 0.59557,-0.37513l7.98149,0c0.32362,0 0.59557,0.17129 0.59557,0.37513l0,3.23525c0,0.20384 -0.27195,0.37513 -0.59557,0.37513l-7.98149,0c-0.32362,0 -0.59557,-0.17129 -0.59557,-0.37513l0,-3.23525z"
fill="currentColor"
fill-opacity="0.08"
opacity="undefined"
stroke="null"
/>
<path
id="svg_9"
d="m2.17616,54.32806c0,-0.20384 0.27195,-0.37513 0.59557,-0.37513l7.98149,0c0.32362,0 0.59557,0.17129 0.59557,0.37513l0,3.23525c0,0.20384 -0.27195,0.37513 -0.59557,0.37513l-7.98149,0c-0.32362,0 -0.59557,-0.17129 -0.59557,-0.37513l0,-3.23525z"
fill="currentColor"
fill-opacity="0.08"
opacity="undefined"
stroke="null"
/>
</g>
</svg>
</template>

View File

@@ -0,0 +1,13 @@
<template>
<svg
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M19.9 12.66a1 1 0 0 1 0-1.32l1.28-1.44a1 1 0 0 0 .12-1.17l-2-3.46a1 1 0 0 0-1.07-.48l-1.88.38a1 1 0 0 1-1.15-.66l-.61-1.83a1 1 0 0 0-.95-.68h-4a1 1 0 0 0-1 .68l-.56 1.83a1 1 0 0 1-1.15.66L5 4.79a1 1 0 0 0-1 .48L2 8.73a1 1 0 0 0 .1 1.17l1.27 1.44a1 1 0 0 1 0 1.32L2.1 14.1a1 1 0 0 0-.1 1.17l2 3.46a1 1 0 0 0 1.07.48l1.88-.38a1 1 0 0 1 1.15.66l.61 1.83a1 1 0 0 0 1 .68h4a1 1 0 0 0 .95-.68l.61-1.83a1 1 0 0 1 1.15-.66l1.88.38a1 1 0 0 0 1.07-.48l2-3.46a1 1 0 0 0-.12-1.17ZM18.41 14l.8.9l-1.28 2.22l-1.18-.24a3 3 0 0 0-3.45 2L12.92 20h-2.56L10 18.86a3 3 0 0 0-3.45-2l-1.18.24l-1.3-2.21l.8-.9a3 3 0 0 0 0-4l-.8-.9l1.28-2.2l1.18.24a3 3 0 0 0 3.45-2L10.36 4h2.56l.38 1.14a3 3 0 0 0 3.45 2l1.18-.24l1.28 2.22l-.8.9a3 3 0 0 0 0 3.98m-6.77-6a4 4 0 1 0 4 4a4 4 0 0 0-4-4m0 6a2 2 0 1 1 2-2a2 2 0 0 1-2 2"
fill="white"
/>
</svg>
</template>

View File

@@ -0,0 +1,173 @@
<template>
<svg
class="custom-radio-image"
fill="none"
height="66"
width="104"
xmlns="http://www.w3.org/2000/svg"
>
<g>
<rect
id="svg_1"
fill="currentColor"
fill-opacity="0.02"
height="66"
rx="4"
stroke="null"
width="104"
x="0.13514"
y="0.13514"
/>
<path
id="svg_2"
d="m-3.37838,3.7543a1.93401,4.02457 0 0 1 1.93401,-4.02457l11.3488,0l0,66.40541l-11.3488,0a1.93401,4.02457 0 0 1 -1.93401,-4.02457l0,-58.35627z"
fill="hsl(var(--color-primary))"
stroke="null"
/>
<rect
id="svg_3"
fill="#e5e5e5"
height="2.789"
rx="1.395"
stroke="null"
width="5.47439"
x="1.64059"
y="15.46086"
/>
<rect
id="svg_4"
fill="#ffffff"
height="7.67897"
rx="2"
stroke="null"
width="8.18938"
x="0.58676"
y="1.42154"
/>
<rect
id="svg_8"
fill="currentColor"
fill-opacity="0.08"
height="9.07027"
rx="2"
stroke="null"
width="75.91967"
x="25.38277"
y="1.42876"
/>
<rect
id="svg_9"
fill="#b2b2b2"
height="4.4"
rx="1"
stroke="null"
width="3.925"
x="27.91529"
y="3.69284"
/>
<rect
id="svg_10"
fill="#b2b2b2"
height="4.4"
rx="1"
stroke="null"
width="3.925"
x="80.75054"
y="3.62876"
/>
<rect
id="svg_11"
fill="#b2b2b2"
height="4.4"
rx="1"
stroke="null"
width="3.925"
x="87.78868"
y="3.69981"
/>
<rect
id="svg_12"
fill="#b2b2b2"
height="4.4"
rx="1"
stroke="null"
width="3.925"
x="94.6847"
y="3.62876"
/>
<rect
id="svg_13"
fill="currentColor"
fill-opacity="0.08"
height="21.51892"
rx="2"
stroke="null"
width="42.9287"
x="58.75427"
y="14.613"
/>
<rect
id="svg_14"
fill="currentColor"
fill-opacity="0.08"
height="20.97838"
rx="2"
stroke="null"
width="28.36894"
x="26.14342"
y="14.613"
/>
<rect
id="svg_15"
fill="currentColor"
fill-opacity="0.08"
height="21.65405"
rx="2"
stroke="null"
width="75.09493"
x="26.34264"
y="39.68822"
/>
<rect
id="svg_5"
fill="#e5e5e5"
height="2.789"
rx="1.395"
stroke="null"
width="5.47439"
x="1.79832"
y="28.39462"
/>
<rect
id="svg_6"
fill="#e5e5e5"
height="2.789"
rx="1.395"
stroke="null"
width="5.47439"
x="1.64059"
y="41.80156"
/>
<rect
id="svg_7"
fill="#e5e5e5"
height="2.789"
rx="1.395"
stroke="null"
width="5.47439"
x="1.64059"
y="55.36623"
/>
<rect
id="svg_16"
fill="currentColor"
fill-opacity="0.08"
height="65.72065"
stroke="null"
width="12.49265"
x="9.85477"
y="-0.02618"
/>
</g>
</svg>
</template>

View File

@@ -0,0 +1,153 @@
<template>
<svg
class="custom-radio-image"
fill="none"
height="66"
width="104"
xmlns="http://www.w3.org/2000/svg"
>
<g>
<rect
id="svg_1"
fill="currentColor"
fill-opacity="0.02"
height="66"
rx="4"
stroke="null"
width="104"
/>
<path
id="svg_2"
d="m-3.37838,3.61916a4.4919,4.02457 0 0 1 4.4919,-4.02457l26.35848,0l0,66.40541l-26.35848,0a4.4919,4.02457 0 0 1 -4.4919,-4.02457l0,-58.35627z"
fill="hsl(var(--color-primary))"
stroke="null"
/>
<rect
id="svg_3"
fill="#e5e5e5"
height="2.789"
rx="1.395"
width="17.66"
x="4.906"
y="23.884"
/>
<rect
id="svg_4"
fill="#ffffff"
height="9.706"
rx="2"
width="9.811"
x="8.83"
y="5.881"
/>
<path
id="svg_5"
d="m4.906,35.833c0,-0.75801 0.63699,-1.395 1.395,-1.395l14.87,0c0.75801,0 1.395,0.63699 1.395,1.395l0,-0.001c0,0.75801 -0.63699,1.395 -1.395,1.395l-14.87,0c-0.75801,0 -1.395,-0.63699 -1.395,-1.395l0,0.001z"
fill="#ffffff"
opacity="undefined"
/>
<rect
id="svg_6"
fill="#ffffff"
height="2.789"
rx="1.395"
width="17.66"
x="4.906"
y="44.992"
/>
<rect
id="svg_7"
fill="#ffffff"
height="2.789"
rx="1.395"
width="17.66"
x="4.906"
y="55.546"
/>
<rect
id="svg_8"
fill="currentColor"
fill-opacity="0.08"
height="9.07027"
rx="2"
stroke="null"
width="73.53879"
x="28.97986"
y="1.42876"
/>
<rect
id="svg_9"
fill="#b2b2b2"
height="4.4"
rx="1"
stroke="null"
width="3.925"
x="32.039"
y="3.89903"
/>
<rect
id="svg_10"
fill="#b2b2b2"
height="4.4"
rx="1"
stroke="null"
width="3.925"
x="80.75054"
y="3.62876"
/>
<rect
id="svg_11"
fill="#b2b2b2"
height="4.4"
rx="1"
stroke="null"
width="3.925"
x="87.58249"
y="3.49362"
/>
<rect
id="svg_12"
fill="#b2b2b2"
height="4.4"
rx="1"
stroke="null"
width="3.925"
x="94.6847"
y="3.62876"
/>
<rect
id="svg_13"
fill="currentColor"
fill-opacity="0.08"
height="21.51892"
rx="2"
stroke="null"
width="45.63141"
x="56.05157"
y="14.613"
/>
<rect
id="svg_14"
fill="currentColor"
fill-opacity="0.08"
height="20.97838"
rx="2"
stroke="null"
width="22.82978"
x="29.38527"
y="14.613"
/>
<rect
id="svg_15"
fill="currentColor"
fill-opacity="0.08"
height="21.65405"
rx="2"
stroke="null"
width="72.45771"
x="28.97986"
y="39.48203"
/>
</g>
</svg>
</template>

View File

@@ -0,0 +1 @@
export { default as PreferencesWidget } from './preferences-widget.vue';

View File

@@ -0,0 +1,58 @@
<script lang="ts" setup>
import type { LocaleSupportType } from '@vben/types';
import {
COLOR_PRIMARY_RESETS,
flatPreferences,
updatePreferences,
} from '@vben-core/preferences';
import { loadLocaleMessages } from '@vben/locales';
import Preferences from './preferences.vue';
function updateLocale(value: string) {
const locale = value as LocaleSupportType;
updatePreferences({
app: { locale },
});
// 更改预览
loadLocaleMessages(locale);
}
</script>
<template>
<Preferences
v-model:breadcrumb-visible="flatPreferences.breadcrumbEnable"
v-model:breadcrumb-style="flatPreferences.breadcrumbStyleType"
v-model:color-gray-mode="flatPreferences.appColorGrayMode"
v-model:breadcrumb-icon="flatPreferences.breadcrumbShowIcon"
v-model:color-primary="flatPreferences.themeColorPrimary"
v-model:color-weak-mode="flatPreferences.appColorWeakMode"
v-model:content-compact="flatPreferences.appContentCompact"
v-model:breadcrumb-home="flatPreferences.breadcrumbShowHome"
v-model:side-collapse="flatPreferences.sidebarCollapse"
v-model:layout="flatPreferences.appLayout"
v-model:semi-dark-menu="flatPreferences.appSemiDarkMenu"
v-model:side-visible="flatPreferences.sidebarEnable"
v-model:footer-visible="flatPreferences.footerEnable"
v-model:tabs-visible="flatPreferences.tabbarEnable"
v-model:header-visible="flatPreferences.headerEnable"
v-model:header-mode="flatPreferences.headerMode"
v-model:footer-fixed="flatPreferences.footerFixed"
v-model:theme="flatPreferences.appThemeMode"
v-model:dynamic-title="flatPreferences.appDynamicTitle"
v-model:breadcrumb-hide-only-one="flatPreferences.breadcrumbHideOnlyOne"
v-model:page-transition="flatPreferences.transitionName"
v-model:page-progress="flatPreferences.transitionProgress"
v-model:tabs-icon="flatPreferences.tabbarShowIcon"
v-model:navigation-accordion="flatPreferences.navigationAccordion"
v-model:navigation-style="flatPreferences.navigationStyleType"
v-model:shortcut-keys="flatPreferences.shortcutKeysEnable"
v-model:navigation-split="flatPreferences.navigationSplit"
v-model:page-transition-enable="flatPreferences.transitionEnable"
v-model:side-collapse-show-title="flatPreferences.sidebarCollapseShowTitle"
:color-primary-presets="COLOR_PRIMARY_RESETS"
:locale="flatPreferences.appLocale"
@update:locale="updateLocale"
/>
</template>

View File

@@ -0,0 +1,291 @@
<script setup lang="ts">
import type { LayoutHeaderModeType, LayoutType } from '@vben/types';
import type { SegmentedItem } from '@vben-core/shadcn-ui';
import { IcRoundFolderCopy, IcRoundRestartAlt } from '@vben-core/iconify';
import {
preferences,
resetPreferences,
usePreferences,
} from '@vben-core/preferences';
import {
VbenButton,
VbenIconButton,
VbenSegmented,
VbenSheet,
toast,
} from '@vben-core/shadcn-ui';
import { $t } from '@vben/locales';
import { useClipboard } from '@vueuse/core';
import { computed } from 'vue';
import {
Animation,
Block,
Breadcrumb,
ColorMode,
Content,
Footer,
General,
Header,
Layout,
Navigation,
Sidebar,
Tabs,
Theme,
ThemeColor,
} from './blocks';
import Trigger from './trigger.vue';
import { useOpenPreferences } from './use-open-preferences';
withDefaults(defineProps<{ colorPrimaryPresets: string[] }>(), {
colorPrimaryPresets: () => [],
});
const theme = defineModel<string>('theme');
const locale = defineModel<string>('locale');
const dynamicTitle = defineModel<boolean>('dynamicTitle');
const semiDarkMenu = defineModel<boolean>('semiDarkMenu');
const breadcrumbVisible = defineModel<boolean>('breadcrumbVisible');
const breadcrumbIcon = defineModel<boolean>('breadcrumbIcon');
const breadcrumbHome = defineModel<boolean>('breadcrumbHome');
const breadcrumbStyle = defineModel<string>('breadcrumbStyle');
const breadcrumbHideOnlyOne = defineModel<boolean>('breadcrumbHideOnlyOne');
const sideCollapseShowTitle = defineModel<boolean>('sideCollapseShowTitle');
const sideCollapse = defineModel<boolean>('sideCollapse');
const colorWeakMode = defineModel<boolean>('colorWeakMode');
const colorGrayMode = defineModel<boolean>('colorGrayMode');
const colorPrimary = defineModel<string>('colorPrimary');
const navigationStyle = defineModel<string>('navigationStyle');
const navigationSplit = defineModel<boolean>('navigationSplit');
const navigationAccordion = defineModel<boolean>('navigationAccordion');
const pageProgress = defineModel<boolean>('pageProgress');
const pageTransition = defineModel<string>('pageTransition');
const pageTransitionEnable = defineModel<boolean>('pageTransitionEnable');
const layout = defineModel<LayoutType>('layout');
const contentCompact = defineModel<string>('contentCompact');
const sideVisible = defineModel<boolean>('sideVisible');
const shortcutKeys = defineModel<boolean>('shortcutKeys');
const tabsVisible = defineModel<boolean>('tabsVisible');
const tabsIcon = defineModel<boolean>('tabsIcon');
// const logoVisible = defineModel<boolean>('logoVisible');
const headerVisible = defineModel<boolean>('headerVisible');
const headerMode = defineModel<LayoutHeaderModeType>('headerMode');
const footerVisible = defineModel<boolean>('footerVisible');
const footerFixed = defineModel<boolean>('footerFixed');
const {
diffPreference,
isFullContent,
isHeaderNav,
isMixedNav,
isSideMixedNav,
isSideMode,
isSideNav,
} = usePreferences();
const { copy } = useClipboard();
const tabs = computed((): SegmentedItem[] => {
return [
{
label: $t('preference.appearance'),
value: 'appearance',
},
{
label: $t('preference.layout'),
value: 'layout',
},
{
label: $t('preference.general'),
value: 'general',
},
// {
// label: $t('preference.shortcut-key'),
// value: 'shortcutKey',
// },
];
});
const showBreadcrumbConfig = computed(() => {
return (
!isFullContent.value &&
!isMixedNav.value &&
!isHeaderNav.value &&
preferences.header.enable
);
});
const { openPreferences } = useOpenPreferences();
async function handleCopy() {
await copy(JSON.stringify(diffPreference.value, null, 2));
toast($t('preference.copy-success'));
}
function handleReset() {
if (!diffPreference.value) {
return;
}
resetPreferences();
toast($t('preference.reset-success'));
}
</script>
<template>
<div class="z-100 fixed right-0 top-1/3">
<VbenSheet
v-model:open="openPreferences"
:description="$t('preference.preferences-subtitle')"
:title="$t('preference.preferences')"
>
<template #trigger>
<Trigger />
</template>
<template #extra>
<VbenIconButton
class="relative"
:disabled="!diffPreference"
:tooltip="$t('preference.reset-tip')"
>
<span
v-if="diffPreference"
class="bg-primary absolute right-0.5 top-0.5 h-2 w-2 rounded"
></span>
<IcRoundRestartAlt class="size-5" @click="handleReset" />
</VbenIconButton>
</template>
<div class="p-5 pt-4">
<VbenSegmented :tabs="tabs" default-value="appearance">
<template #appearance>
<Block :title="$t('preference.theme')">
<Theme v-model="theme" v-model:semi-dark-menu="semiDarkMenu" />
</Block>
<Block :title="$t('preference.theme-color')">
<ThemeColor
v-model="colorPrimary"
:color-primary-presets="colorPrimaryPresets"
/>
</Block>
<Block :title="$t('preference.other')">
<ColorMode
v-model:color-gray-mode="colorGrayMode"
v-model:color-weak-mode="colorWeakMode"
/>
</Block>
</template>
<template #layout>
<Block :title="$t('preference.layout')">
<Layout v-model="layout" />
</Block>
<Block :title="$t('preference.content')">
<Content v-model="contentCompact" />
</Block>
<Block :title="$t('preference.sidebar')">
<Sidebar
v-model:side-visible="sideVisible"
v-model:side-collapse="sideCollapse"
v-model:side-collapse-show-title="sideCollapseShowTitle"
:disabled="!isSideMode"
/>
</Block>
<Block :title="$t('preference.header')">
<Header
v-model:header-visible="headerVisible"
v-model:headerMode="headerMode"
:disabled="isFullContent"
/>
</Block>
<Block :title="$t('preference.navigation-menu')">
<Navigation
v-model:navigation-style="navigationStyle"
v-model:navigation-split="navigationSplit"
v-model:navigation-accordion="navigationAccordion"
:disabled="isFullContent"
:disabled-navigation-split="!isMixedNav"
/>
</Block>
<Block :title="$t('preference.breadcrumb')">
<Breadcrumb
v-model:breadcrumb-visible="breadcrumbVisible"
v-model:breadcrumb-icon="breadcrumbIcon"
v-model:breadcrumb-style="breadcrumbStyle"
v-model:breadcrumb-home="breadcrumbHome"
v-model:breadcrumb-hide-only-one="breadcrumbHideOnlyOne"
:disabled="
!showBreadcrumbConfig || !(isSideNav || isSideMixedNav)
"
/>
</Block>
<Block :title="$t('preference.tabs')">
<Tabs
v-model:tabs-visible="tabsVisible"
v-model:tabs-icon="tabsIcon"
/>
</Block>
<Block :title="$t('preference.footer')">
<Footer
v-model:footer-visible="footerVisible"
v-model:footer-fixed="footerFixed"
/>
</Block>
</template>
<template #general>
<Block :title="$t('preference.general')">
<General
v-model:locale="locale"
v-model:dynamic-title="dynamicTitle"
v-model:shortcut-keys="shortcutKeys"
/>
</Block>
<Block :title="$t('preference.animation')">
<Animation
v-model:page-progress="pageProgress"
v-model:page-transition="pageTransition"
v-model:page-transition-enable="pageTransitionEnable"
/>
</Block>
</template>
<template #shortcutKey>
<Block :title="$t('preference.general')">
<General
v-model:locale="locale"
v-model:dynamic-title="dynamicTitle"
v-model:shortcut-keys="shortcutKeys"
/>
</Block>
<Block :title="$t('preference.animation')">
<Animation
v-model:page-progress="pageProgress"
v-model:page-transition="pageTransition"
v-model:page-transition-enable="pageTransitionEnable"
/>
</Block>
</template>
</VbenSegmented>
</div>
<template #footer>
<VbenButton
class="mx-6 w-full"
variant="default"
size="sm"
:disabled="!diffPreference"
@click="handleCopy"
>
<IcRoundFolderCopy class="mr-2 size-3" />
{{ $t('preference.copy') }}
</VbenButton>
</template>
</VbenSheet>
</div>
</template>

View File

@@ -0,0 +1,20 @@
<script setup lang="ts">
import { VbenButton } from '@vben-core/shadcn-ui';
import { $t } from '@vben/locales';
import IconSetting from './icons/setting.vue';
defineOptions({
name: 'PreferenceTrigger',
});
</script>
<template>
<VbenButton
class="bg-primary flex-col-center h-9 w-9 cursor-pointer rounded-l-md rounded-r-none border-none"
:title="$t('preference.preferences')"
>
<IconSetting class="text-lg" />
</VbenButton>
</template>

View File

@@ -0,0 +1,16 @@
import { ref } from 'vue';
const openPreferences = ref(false);
function useOpenPreferences() {
function handleOpenPreference() {
openPreferences.value = true;
}
return {
handleOpenPreference,
openPreferences,
};
}
export { useOpenPreferences };