mirror of
https://github.com/soybeanjs/soybean-admin.git
synced 2025-09-25 20:56:44 +08:00
Compare commits
12 Commits
bf0e5569ac
...
65c318bcc5
Author | SHA1 | Date | |
---|---|---|---|
|
65c318bcc5 | ||
|
8e6a6d6866 | ||
|
3c0a52825d | ||
|
100e0ea55d | ||
|
257f1183fc | ||
|
d73111116a | ||
|
29a2a5c66a | ||
|
4005763c00 | ||
|
a55b4dc073 | ||
|
be8f915a0c | ||
|
358e129765 | ||
|
9ea56c9b82 |
13
.vscode/settings.json
vendored
13
.vscode/settings.json
vendored
@ -4,7 +4,18 @@
|
||||
"source.organizeImports": "never"
|
||||
},
|
||||
"editor.formatOnSave": false,
|
||||
"eslint.validate": ["html", "css", "scss", "json", "jsonc"],
|
||||
"eslint.validate": [
|
||||
"html",
|
||||
"css",
|
||||
"scss",
|
||||
"json",
|
||||
"jsonc",
|
||||
"javascript",
|
||||
"javascriptreact",
|
||||
"typescript",
|
||||
"typescriptreact",
|
||||
"vue"
|
||||
],
|
||||
"i18n-ally.displayLanguage": "zh-cn",
|
||||
"i18n-ally.enabledParsers": ["ts"],
|
||||
"i18n-ally.enabledFrameworks": ["vue"],
|
||||
|
@ -86,7 +86,7 @@
|
||||
"vue": "3.5.17",
|
||||
"vue-draggable-plus": "0.6.0",
|
||||
"vue-i18n": "11.1.10",
|
||||
"vue-pdf-embed": "2.1.3",
|
||||
"vue-pdf-embed": "2.1.2",
|
||||
"vue-router": "4.5.1",
|
||||
"wangeditor": "4.7.15",
|
||||
"xgplayer": "3.0.22",
|
||||
|
@ -32,7 +32,8 @@ export function createStorage<T extends object>(type: StorageType, storagePrefix
|
||||
storageData = JSON.parse(json);
|
||||
} catch {}
|
||||
|
||||
if (storageData) {
|
||||
// storageData may be `false` if it is boolean type
|
||||
if (storageData !== null) {
|
||||
return storageData as T[K];
|
||||
}
|
||||
}
|
||||
|
902
pnpm-lock.yaml
902
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
42
src/components/common/icon-tooltip.vue
Normal file
42
src/components/common/icon-tooltip.vue
Normal file
@ -0,0 +1,42 @@
|
||||
<script lang="ts" setup>
|
||||
import { computed, useSlots } from 'vue';
|
||||
import type { PopoverPlacement } from 'naive-ui';
|
||||
|
||||
defineOptions({ name: 'IconTooltip' });
|
||||
|
||||
interface Props {
|
||||
icon?: string;
|
||||
localIcon?: string;
|
||||
desc?: string;
|
||||
placement?: PopoverPlacement;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
icon: 'mdi-help-circle',
|
||||
localIcon: '',
|
||||
desc: '',
|
||||
placement: 'top'
|
||||
});
|
||||
|
||||
const slots = useSlots();
|
||||
const hasCustomTrigger = computed(() => Boolean(slots.trigger));
|
||||
|
||||
if (!hasCustomTrigger.value && !props.icon && !props.localIcon) {
|
||||
throw new Error('icon or localIcon is required when no custom trigger slot is provided');
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NTooltip :placement="placement">
|
||||
<template #trigger>
|
||||
<slot name="trigger">
|
||||
<div class="cursor-pointer">
|
||||
<SvgIcon :icon="icon" :local-icon="localIcon" />
|
||||
</div>
|
||||
</slot>
|
||||
</template>
|
||||
<slot>
|
||||
<span>{{ desc }}</span>
|
||||
</slot>
|
||||
</NTooltip>
|
||||
</template>
|
@ -58,8 +58,8 @@ export const themePageAnimationModeRecord: Record<UnionKey.ThemePageAnimateMode,
|
||||
export const themePageAnimationModeOptions = transformRecordToOption(themePageAnimationModeRecord);
|
||||
|
||||
export const resetCacheStrategyRecord: Record<UnionKey.ResetCacheStrategy, App.I18n.I18nKey> = {
|
||||
close: 'theme.layout.resetCacheStrategy.close',
|
||||
refresh: 'theme.layout.resetCacheStrategy.refresh'
|
||||
refresh: 'theme.layout.resetCacheStrategy.refresh',
|
||||
close: 'theme.layout.resetCacheStrategy.close'
|
||||
};
|
||||
|
||||
export const resetCacheStrategyOptions = transformRecordToOption(resetCacheStrategyRecord);
|
||||
|
@ -38,6 +38,13 @@ export function useNaiveTable<ResponseData, ApiData>(options: UseNaiveTableOptio
|
||||
getColumns
|
||||
});
|
||||
|
||||
// calculate the total width of the table this is used for horizontal scrolling
|
||||
const scrollX = computed(() => {
|
||||
return result.columns.value.reduce((acc, column) => {
|
||||
return acc + Number(column.width ?? column.minWidth ?? 120);
|
||||
}, 0);
|
||||
});
|
||||
|
||||
scope.run(() => {
|
||||
watch(
|
||||
() => appStore.locale,
|
||||
@ -51,7 +58,10 @@ export function useNaiveTable<ResponseData, ApiData>(options: UseNaiveTableOptio
|
||||
scope.stop();
|
||||
});
|
||||
|
||||
return result;
|
||||
return {
|
||||
...result,
|
||||
scrollX
|
||||
};
|
||||
}
|
||||
|
||||
type PaginationParams = Pick<PaginationProps, 'page' | 'pageSize'>;
|
||||
|
@ -30,6 +30,7 @@ const { selectedKey } = useMenu();
|
||||
/>
|
||||
</Teleport>
|
||||
<Teleport :to="`#${GLOBAL_SIDER_MENU_ID}`">
|
||||
<div class="h-full pt-2">
|
||||
<FirstLevelMenu
|
||||
:menus="firstLevelMenus"
|
||||
:active-menu-key="activeFirstLevelMenuKey"
|
||||
@ -39,6 +40,7 @@ const { selectedKey } = useMenu();
|
||||
@select="handleSelectFirstLevelMenu"
|
||||
@toggle-sider-collapse="appStore.toggleSiderCollapse"
|
||||
/>
|
||||
</div>
|
||||
</Teleport>
|
||||
</template>
|
||||
|
||||
|
@ -80,7 +80,7 @@ function handleChangeMode(mode: UnionKey.ThemeLayoutMode) {
|
||||
class="flex-col-center cursor-pointer"
|
||||
@click="handleChangeMode(key)"
|
||||
>
|
||||
<NTooltip :placement="item.placement">
|
||||
<IconTooltip :placement="item.placement">
|
||||
<template #trigger>
|
||||
<div
|
||||
class="h-64px w-96px gap-6px rd-4px p-6px shadow ring-2 ring-transparent transition-all hover:ring-primary"
|
||||
@ -92,7 +92,7 @@ function handleChangeMode(mode: UnionKey.ThemeLayoutMode) {
|
||||
</div>
|
||||
</template>
|
||||
{{ $t(`theme.layout.layoutMode.${key}_detail`) }}
|
||||
</NTooltip>
|
||||
</IconTooltip>
|
||||
<p class="mt-8px text-12px">{{ $t(themeLayoutModeRecord[key]) }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -13,7 +13,7 @@ defineProps<Props>();
|
||||
|
||||
<template>
|
||||
<div class="w-full flex-y-center justify-between">
|
||||
<div>
|
||||
<div class="flex-y-center">
|
||||
<span class="pr-8px text-base-text">{{ label }}</span>
|
||||
<slot name="suffix"></slot>
|
||||
</div>
|
||||
|
@ -6,6 +6,7 @@ import AppearanceSettings from './modules/appearance/index.vue';
|
||||
import LayoutSettings from './modules/layout/index.vue';
|
||||
import GeneralSettings from './modules/general/index.vue';
|
||||
import ConfigOperation from './modules/config-operation.vue';
|
||||
import PresetSettings from './modules/preset/index.vue';
|
||||
|
||||
defineOptions({
|
||||
name: 'ThemeDrawer'
|
||||
@ -33,6 +34,7 @@ const drawerWidth = computed(() => {
|
||||
<NTab name="appearance" :tab="$t('theme.tabs.appearance')"></NTab>
|
||||
<NTab name="layout" :tab="$t('theme.tabs.layout')"></NTab>
|
||||
<NTab name="general" :tab="$t('theme.tabs.general')"></NTab>
|
||||
<NTab name="preset" :tab="$t('theme.tabs.preset')"></NTab>
|
||||
</NTabs>
|
||||
|
||||
<div class="min-h-400px">
|
||||
@ -40,6 +42,7 @@ const drawerWidth = computed(() => {
|
||||
<AppearanceSettings v-if="activeTab === 'appearance'" />
|
||||
<LayoutSettings v-else-if="activeTab === 'layout'" />
|
||||
<GeneralSettings v-else-if="activeTab === 'general'" />
|
||||
<PresetSettings v-else-if="activeTab === 'preset'" />
|
||||
</KeepAlive>
|
||||
</div>
|
||||
|
||||
|
@ -36,12 +36,9 @@ const swatches: string[] = [
|
||||
<template>
|
||||
<NDivider>{{ $t('theme.appearance.themeColor.title') }}</NDivider>
|
||||
<div class="flex-col-stretch gap-12px">
|
||||
<NTooltip placement="top-start">
|
||||
<template #trigger>
|
||||
<SettingItem key="recommend-color" :label="$t('theme.appearance.recommendColor')">
|
||||
<NSwitch v-model:value="themeStore.recommendColor" />
|
||||
</SettingItem>
|
||||
</template>
|
||||
<template #suffix>
|
||||
<IconTooltip>
|
||||
<p>
|
||||
<span class="pr-12px">{{ $t('theme.appearance.recommendColorDesc') }}</span>
|
||||
<br />
|
||||
@ -56,7 +53,11 @@ const swatches: string[] = [
|
||||
https://uicolors.app/create
|
||||
</NButton>
|
||||
</p>
|
||||
</NTooltip>
|
||||
</IconTooltip>
|
||||
</template>
|
||||
<NSwitch v-model:value="themeStore.recommendColor" />
|
||||
</SettingItem>
|
||||
|
||||
<SettingItem
|
||||
v-for="(_, key) in themeStore.themeColors"
|
||||
:key="key"
|
||||
|
@ -19,6 +19,9 @@ const isWrapperScrollMode = computed(() => themeStore.layout.scrollMode === 'wra
|
||||
<NDivider>{{ $t('theme.layout.content.title') }}</NDivider>
|
||||
<TransitionGroup tag="div" name="setting-list" class="flex-col-stretch gap-12px">
|
||||
<SettingItem key="1" :label="$t('theme.layout.content.scrollMode.title')">
|
||||
<template #suffix>
|
||||
<IconTooltip :desc="$t('theme.layout.content.scrollMode.tip')" />
|
||||
</template>
|
||||
<NSelect
|
||||
v-model:value="themeStore.layout.scrollMode"
|
||||
:options="translateOptions(themeScrollModeOptions)"
|
||||
|
@ -27,6 +27,9 @@ const themeStore = useThemeStore();
|
||||
<NSwitch v-model:value="themeStore.tab.visible" />
|
||||
</SettingItem>
|
||||
<SettingItem v-if="themeStore.tab.visible" key="2" :label="$t('theme.layout.tab.cache')">
|
||||
<template #suffix>
|
||||
<IconTooltip :desc="$t('theme.layout.tab.cacheTip')" />
|
||||
</template>
|
||||
<NSwitch v-model:value="themeStore.tab.cache" />
|
||||
</SettingItem>
|
||||
<SettingItem v-if="themeStore.tab.visible" key="3" :label="$t('theme.layout.tab.height')">
|
||||
|
15
src/layouts/modules/theme-drawer/modules/preset/index.vue
Normal file
15
src/layouts/modules/theme-drawer/modules/preset/index.vue
Normal file
@ -0,0 +1,15 @@
|
||||
<script setup lang="ts">
|
||||
import ThemePreset from './modules/theme-preset.vue';
|
||||
|
||||
defineOptions({
|
||||
name: 'PresetSettings'
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex-col-stretch gap-16px">
|
||||
<ThemePreset />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
@ -0,0 +1,148 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
import { useThemeStore } from '@/store/modules/theme';
|
||||
import { $t } from '@/locales';
|
||||
|
||||
defineOptions({
|
||||
name: 'ThemePreset'
|
||||
});
|
||||
|
||||
type ThemePreset = Pick<
|
||||
App.Theme.ThemeSetting,
|
||||
| 'themeScheme'
|
||||
| 'grayscale'
|
||||
| 'colourWeakness'
|
||||
| 'recommendColor'
|
||||
| 'themeColor'
|
||||
| 'otherColor'
|
||||
| 'isInfoFollowPrimary'
|
||||
| 'resetCacheStrategy'
|
||||
| 'layout'
|
||||
| 'page'
|
||||
| 'header'
|
||||
| 'tab'
|
||||
| 'fixedHeaderAndTab'
|
||||
| 'sider'
|
||||
| 'footer'
|
||||
| 'watermark'
|
||||
| 'tokens'
|
||||
> & {
|
||||
name: string;
|
||||
desc: string;
|
||||
i18nkey?: string;
|
||||
version: string;
|
||||
};
|
||||
|
||||
const presetModules = import.meta.glob('@/theme/preset/*.json', { eager: true, import: 'default' });
|
||||
|
||||
const themeStore = useThemeStore();
|
||||
|
||||
// Extract preset data
|
||||
const presets = computed(() =>
|
||||
Object.entries(presetModules)
|
||||
.map(([path, presetData]) => {
|
||||
const fileName = path.split('/').pop()?.replace('.json', '') || '';
|
||||
return {
|
||||
id: fileName,
|
||||
...(presetData as ThemePreset)
|
||||
};
|
||||
})
|
||||
.sort((a, b) => {
|
||||
if (a.name === 'default') return -1;
|
||||
if (b.name === 'default') return 1;
|
||||
return a.name.localeCompare(b.name);
|
||||
})
|
||||
);
|
||||
|
||||
const getPresetName = (preset: ThemePreset): string => {
|
||||
if (!preset.i18nkey) return preset.name;
|
||||
try {
|
||||
const key = `${preset.i18nkey}.name` as App.I18n.I18nKey;
|
||||
const translated = $t(key);
|
||||
return translated !== key ? translated : preset.name;
|
||||
} catch {
|
||||
return preset.name;
|
||||
}
|
||||
};
|
||||
|
||||
const getPresetDesc = (preset: ThemePreset): string => {
|
||||
if (!preset.i18nkey) return preset.desc;
|
||||
try {
|
||||
const key = `${preset.i18nkey}.desc` as App.I18n.I18nKey;
|
||||
const translated = $t(key);
|
||||
return translated !== key ? translated : preset.desc;
|
||||
} catch {
|
||||
return preset.desc;
|
||||
}
|
||||
};
|
||||
|
||||
const applyPreset = ({ themeScheme, grayscale, colourWeakness, layout, watermark, ...rest }: ThemePreset): void => {
|
||||
themeStore.setThemeScheme(themeScheme);
|
||||
themeStore.setGrayscale(grayscale);
|
||||
themeStore.setColourWeakness(colourWeakness);
|
||||
themeStore.setThemeLayout(layout.mode);
|
||||
themeStore.setWatermarkEnableUserName(watermark.enableUserName);
|
||||
themeStore.setWatermarkEnableTime(watermark.enableTime);
|
||||
|
||||
Object.assign(themeStore, {
|
||||
...rest,
|
||||
layout: { ...themeStore.layout, scrollMode: layout.scrollMode },
|
||||
page: { ...rest.page },
|
||||
header: { ...rest.header },
|
||||
tab: { ...rest.tab },
|
||||
sider: { ...rest.sider },
|
||||
footer: { ...rest.footer },
|
||||
watermark: { ...watermark },
|
||||
tokens: { ...rest.tokens }
|
||||
});
|
||||
|
||||
window.$message?.success($t('theme.appearance.preset.applySuccess'));
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NDivider>{{ $t('theme.appearance.preset.title') }}</NDivider>
|
||||
|
||||
<div class="flex flex-col gap-3">
|
||||
<div
|
||||
v-for="preset in presets"
|
||||
:key="preset.id"
|
||||
class="border border-primary/10 rounded-lg border-solid bg-white/5 p-3 backdrop-blur-10 transition-all duration-300 hover:(shadow-md -translate-y-0.5)"
|
||||
>
|
||||
<div class="mb-2 flex items-center justify-between">
|
||||
<div class="min-w-0 w-full flex flex-1 items-center justify-between gap-2">
|
||||
<h5 class="m-0 truncate text-sm text-primary font-600">
|
||||
{{ getPresetName(preset) }}
|
||||
</h5>
|
||||
<NBadge :value="`v${preset.version}`" type="info" size="small" class="flex-shrink-0 opacity-80" />
|
||||
</div>
|
||||
<NButton type="primary" size="tiny" ghost round class="ml-2 flex-shrink-0" @click="applyPreset(preset)">
|
||||
{{ $t('theme.appearance.preset.apply') }}
|
||||
</NButton>
|
||||
</div>
|
||||
|
||||
<p class="line-clamp-2 mb-3 text-xs text-gray-500 leading-4">{{ getPresetDesc(preset) }}</p>
|
||||
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex gap-1">
|
||||
<div
|
||||
v-for="(color, key) in { primary: preset.themeColor, ...preset.otherColor }"
|
||||
:key="key"
|
||||
class="h-3 w-3 cursor-pointer border border-white/30 rounded-full transition-transform hover:scale-110"
|
||||
:style="{ backgroundColor: color }"
|
||||
:class="{ 'ring-1 ring-primary/50': key === 'primary' }"
|
||||
:title="key"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex items-center gap-1">
|
||||
<div class="text-lg">
|
||||
{{ preset.themeScheme === 'dark' ? '🌙' : '☀️' }}
|
||||
</div>
|
||||
<div class="text-lg">
|
||||
{{ preset.grayscale ? '🎨' : '' }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
@ -62,7 +62,8 @@ const local: App.I18n.Schema = {
|
||||
tabs: {
|
||||
appearance: 'Appearance',
|
||||
layout: 'Layout',
|
||||
general: 'General'
|
||||
general: 'General',
|
||||
preset: 'Preset'
|
||||
},
|
||||
appearance: {
|
||||
themeSchema: {
|
||||
@ -83,7 +84,28 @@ const local: App.I18n.Schema = {
|
||||
followPrimary: 'Follow Primary'
|
||||
},
|
||||
recommendColor: 'Apply Recommended Color Algorithm',
|
||||
recommendColorDesc: 'The recommended color algorithm refers to'
|
||||
recommendColorDesc: 'The recommended color algorithm refers to',
|
||||
preset: {
|
||||
title: 'Theme Presets',
|
||||
apply: 'Apply',
|
||||
applySuccess: 'Preset applied successfully',
|
||||
default: {
|
||||
name: 'Default Preset',
|
||||
desc: 'Default theme preset with balanced settings'
|
||||
},
|
||||
dark: {
|
||||
name: 'Dark Preset',
|
||||
desc: 'Dark theme preset for night time usage'
|
||||
},
|
||||
compact: {
|
||||
name: 'Compact Preset',
|
||||
desc: 'Compact layout preset for small screens'
|
||||
},
|
||||
azir: {
|
||||
name: "Azir's Preset",
|
||||
desc: 'It is a cold and elegant preset that Azir likes'
|
||||
}
|
||||
}
|
||||
},
|
||||
layout: {
|
||||
layoutMode: {
|
||||
@ -109,6 +131,7 @@ const local: App.I18n.Schema = {
|
||||
title: 'Tab Settings',
|
||||
visible: 'Tab Visible',
|
||||
cache: 'Tag Bar Info Cache',
|
||||
cacheTip: 'One-click to open/close global keepalive',
|
||||
height: 'Tab Height',
|
||||
mode: {
|
||||
title: 'Tab Mode',
|
||||
@ -144,6 +167,7 @@ const local: App.I18n.Schema = {
|
||||
title: 'Content Area Settings',
|
||||
scrollMode: {
|
||||
title: 'Scroll Mode',
|
||||
tip: 'The theme scroll only scrolls the main part, the outer scroll can carry the header and footer together',
|
||||
wrapper: 'Wrapper',
|
||||
content: 'Content'
|
||||
},
|
||||
|
@ -62,7 +62,8 @@ const local: App.I18n.Schema = {
|
||||
tabs: {
|
||||
appearance: '外观',
|
||||
layout: '布局',
|
||||
general: '通用'
|
||||
general: '通用',
|
||||
preset: '预设'
|
||||
},
|
||||
appearance: {
|
||||
themeSchema: {
|
||||
@ -83,7 +84,28 @@ const local: App.I18n.Schema = {
|
||||
followPrimary: '跟随主色'
|
||||
},
|
||||
recommendColor: '应用推荐算法的颜色',
|
||||
recommendColorDesc: '推荐颜色的算法参照'
|
||||
recommendColorDesc: '推荐颜色的算法参照',
|
||||
preset: {
|
||||
title: '主题预设',
|
||||
apply: '应用',
|
||||
applySuccess: '预设应用成功',
|
||||
default: {
|
||||
name: '默认预设',
|
||||
desc: 'Soybean 默认主题预设'
|
||||
},
|
||||
dark: {
|
||||
name: '暗色预设',
|
||||
desc: '适用于夜间使用的暗色主题预设'
|
||||
},
|
||||
compact: {
|
||||
name: '紧凑型',
|
||||
desc: '适用于小屏幕的紧凑布局预设'
|
||||
},
|
||||
azir: {
|
||||
name: 'Azir的预设',
|
||||
desc: '是 Azir 比较喜欢的莫兰迪色系冷淡风'
|
||||
}
|
||||
}
|
||||
},
|
||||
layout: {
|
||||
layoutMode: {
|
||||
@ -106,6 +128,7 @@ const local: App.I18n.Schema = {
|
||||
title: '标签栏设置',
|
||||
visible: '显示标签栏',
|
||||
cache: '标签栏信息缓存',
|
||||
cacheTip: '一键开启/关闭全局 keepalive',
|
||||
height: '标签栏高度',
|
||||
mode: {
|
||||
title: '标签栏风格',
|
||||
@ -141,6 +164,7 @@ const local: App.I18n.Schema = {
|
||||
title: '内容区域设置',
|
||||
scrollMode: {
|
||||
title: '滚动模式',
|
||||
tip: '主题滚动仅 main 部分滚动,外层滚动可携带头部底部一起滚动',
|
||||
wrapper: '外层滚动',
|
||||
content: '主体滚动'
|
||||
},
|
||||
|
90
src/theme/preset/azir.json
Normal file
90
src/theme/preset/azir.json
Normal file
@ -0,0 +1,90 @@
|
||||
{
|
||||
"name": "Azir's Preset",
|
||||
"desc": "It is a cold and elegant preset that Azir likes",
|
||||
"i18nkey": "theme.appearance.preset.azir",
|
||||
"version": "1.0.0",
|
||||
"themeScheme": "light",
|
||||
"grayscale": false,
|
||||
"colourWeakness": false,
|
||||
"recommendColor": true,
|
||||
"themeColor": "#78a878",
|
||||
"otherColor": {
|
||||
"info": "#89b989",
|
||||
"success": "#99c299",
|
||||
"warning": "#d4bb9d",
|
||||
"error": "#c49a9a"
|
||||
},
|
||||
"isInfoFollowPrimary": true,
|
||||
"resetCacheStrategy": "refresh",
|
||||
"layout": {
|
||||
"mode": "vertical-mix",
|
||||
"scrollMode": "wrapper"
|
||||
},
|
||||
"page": {
|
||||
"animate": true,
|
||||
"animateMode": "zoom-fade"
|
||||
},
|
||||
"header": {
|
||||
"height": 64,
|
||||
"breadcrumb": {
|
||||
"visible": true,
|
||||
"showIcon": true
|
||||
},
|
||||
"multilingual": {
|
||||
"visible": true
|
||||
},
|
||||
"globalSearch": {
|
||||
"visible": true
|
||||
}
|
||||
},
|
||||
"tab": {
|
||||
"visible": true,
|
||||
"cache": true,
|
||||
"height": 48,
|
||||
"mode": "chrome"
|
||||
},
|
||||
"fixedHeaderAndTab": true,
|
||||
"sider": {
|
||||
"inverted": false,
|
||||
"width": 220,
|
||||
"collapsedWidth": 64,
|
||||
"mixWidth": 90,
|
||||
"mixCollapsedWidth": 64,
|
||||
"mixChildMenuWidth": 200
|
||||
},
|
||||
"footer": {
|
||||
"visible": true,
|
||||
"fixed": true,
|
||||
"height": 56,
|
||||
"right": true
|
||||
},
|
||||
"watermark": {
|
||||
"visible": false,
|
||||
"text": "SoybeanAdmin",
|
||||
"enableUserName": false,
|
||||
"enableTime": true,
|
||||
"timeFormat": "YYYY-MM-DD HH:mm:ss"
|
||||
},
|
||||
"tokens": {
|
||||
"light": {
|
||||
"colors": {
|
||||
"container": "rgb(255, 255, 255)",
|
||||
"layout": "rgb(247, 250, 252)",
|
||||
"inverted": "rgb(0, 20, 40)",
|
||||
"base-text": "rgb(31, 31, 31)"
|
||||
},
|
||||
"boxShadow": {
|
||||
"header": "0 1px 2px rgb(0, 21, 41, 0.08)",
|
||||
"sider": "2px 0 8px 0 rgb(29, 35, 41, 0.05)",
|
||||
"tab": "0 1px 2px rgb(0, 21, 41, 0.08)"
|
||||
}
|
||||
},
|
||||
"dark": {
|
||||
"colors": {
|
||||
"container": "rgb(28, 28, 28)",
|
||||
"layout": "rgb(18, 18, 18)",
|
||||
"base-text": "rgb(224, 224, 224)"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
90
src/theme/preset/compact.json
Normal file
90
src/theme/preset/compact.json
Normal file
@ -0,0 +1,90 @@
|
||||
{
|
||||
"name": "Compact Preset",
|
||||
"desc": "Compact layout preset for small screens",
|
||||
"i18nkey": "theme.appearance.preset.compact",
|
||||
"version": "1.0.0",
|
||||
"themeScheme": "light",
|
||||
"grayscale": false,
|
||||
"colourWeakness": false,
|
||||
"recommendColor": false,
|
||||
"themeColor": "#646cff",
|
||||
"otherColor": {
|
||||
"info": "#2080f0",
|
||||
"success": "#52c41a",
|
||||
"warning": "#faad14",
|
||||
"error": "#f5222d"
|
||||
},
|
||||
"isInfoFollowPrimary": true,
|
||||
"resetCacheStrategy": "close",
|
||||
"layout": {
|
||||
"mode": "vertical",
|
||||
"scrollMode": "content"
|
||||
},
|
||||
"page": {
|
||||
"animate": true,
|
||||
"animateMode": "fade-slide"
|
||||
},
|
||||
"header": {
|
||||
"height": 48,
|
||||
"breadcrumb": {
|
||||
"visible": true,
|
||||
"showIcon": true
|
||||
},
|
||||
"multilingual": {
|
||||
"visible": false
|
||||
},
|
||||
"globalSearch": {
|
||||
"visible": false
|
||||
}
|
||||
},
|
||||
"tab": {
|
||||
"visible": true,
|
||||
"cache": true,
|
||||
"height": 36,
|
||||
"mode": "button"
|
||||
},
|
||||
"fixedHeaderAndTab": true,
|
||||
"sider": {
|
||||
"inverted": false,
|
||||
"width": 180,
|
||||
"collapsedWidth": 48,
|
||||
"mixWidth": 80,
|
||||
"mixCollapsedWidth": 48,
|
||||
"mixChildMenuWidth": 180
|
||||
},
|
||||
"footer": {
|
||||
"visible": false,
|
||||
"fixed": false,
|
||||
"height": 40,
|
||||
"right": true
|
||||
},
|
||||
"watermark": {
|
||||
"visible": false,
|
||||
"text": "SoybeanAdmin",
|
||||
"enableUserName": false,
|
||||
"enableTime": false,
|
||||
"timeFormat": "YYYY-MM-DD HH:mm"
|
||||
},
|
||||
"tokens": {
|
||||
"light": {
|
||||
"colors": {
|
||||
"container": "rgb(255, 255, 255)",
|
||||
"layout": "rgb(247, 250, 252)",
|
||||
"inverted": "rgb(0, 20, 40)",
|
||||
"base-text": "rgb(31, 31, 31)"
|
||||
},
|
||||
"boxShadow": {
|
||||
"header": "0 1px 2px rgb(0, 21, 41, 0.08)",
|
||||
"sider": "2px 0 8px 0 rgb(29, 35, 41, 0.05)",
|
||||
"tab": "0 1px 2px rgb(0, 21, 41, 0.08)"
|
||||
}
|
||||
},
|
||||
"dark": {
|
||||
"colors": {
|
||||
"container": "rgb(28, 28, 28)",
|
||||
"layout": "rgb(18, 18, 18)",
|
||||
"base-text": "rgb(224, 224, 224)"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
90
src/theme/preset/dark.json
Normal file
90
src/theme/preset/dark.json
Normal file
@ -0,0 +1,90 @@
|
||||
{
|
||||
"name": "Dark Preset",
|
||||
"desc": "Dark theme preset for night time usage",
|
||||
"i18nkey": "theme.appearance.preset.dark",
|
||||
"version": "1.0.0",
|
||||
"themeScheme": "dark",
|
||||
"grayscale": false,
|
||||
"colourWeakness": false,
|
||||
"recommendColor": false,
|
||||
"themeColor": "#409eff",
|
||||
"otherColor": {
|
||||
"info": "#2080f0",
|
||||
"success": "#52c41a",
|
||||
"warning": "#faad14",
|
||||
"error": "#f5222d"
|
||||
},
|
||||
"isInfoFollowPrimary": true,
|
||||
"resetCacheStrategy": "close",
|
||||
"layout": {
|
||||
"mode": "vertical",
|
||||
"scrollMode": "content"
|
||||
},
|
||||
"page": {
|
||||
"animate": true,
|
||||
"animateMode": "fade-slide"
|
||||
},
|
||||
"header": {
|
||||
"height": 56,
|
||||
"breadcrumb": {
|
||||
"visible": true,
|
||||
"showIcon": true
|
||||
},
|
||||
"multilingual": {
|
||||
"visible": true
|
||||
},
|
||||
"globalSearch": {
|
||||
"visible": true
|
||||
}
|
||||
},
|
||||
"tab": {
|
||||
"visible": true,
|
||||
"cache": true,
|
||||
"height": 44,
|
||||
"mode": "chrome"
|
||||
},
|
||||
"fixedHeaderAndTab": true,
|
||||
"sider": {
|
||||
"inverted": true,
|
||||
"width": 220,
|
||||
"collapsedWidth": 64,
|
||||
"mixWidth": 90,
|
||||
"mixCollapsedWidth": 64,
|
||||
"mixChildMenuWidth": 200
|
||||
},
|
||||
"footer": {
|
||||
"visible": true,
|
||||
"fixed": false,
|
||||
"height": 48,
|
||||
"right": true
|
||||
},
|
||||
"watermark": {
|
||||
"visible": false,
|
||||
"text": "SoybeanAdmin",
|
||||
"enableUserName": false,
|
||||
"enableTime": false,
|
||||
"timeFormat": "YYYY-MM-DD HH:mm"
|
||||
},
|
||||
"tokens": {
|
||||
"light": {
|
||||
"colors": {
|
||||
"container": "rgb(255, 255, 255)",
|
||||
"layout": "rgb(247, 250, 252)",
|
||||
"inverted": "rgb(0, 20, 40)",
|
||||
"base-text": "rgb(31, 31, 31)"
|
||||
},
|
||||
"boxShadow": {
|
||||
"header": "0 1px 2px rgb(0, 21, 41, 0.08)",
|
||||
"sider": "2px 0 8px 0 rgb(29, 35, 41, 0.05)",
|
||||
"tab": "0 1px 2px rgb(0, 21, 41, 0.08)"
|
||||
}
|
||||
},
|
||||
"dark": {
|
||||
"colors": {
|
||||
"container": "rgb(28, 28, 28)",
|
||||
"layout": "rgb(18, 18, 18)",
|
||||
"base-text": "rgb(224, 224, 224)"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
90
src/theme/preset/default.json
Normal file
90
src/theme/preset/default.json
Normal file
@ -0,0 +1,90 @@
|
||||
{
|
||||
"name": "default",
|
||||
"desc": "Default theme preset with balanced settings",
|
||||
"i18nkey": "theme.appearance.preset.default",
|
||||
"version": "1.0.0",
|
||||
"themeScheme": "light",
|
||||
"grayscale": false,
|
||||
"colourWeakness": false,
|
||||
"recommendColor": false,
|
||||
"themeColor": "#646cff",
|
||||
"otherColor": {
|
||||
"info": "#2080f0",
|
||||
"success": "#52c41a",
|
||||
"warning": "#faad14",
|
||||
"error": "#f5222d"
|
||||
},
|
||||
"isInfoFollowPrimary": true,
|
||||
"resetCacheStrategy": "close",
|
||||
"layout": {
|
||||
"mode": "vertical",
|
||||
"scrollMode": "content"
|
||||
},
|
||||
"page": {
|
||||
"animate": true,
|
||||
"animateMode": "fade-slide"
|
||||
},
|
||||
"header": {
|
||||
"height": 56,
|
||||
"breadcrumb": {
|
||||
"visible": true,
|
||||
"showIcon": true
|
||||
},
|
||||
"multilingual": {
|
||||
"visible": true
|
||||
},
|
||||
"globalSearch": {
|
||||
"visible": true
|
||||
}
|
||||
},
|
||||
"tab": {
|
||||
"visible": true,
|
||||
"cache": true,
|
||||
"height": 44,
|
||||
"mode": "chrome"
|
||||
},
|
||||
"fixedHeaderAndTab": true,
|
||||
"sider": {
|
||||
"inverted": false,
|
||||
"width": 220,
|
||||
"collapsedWidth": 64,
|
||||
"mixWidth": 90,
|
||||
"mixCollapsedWidth": 64,
|
||||
"mixChildMenuWidth": 200
|
||||
},
|
||||
"footer": {
|
||||
"visible": true,
|
||||
"fixed": false,
|
||||
"height": 48,
|
||||
"right": true
|
||||
},
|
||||
"watermark": {
|
||||
"visible": false,
|
||||
"text": "SoybeanAdmin",
|
||||
"enableUserName": false,
|
||||
"enableTime": false,
|
||||
"timeFormat": "YYYY-MM-DD HH:mm"
|
||||
},
|
||||
"tokens": {
|
||||
"light": {
|
||||
"colors": {
|
||||
"container": "rgb(255, 255, 255)",
|
||||
"layout": "rgb(247, 250, 252)",
|
||||
"inverted": "rgb(0, 20, 40)",
|
||||
"base-text": "rgb(31, 31, 31)"
|
||||
},
|
||||
"boxShadow": {
|
||||
"header": "0 1px 2px rgb(0, 21, 41, 0.08)",
|
||||
"sider": "2px 0 8px 0 rgb(29, 35, 41, 0.05)",
|
||||
"tab": "0 1px 2px rgb(0, 21, 41, 0.08)"
|
||||
}
|
||||
},
|
||||
"dark": {
|
||||
"colors": {
|
||||
"container": "rgb(28, 28, 28)",
|
||||
"layout": "rgb(18, 18, 18)",
|
||||
"base-text": "rgb(224, 224, 224)"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -12,7 +12,7 @@ export const themeSettings: App.Theme.ThemeSetting = {
|
||||
error: '#f5222d'
|
||||
},
|
||||
isInfoFollowPrimary: true,
|
||||
resetCacheStrategy: 'close',
|
||||
resetCacheStrategy: 'refresh',
|
||||
layout: {
|
||||
mode: 'vertical',
|
||||
scrollMode: 'content'
|
||||
|
15
src/typings/app.d.ts
vendored
15
src/typings/app.d.ts
vendored
@ -367,6 +367,7 @@ declare namespace App {
|
||||
appearance: string;
|
||||
layout: string;
|
||||
general: string;
|
||||
preset: string;
|
||||
};
|
||||
appearance: {
|
||||
themeSchema: { title: string } & Record<UnionKey.ThemeScheme, string>;
|
||||
@ -378,6 +379,17 @@ declare namespace App {
|
||||
} & Theme.ThemeColor;
|
||||
recommendColor: string;
|
||||
recommendColorDesc: string;
|
||||
preset: {
|
||||
title: string;
|
||||
apply: string;
|
||||
applySuccess: string;
|
||||
[key: string]:
|
||||
| {
|
||||
name: string;
|
||||
desc: string;
|
||||
}
|
||||
| string;
|
||||
};
|
||||
};
|
||||
layout: {
|
||||
layoutMode: { title: string } & Record<UnionKey.ThemeLayoutMode, string> & {
|
||||
@ -387,6 +399,7 @@ declare namespace App {
|
||||
title: string;
|
||||
visible: string;
|
||||
cache: string;
|
||||
cacheTip: string;
|
||||
height: string;
|
||||
mode: { title: string } & Record<UnionKey.ThemeTabMode, string>;
|
||||
};
|
||||
@ -416,7 +429,7 @@ declare namespace App {
|
||||
};
|
||||
content: {
|
||||
title: string;
|
||||
scrollMode: { title: string } & Record<UnionKey.ThemeScrollMode, string>;
|
||||
scrollMode: { title: string; tip: string } & Record<UnionKey.ThemeScrollMode, string>;
|
||||
page: {
|
||||
animate: string;
|
||||
mode: { title: string } & Record<UnionKey.ThemePageAnimateMode, string>;
|
||||
|
13
src/typings/components.d.ts
vendored
13
src/typings/components.d.ts
vendored
@ -38,6 +38,8 @@ declare module 'vue' {
|
||||
IconIcRoundRemove: typeof import('~icons/ic/round-remove')['default']
|
||||
IconIcRoundSearch: typeof import('~icons/ic/round-search')['default']
|
||||
IconLocalActivity: typeof import('~icons/local/activity')['default']
|
||||
IconGridiconsFullscreen: typeof import('~icons/gridicons/fullscreen')['default']
|
||||
IconGridiconsFullscreenExit: typeof import('~icons/gridicons/fullscreen-exit')['default']
|
||||
IconLocalBanner: typeof import('~icons/local/banner')['default']
|
||||
IconLocalCast: typeof import('~icons/local/cast')['default']
|
||||
IconLocalLogo: typeof import('~icons/local/logo')['default']
|
||||
@ -45,17 +47,18 @@ declare module 'vue' {
|
||||
'IconMdi:printer': typeof import('~icons/mdi/printer')['default']
|
||||
IconMdiArrowDownThin: typeof import('~icons/mdi/arrow-down-thin')['default']
|
||||
IconMdiArrowUpThin: typeof import('~icons/mdi/arrow-up-thin')['default']
|
||||
IconMdiDrag: typeof import('~icons/mdi/drag')['default']
|
||||
IconMdiKeyboardEsc: typeof import('~icons/mdi/keyboard-esc')['default']
|
||||
IconMdiKeyboardReturn: typeof import('~icons/mdi/keyboard-return')['default']
|
||||
IconMdiRefresh: typeof import('~icons/mdi/refresh')['default']
|
||||
'IconMingcute:zoomInLine': typeof import('~icons/mingcute/zoom-in-line')['default']
|
||||
'IconMingcute:zoomOutLine': typeof import('~icons/mingcute/zoom-out-line')['default']
|
||||
IconTooltip: typeof import('./../components/common/icon-tooltip.vue')['default']
|
||||
IconUilSearch: typeof import('~icons/uil/search')['default']
|
||||
LangSwitch: typeof import('./../components/common/lang-switch.vue')['default']
|
||||
LookForward: typeof import('./../components/custom/look-forward.vue')['default']
|
||||
MenuToggler: typeof import('./../components/common/menu-toggler.vue')['default']
|
||||
NAlert: typeof import('naive-ui')['NAlert']
|
||||
NBadge: typeof import('naive-ui')['NBadge']
|
||||
NBreadcrumb: typeof import('naive-ui')['NBreadcrumb']
|
||||
NBreadcrumbItem: typeof import('naive-ui')['NBreadcrumbItem']
|
||||
NButton: typeof import('naive-ui')['NButton']
|
||||
@ -65,20 +68,15 @@ declare module 'vue' {
|
||||
NCollapse: typeof import('naive-ui')['NCollapse']
|
||||
NCollapseItem: typeof import('naive-ui')['NCollapseItem']
|
||||
NColorPicker: typeof import('naive-ui')['NColorPicker']
|
||||
NDataTable: typeof import('naive-ui')['NDataTable']
|
||||
NDescriptions: typeof import('naive-ui')['NDescriptions']
|
||||
NDescriptionsItem: typeof import('naive-ui')['NDescriptionsItem']
|
||||
NDialogProvider: typeof import('naive-ui')['NDialogProvider']
|
||||
NDivider: typeof import('naive-ui')['NDivider']
|
||||
NDrawer: typeof import('naive-ui')['NDrawer']
|
||||
NDrawerContent: typeof import('naive-ui')['NDrawerContent']
|
||||
NDropdown: typeof import('naive-ui')['NDropdown']
|
||||
NDynamicInput: typeof import('naive-ui')['NDynamicInput']
|
||||
NEmpty: typeof import('naive-ui')['NEmpty']
|
||||
NFlex: typeof import('naive-ui')['NFlex']
|
||||
NForm: typeof import('naive-ui')['NForm']
|
||||
NFormItem: typeof import('naive-ui')['NFormItem']
|
||||
NFormItemGi: typeof import('naive-ui')['NFormItemGi']
|
||||
NGi: typeof import('naive-ui')['NGi']
|
||||
NGrid: typeof import('naive-ui')['NGrid']
|
||||
NInput: typeof import('naive-ui')['NInput']
|
||||
@ -94,8 +92,6 @@ declare module 'vue' {
|
||||
NPagination: typeof import('naive-ui')['NPagination']
|
||||
NPopconfirm: typeof import('naive-ui')['NPopconfirm']
|
||||
NPopover: typeof import('naive-ui')['NPopover']
|
||||
NRadio: typeof import('naive-ui')['NRadio']
|
||||
NRadioGroup: typeof import('naive-ui')['NRadioGroup']
|
||||
NScrollbar: typeof import('naive-ui')['NScrollbar']
|
||||
NSelect: typeof import('naive-ui')['NSelect']
|
||||
NSkeleton: typeof import('naive-ui')['NSkeleton']
|
||||
@ -111,7 +107,6 @@ declare module 'vue' {
|
||||
NTag: typeof import('naive-ui')['NTag']
|
||||
NThing: typeof import('naive-ui')['NThing']
|
||||
NTooltip: typeof import('naive-ui')['NTooltip']
|
||||
NTree: typeof import('naive-ui')['NTree']
|
||||
NWatermark: typeof import('naive-ui')['NWatermark']
|
||||
PinToggler: typeof import('./../components/common/pin-toggler.vue')['default']
|
||||
ProCard: typeof import('pro-naive-ui')['ProCard']
|
||||
|
Loading…
Reference in New Issue
Block a user