mirror of
https://github.com/vastxie/99AI.git
synced 2026-04-23 18:54:25 +08:00
v4.3.0
This commit is contained in:
428
admin/src/layouts/components/AppSetting/index.vue
Executable file
428
admin/src/layouts/components/AppSetting/index.vue
Executable file
@@ -0,0 +1,428 @@
|
||||
<script setup lang="ts">
|
||||
import settingsDefault from '@/settings.default';
|
||||
import useMenuStore from '@/store/modules/menu';
|
||||
import useSettingsStore from '@/store/modules/settings';
|
||||
import eventBus from '@/utils/eventBus';
|
||||
import { useClipboard } from '@vueuse/core';
|
||||
|
||||
defineOptions({
|
||||
name: 'AppSetting',
|
||||
});
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
const settingsStore = useSettingsStore();
|
||||
const menuStore = useMenuStore();
|
||||
|
||||
const isShow = ref(false);
|
||||
|
||||
watch(
|
||||
() => settingsStore.settings.menu.menuMode,
|
||||
(value) => {
|
||||
if (value === 'single') {
|
||||
menuStore.setActived(0);
|
||||
} else {
|
||||
menuStore.setActived(route.fullPath);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
onMounted(() => {
|
||||
eventBus.on('global-app-setting-toggle', () => {
|
||||
isShow.value = !isShow.value;
|
||||
});
|
||||
});
|
||||
|
||||
const { copy, copied, isSupported } = useClipboard();
|
||||
|
||||
// watch(copied, (val) => {
|
||||
// if (val) {
|
||||
// Message.success('复制成功,请粘贴到 src/settings.ts 文件中!', {
|
||||
// zIndex: 2000,
|
||||
// })
|
||||
// }
|
||||
// })
|
||||
|
||||
function isObject(value: any) {
|
||||
return typeof value === 'object' && !Array.isArray(value);
|
||||
}
|
||||
// 比较两个对象,并提取出不同的部分
|
||||
function getObjectDiff(originalObj: Record<string, any>, diffObj: Record<string, any>) {
|
||||
if (!isObject(originalObj) || !isObject(diffObj)) {
|
||||
return diffObj;
|
||||
}
|
||||
const diff: Record<string, any> = {};
|
||||
for (const key in diffObj) {
|
||||
const originalValue = originalObj[key];
|
||||
const diffValue = diffObj[key];
|
||||
if (JSON.stringify(originalValue) !== JSON.stringify(diffValue)) {
|
||||
if (isObject(originalValue) && isObject(diffValue)) {
|
||||
const nestedDiff = getObjectDiff(originalValue, diffValue);
|
||||
if (Object.keys(nestedDiff).length > 0) {
|
||||
diff[key] = nestedDiff;
|
||||
}
|
||||
} else {
|
||||
diff[key] = diffValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
return diff;
|
||||
}
|
||||
|
||||
function handleCopy() {
|
||||
copy(JSON.stringify(getObjectDiff(settingsDefault, settingsStore.settings), null, 2));
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<HSlideover v-model="isShow" title="应用配置">
|
||||
<div class="rounded-2 bg-rose/20 px-4 py-2 text-sm/6 c-rose">
|
||||
<p class="my-1">
|
||||
应用配置可实时预览效果,但只是临时生效,要想真正应用于项目,可以点击下方的「复制配置」按钮,并将配置粘贴到
|
||||
src/settings.ts 文件中。
|
||||
</p>
|
||||
<p class="my-1">注意:在生产环境中应关闭该模块。</p>
|
||||
</div>
|
||||
<div class="divider">颜色主题风格</div>
|
||||
<div class="flex items-center justify-center pb-4">
|
||||
<HTabList
|
||||
v-model="settingsStore.settings.app.colorScheme"
|
||||
:options="[
|
||||
{ icon: 'i-ri:sun-line', label: '明亮', value: 'light' },
|
||||
{ icon: 'i-ri:moon-line', label: '暗黑', value: 'dark' },
|
||||
{ icon: 'i-codicon:color-mode', label: '系统', value: '' },
|
||||
]"
|
||||
class="w-60"
|
||||
/>
|
||||
</div>
|
||||
<div v-if="settingsStore.mode === 'pc'" class="divider">导航栏模式</div>
|
||||
<div v-if="settingsStore.mode === 'pc'" class="menu-mode">
|
||||
<HTooltip text="侧边栏模式 (含主导航)" placement="bottom" :delay="500">
|
||||
<div
|
||||
class="mode mode-side"
|
||||
:class="{ active: settingsStore.settings.menu.menuMode === 'side' }"
|
||||
@click="settingsStore.settings.menu.menuMode = 'side'"
|
||||
>
|
||||
<div class="mode-container" />
|
||||
</div>
|
||||
</HTooltip>
|
||||
<HTooltip text="顶部模式" placement="bottom" :delay="500">
|
||||
<div
|
||||
class="mode mode-head"
|
||||
:class="{ active: settingsStore.settings.menu.menuMode === 'head' }"
|
||||
@click="settingsStore.settings.menu.menuMode = 'head'"
|
||||
>
|
||||
<div class="mode-container" />
|
||||
</div>
|
||||
</HTooltip>
|
||||
<HTooltip text="侧边栏模式 (不含主导航)" placement="bottom" :delay="500">
|
||||
<div
|
||||
class="mode mode-single"
|
||||
:class="{ active: settingsStore.settings.menu.menuMode === 'single' }"
|
||||
@click="settingsStore.settings.menu.menuMode = 'single'"
|
||||
>
|
||||
<div class="mode-container" />
|
||||
</div>
|
||||
</HTooltip>
|
||||
</div>
|
||||
<div class="divider">导航栏</div>
|
||||
<div class="setting-item">
|
||||
<div class="label">
|
||||
主导航切换跳转
|
||||
<HTooltip text="开启该功能后,切换主导航时,页面自动跳转至该主导航下,次导航里第一个导航">
|
||||
<SvgIcon name="i-ri:question-line" />
|
||||
</HTooltip>
|
||||
</div>
|
||||
<HToggle
|
||||
v-model="settingsStore.settings.menu.switchMainMenuAndPageJump"
|
||||
:disabled="['single'].includes(settingsStore.settings.menu.menuMode)"
|
||||
/>
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<div class="label">
|
||||
次导航保持展开一个
|
||||
<HTooltip text="开启该功能后,次导航只保持单个菜单的展开">
|
||||
<SvgIcon name="i-ri:question-line" />
|
||||
</HTooltip>
|
||||
</div>
|
||||
<HToggle v-model="settingsStore.settings.menu.subMenuUniqueOpened" />
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<div class="label">次导航是否折叠</div>
|
||||
<HToggle v-model="settingsStore.settings.menu.subMenuCollapse" />
|
||||
</div>
|
||||
<div v-if="settingsStore.mode === 'pc'" class="setting-item">
|
||||
<div class="label">显示次导航折叠按钮</div>
|
||||
<HToggle v-model="settingsStore.settings.menu.enableSubMenuCollapseButton" />
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<div class="label">是否启用快捷键</div>
|
||||
<HToggle
|
||||
v-model="settingsStore.settings.menu.enableHotkeys"
|
||||
:disabled="['single'].includes(settingsStore.settings.menu.menuMode)"
|
||||
/>
|
||||
</div>
|
||||
<div class="divider">顶栏</div>
|
||||
<div class="setting-item">
|
||||
<div class="label">模式</div>
|
||||
<HCheckList
|
||||
v-model="settingsStore.settings.topbar.mode"
|
||||
:options="[
|
||||
{ label: '静止', value: 'static' },
|
||||
{ label: '固定', value: 'fixed' },
|
||||
{ label: '粘性', value: 'sticky' },
|
||||
]"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<div class="divider">标签栏</div>
|
||||
<div class="setting-item">
|
||||
<div class="label">是否启用</div>
|
||||
<HToggle v-model="settingsStore.settings.tabbar.enable" />
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<div class="label">是否显示图标</div>
|
||||
<HToggle
|
||||
v-model="settingsStore.settings.tabbar.enableIcon"
|
||||
:disabled="!settingsStore.settings.tabbar.enable"
|
||||
/>
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<div class="label">是否启用快捷键</div>
|
||||
<HToggle
|
||||
v-model="settingsStore.settings.tabbar.enableHotkeys"
|
||||
:disabled="!settingsStore.settings.tabbar.enable"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="divider">工具栏</div>
|
||||
<div v-if="settingsStore.mode === 'pc'" class="setting-item">
|
||||
<div class="label">面包屑导航</div>
|
||||
<HToggle v-model="settingsStore.settings.toolbar.breadcrumb" />
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<div class="label">
|
||||
导航搜索
|
||||
<HTooltip text="对导航进行快捷搜索">
|
||||
<SvgIcon name="i-ri:question-line" />
|
||||
</HTooltip>
|
||||
</div>
|
||||
<HToggle v-model="settingsStore.settings.toolbar.navSearch" />
|
||||
</div>
|
||||
<div v-if="settingsStore.mode === 'pc'" class="setting-item">
|
||||
<div class="label">全屏</div>
|
||||
<HToggle v-model="settingsStore.settings.toolbar.fullscreen" />
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<div class="label">
|
||||
页面刷新
|
||||
<HTooltip text="使用框架内提供的刷新功能进行页面刷新">
|
||||
<SvgIcon name="i-ri:question-line" />
|
||||
</HTooltip>
|
||||
</div>
|
||||
<HToggle v-model="settingsStore.settings.toolbar.pageReload" />
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<div class="label">
|
||||
颜色主题
|
||||
<HTooltip text="开启后可在明亮/暗黑模式中切换">
|
||||
<SvgIcon name="i-ri:question-line" />
|
||||
</HTooltip>
|
||||
</div>
|
||||
<HToggle v-model="settingsStore.settings.toolbar.colorScheme" />
|
||||
</div>
|
||||
<div class="divider">页面</div>
|
||||
<div class="setting-item">
|
||||
<div class="label">是否启用快捷键</div>
|
||||
<HToggle v-model="settingsStore.settings.mainPage.enableHotkeys" />
|
||||
</div>
|
||||
<div class="divider">导航搜索</div>
|
||||
<div class="setting-item">
|
||||
<div class="label">是否启用快捷键</div>
|
||||
<HToggle
|
||||
v-model="settingsStore.settings.navSearch.enableHotkeys"
|
||||
:disabled="!settingsStore.settings.toolbar.navSearch"
|
||||
/>
|
||||
</div>
|
||||
<div class="divider">底部版权</div>
|
||||
<div class="setting-item">
|
||||
<div class="label">是否启用</div>
|
||||
<HToggle v-model="settingsStore.settings.copyright.enable" />
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<div class="label">日期</div>
|
||||
<HInput
|
||||
v-model="settingsStore.settings.copyright.dates"
|
||||
:disabled="!settingsStore.settings.copyright.enable"
|
||||
/>
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<div class="label">公司</div>
|
||||
<HInput
|
||||
v-model="settingsStore.settings.copyright.company"
|
||||
:disabled="!settingsStore.settings.copyright.enable"
|
||||
/>
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<div class="label">网址</div>
|
||||
<HInput
|
||||
v-model="settingsStore.settings.copyright.website"
|
||||
:disabled="!settingsStore.settings.copyright.enable"
|
||||
/>
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<div class="label">备案</div>
|
||||
<HInput
|
||||
v-model="settingsStore.settings.copyright.beian"
|
||||
:disabled="!settingsStore.settings.copyright.enable"
|
||||
/>
|
||||
</div>
|
||||
<div class="divider">主页</div>
|
||||
<div class="setting-item">
|
||||
<div class="label">
|
||||
是否启用
|
||||
<HTooltip text="该功能开启时,登录成功默认进入主页,反之则默认进入导航栏里第一个导航页面">
|
||||
<SvgIcon name="i-ri:question-line" />
|
||||
</HTooltip>
|
||||
</div>
|
||||
<HToggle v-model="settingsStore.settings.home.enable" />
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<div class="label">
|
||||
主页名称
|
||||
<HTooltip text="开启国际化时,该设置无效">
|
||||
<SvgIcon name="i-ri:question-line" />
|
||||
</HTooltip>
|
||||
</div>
|
||||
<HInput v-model="settingsStore.settings.home.title" />
|
||||
</div>
|
||||
<div class="divider">其它</div>
|
||||
<div class="setting-item">
|
||||
<div class="label">是否启用权限</div>
|
||||
<HToggle v-model="settingsStore.settings.app.enablePermission" />
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<div class="label">
|
||||
载入进度条
|
||||
<HTooltip text="该功能开启时,跳转路由会看到页面顶部有进度条">
|
||||
<SvgIcon name="i-ri:question-line" />
|
||||
</HTooltip>
|
||||
</div>
|
||||
<HToggle v-model="settingsStore.settings.app.enableProgress" />
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<div class="label">
|
||||
动态标题
|
||||
<HTooltip
|
||||
text="该功能开启时,页面标题会显示当前路由标题,格式为“页面标题 - 网站名称”;关闭时则显示网站名称,网站名称在项目根目录下 .env.* 文件里配置"
|
||||
>
|
||||
<SvgIcon name="i-ri:question-line" />
|
||||
</HTooltip>
|
||||
</div>
|
||||
<HToggle v-model="settingsStore.settings.app.enableDynamicTitle" />
|
||||
</div>
|
||||
<template v-if="isSupported" #footer>
|
||||
<HButton block @click="handleCopy">
|
||||
<SvgIcon name="i-ep:document-copy" />
|
||||
复制配置
|
||||
</HButton>
|
||||
</template>
|
||||
</HSlideover>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.divider {
|
||||
--at-apply: flex items-center justify-between gap-4 my-4 whitespace-nowrap text-sm font-500;
|
||||
|
||||
&::before,
|
||||
&::after {
|
||||
--at-apply: content-empty w-full h-1px bg-stone-2 dark-bg-stone-6;
|
||||
}
|
||||
}
|
||||
|
||||
.menu-mode {
|
||||
--at-apply: flex items-center justify-center gap-4 pb-4;
|
||||
|
||||
.mode {
|
||||
--at-apply: relative w-16 h-12 rounded-2 ring-1 ring-stone-2 dark-ring-stone-7 cursor-pointer
|
||||
transition;
|
||||
|
||||
&.active {
|
||||
--at-apply: ring-ui-primary ring-2;
|
||||
}
|
||||
|
||||
&::before,
|
||||
&::after,
|
||||
.mode-container {
|
||||
--at-apply: absolute pointer-events-none;
|
||||
}
|
||||
|
||||
&::before {
|
||||
--at-apply: content-empty bg-ui-primary;
|
||||
}
|
||||
|
||||
&::after {
|
||||
--at-apply: content-empty bg-ui-primary/60;
|
||||
}
|
||||
|
||||
.mode-container {
|
||||
--at-apply: bg-ui-primary/20 border-dashed border-ui-primary;
|
||||
|
||||
&::before {
|
||||
--at-apply: content-empty absolute w-full h-full;
|
||||
}
|
||||
}
|
||||
|
||||
&-side {
|
||||
&::before {
|
||||
--at-apply: top-2 bottom-2 left-2 w-2 rounded-tl-1 rounded-bl-1;
|
||||
}
|
||||
|
||||
&::after {
|
||||
--at-apply: top-2 bottom-2 left-4.5 w-3;
|
||||
}
|
||||
|
||||
.mode-container {
|
||||
--at-apply: inset-t-2 inset-r-2 inset-b-2 inset-l-8 rounded-tr-1 rounded-br-1;
|
||||
}
|
||||
}
|
||||
|
||||
&-head {
|
||||
&::before {
|
||||
--at-apply: top-2 left-2 right-2 h-2 rounded-tl-1 rounded-tr-1;
|
||||
}
|
||||
|
||||
&::after {
|
||||
--at-apply: top-4.5 left-2 bottom-2 w-3 rounded-bl-1;
|
||||
}
|
||||
|
||||
.mode-container {
|
||||
--at-apply: inset-t-4.5 inset-r-2 inset-b-2 inset-l-5.5 rounded-br-1;
|
||||
}
|
||||
}
|
||||
|
||||
&-single {
|
||||
&::after {
|
||||
--at-apply: top-2 left-2 bottom-2 w-3 rounded-tl-1 rounded-bl-1;
|
||||
}
|
||||
|
||||
.mode-container {
|
||||
--at-apply: inset-t-2 inset-r-2 inset-b-2 inset-l-5.5 rounded-tr-1 rounded-br-1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.setting-item {
|
||||
--at-apply: flex items-center justify-between gap-4 px-4 py-2 rounded-2 transition
|
||||
hover-bg-stone-1 dark-hover-bg-stone-9;
|
||||
|
||||
.label {
|
||||
--at-apply: flex items-center flex-shrink-0 gap-2 text-sm;
|
||||
|
||||
i {
|
||||
--at-apply: text-xl text-orange cursor-help;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user