feat: 主题设置页面

This commit is contained in:
wang_fan_w 2024-05-04 23:36:38 +08:00
parent e3773fd8b5
commit bf7eaf4ae6
9 changed files with 288 additions and 38 deletions

View File

@ -1,5 +1,5 @@
<template>
<a-drawer :width="340" :visible="props.systemOpen" @ok="handleOk" @cancel="handleCancel" unmount-on-close>
<a-drawer :width="340" :visible="props.systemOpen" @ok="handleCancel" @cancel="handleCancel" unmount-on-close>
<template #title> 系统设置 </template>
<div>
<div>
@ -8,6 +8,10 @@
<div>菜单折叠</div>
<a-switch v-model="collapsed" />
</div>
<div class="flex-row">
<div>菜单手风琴</div>
<a-switch v-model="isAccordion" />
</div>
<div class="flex-row">
<div>面包屑</div>
<a-switch v-model="isBreadcrumb" />
@ -25,23 +29,23 @@
<div class="title">水印设置</div>
<div class="flex-row">
<div>水印颜色</div>
<a-switch />
<a-color-picker v-model="watermarkStyle.color" format="rgb" :history-colors="['#00000026']" />
</div>
<div class="flex-row">
<div>水印文案</div>
<a-input :style="{ width: '100px' }" default-value="content" placeholder="请输入" allow-clear />
<a-input :style="{ width: '100px' }" v-model="watermark" placeholder="请输入" allow-clear />
</div>
<div class="flex-row">
<div>水印大小</div>
<a-slider :default-value="50" :style="{ width: '100px' }" />
<a-slider v-model="watermarkStyle.fontSize" :min="10" :max="50" :style="{ width: '100px' }" />
</div>
<div class="flex-row">
<div>水印角度</div>
<a-slider :default-value="50" :style="{ width: '100px' }" />
<a-slider v-model="watermarkRotate" :min="0" :max="360" :style="{ width: '100px' }" />
</div>
<div class="flex-row">
<div>水印间隙</div>
<a-slider :default-value="50" :style="{ width: '100px' }" />
<a-slider :default-value="gapInfo[0]" :min="0" :max="300" :style="{ width: '100px' }" @change="onWatermarkGap" />
</div>
</div>
</div>
@ -55,7 +59,8 @@ import { useThemeConfig } from "@/store/modules/theme-config";
import { currentlyRoute } from "@/router/route-output";
const themeStore = useThemeConfig();
const routerStore = useRoutesListStore();
const { collapsed, isBreadcrumb, isTabs, isFooter } = storeToRefs(themeStore);
const { collapsed, isAccordion, isBreadcrumb, isTabs, isFooter, watermark, watermarkStyle, watermarkRotate, watermarkGap } =
storeToRefs(themeStore);
const { tabsList, cacheRoutes } = storeToRefs(routerStore);
const route = useRoute();
const props = defineProps({
@ -64,9 +69,17 @@ const props = defineProps({
default: false
}
});
// tabs
// tabs
// tabs
const gapInfo = ref(watermarkGap.value);
const onWatermarkGap = (e: number) => {
watermarkGap.value = watermarkGap.value.map(() => e);
};
/*
是否关闭tabs栏
如果关闭那么所有tabs全部取消所有页面缓存全部取消
如果开启那么添加当前路由到tabs
*/
const tabsChange = (e: Boolean) => {
if (!e) {
tabsList.value = [];
@ -76,9 +89,6 @@ const tabsChange = (e: Boolean) => {
}
};
const emits = defineEmits(["systemCancel"]);
const handleOk = () => {
emits("systemCancel");
};
const handleCancel = () => {
emits("systemCancel");
};

View File

@ -0,0 +1,176 @@
<template>
<a-drawer :width="340" :visible="props.themeOpen" @ok="handleCancel" @cancel="handleCancel" unmount-on-close>
<template #title> 主题设置 </template>
<div>
<div>
<a-divider orientation="center">导航模式</a-divider>
<div class="flex-common">
<a-tooltip v-for="item in layoutList" :key="item.value" :content="item.label" position="top" mini>
<div
:class="layoutType === item.value ? `current-layout ${item.class}` : item.class"
@click="layouetChange(item.value)"
>
<icon-check-circle-fill class="layout-icon" />
</div>
</a-tooltip>
</div>
</div>
<div>
<a-divider orientation="center">主题设置</a-divider>
<div class="flex-row">
<div>主题色</div>
<a-switch />
</div>
<div class="flex-row">
<div>色弱模式</div>
<a-switch />
</div>
<div class="flex-row">
<div>灰色模式</div>
<a-switch />
</div>
<div class="flex-row">
<div>侧边栏深色</div>
<a-switch />
</div>
</div>
</div>
</a-drawer>
</template>
<script setup lang="ts">
import { storeToRefs } from "pinia";
import { useThemeConfig } from "@/store/modules/theme-config";
const themeStore = useThemeConfig();
const { layoutType } = storeToRefs(themeStore);
const layoutList = reactive({
layoutDefaults: {
value: "layoutDefaults",
label: "默认布局",
class: "layout-defaults"
},
layoutHead: {
value: "layoutHead",
label: "横向布局",
class: "layout-head"
},
layoutMixing: {
value: "layoutMixing",
label: "混合布局",
class: "layout-mixing"
}
});
const layouetChange = (type: string) => {
layoutType.value = type;
};
const props = defineProps({
themeOpen: {
type: Boolean,
default: false
}
});
const emits = defineEmits(["themeCancel"]);
const handleCancel = () => {
emits("themeCancel");
};
</script>
<style lang="scss" scoped>
.box-gap {
margin-top: $margin;
}
.flex-row {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: $margin;
}
.flex-common {
display: flex;
align-items: center;
justify-content: center;
margin-bottom: $margin;
}
.layout-defaults,
.layout-head,
.layout-mixing {
width: 70px;
height: 50px;
margin-right: $margin;
background: $color-fill-1;
border-radius: $radius-box;
overflow: hidden;
box-shadow: $shadow-special;
.layout-icon {
display: none;
}
}
.current-layout {
position: relative;
.layout-icon {
display: block;
position: absolute;
right: 2px;
bottom: 2px;
font-size: $font-size-body-3;
color: $color-primary;
}
}
.layout-defaults {
position: relative;
&::before {
content: "";
position: absolute;
left: 0;
width: 20px;
height: 100%;
background: #232324;
}
&::after {
content: "";
position: absolute;
right: 0;
top: 0;
width: 50px;
height: 15px;
background: #fff;
}
}
.layout-head {
position: relative;
&::before {
content: "";
position: absolute;
top: 0;
width: 100%;
height: 15px;
background: #232324;
}
}
.layout-mixing {
position: relative;
&::before {
content: "";
position: absolute;
top: 0;
width: 100%;
height: 15px;
background: #232324;
}
&::after {
content: "";
position: absolute;
left: 0;
top: 15px;
width: 20px;
height: calc(100% - 15px);
background: #fff;
}
}
</style>

View File

@ -63,7 +63,7 @@
</a-tooltip>
<!-- 主题设置 -->
<a-tooltip :content="$t(`language.theme-settings`)">
<a-button size="mini" type="text" class="icon_btn">
<a-button size="mini" type="text" class="icon_btn" @click="onThemeSetting">
<template #icon>
<icon-skin :size="18" />
</template>
@ -112,12 +112,14 @@
</a-dropdown>
</div>
<SystemSettings :system-open="systemOpen" @system-cancel="systemOpen = false" />
<ThemeSettings :theme-open="themeOpen" @theme-cancel="themeOpen = false" />
</a-layout-header>
</template>
<script setup lang="ts">
import Notice from "@/layout/components/Header/components/notice/index.vue";
import Breadcrumb from "@/layout/components/Header/components/breadcrumb/index.vue";
import SystemSettings from "@/layout/components/Header/components/system-settings/index.vue";
import ThemeSettings from "@/layout/components/Header/components/theme-settings/index.vue";
import myImage from "@/assets/img/my-image.jpg";
import { useI18n } from "vue-i18n";
import { Modal } from "@arco-design/web-vue";
@ -140,6 +142,12 @@ const systemOpen = ref(false);
const onSystemSetting = () => {
systemOpen.value = true;
};
//
const themeOpen = ref(false);
const onThemeSetting = () => {
themeOpen.value = true;
};
//
const fullScreen = ref(true);
const onFullScreen = () => {

View File

@ -1,18 +1,20 @@
<template>
<a-layout-content :class="isFooter ? 'content' : 'content-no-footer'">
<Tabs v-if="isTabs" />
<a-scrollbar style="height: 100%; overflow: auto" :outer-class="isTabs ? 'scrollbar' : 'scrollbar-no-tabs'">
<div class="main">
<router-view v-slot="{ Component, route }">
<MainTransition>
<keep-alive :include="cacheRoutes">
<component :is="Component" :key="route.name" v-if="refreshPage" />
</keep-alive>
</MainTransition>
</router-view>
</div>
</a-scrollbar>
</a-layout-content>
<a-watermark :content="watermark" v-bind="watermarkConfig">
<a-layout-content :class="isFooter ? 'content' : 'content-no-footer'">
<Tabs v-if="isTabs" />
<a-scrollbar style="height: 100%; overflow: auto" :outer-class="isTabs ? 'scrollbar' : 'scrollbar-no-tabs'">
<div class="main">
<router-view v-slot="{ Component, route }">
<MainTransition>
<keep-alive :include="cacheRoutes">
<component :is="Component" :key="route.name" v-if="refreshPage" />
</keep-alive>
</MainTransition>
</router-view>
</div>
</a-scrollbar>
</a-layout-content>
</a-watermark>
</template>
<script setup lang="ts">
@ -21,9 +23,22 @@ import { storeToRefs } from "pinia";
import { useThemeConfig } from "@/store/modules/theme-config";
import { useRoutesListStore } from "@/store/modules/route-list";
const themeStore = useThemeConfig();
let { refreshPage, isTabs, isFooter } = storeToRefs(themeStore);
let { refreshPage, isTabs, isFooter, watermark, watermarkStyle, watermarkRotate, watermarkGap } = storeToRefs(themeStore);
const routerStore = useRoutesListStore();
const { cacheRoutes } = storeToRefs(routerStore);
//
const watermarkConfig = computed(() => {
return {
font: watermarkStyle.value,
rotate: watermarkRotate.value,
gap: watermarkGap.value
};
});
watch(watermarkConfig, newv => {
console.log(newv);
});
</script>
<style lang="scss" scoped>

View File

@ -2,8 +2,9 @@
<a-menu
breakpoint="xl"
:collapsed="collapsed"
:auto-scroll-into-view="true"
:auto-open-selected="true"
:accordion="true"
:accordion="isAccordion"
:selected-keys="[currentRoute.name]"
@menu-item-click="onMenuItem"
>
@ -22,7 +23,7 @@ const router = useRouter();
const routerStore = useRoutesListStore();
const { routeTree, currentRoute } = storeToRefs(routerStore);
const themeStore = useThemeConfig();
const { collapsed } = storeToRefs(themeStore);
const { collapsed, isAccordion } = storeToRefs(themeStore);
/**
* @description 菜单点击事件

View File

@ -1,17 +1,24 @@
<template>
<div>
<LangProvider>
<component :is="layouts['defaults']" />
<component :is="layouts[layoutType]" />
</LangProvider>
</div>
</template>
<script setup lang="ts">
import { storeToRefs } from "pinia";
import { useThemeConfig } from "@/store/modules/theme-config";
import { loadingPage } from "@/utils/loading-page";
const themeStore = useThemeConfig();
const { layoutType } = storeToRefs(themeStore);
// -
const layouts = {
defaults: defineAsyncComponent(() => import("@/layout/layout-defaults/index.vue")),
mixing: defineAsyncComponent(() => import("@/layout/layout-mixing/index.vue"))
const layouts: any = {
layoutDefaults: defineAsyncComponent(() => import("@/layout/layout-defaults/index.vue")),
layoutHead: defineAsyncComponent(() => import("@/layout/layout-head/index.vue")),
layoutMixing: defineAsyncComponent(() => import("@/layout/layout-mixing/index.vue"))
};
onMounted(() => {

View File

@ -0,0 +1,7 @@
<template>
<div>头部布局</div>
</template>
<script setup lang="ts"></script>
<style lang="scss" scoped></style>

View File

@ -215,7 +215,7 @@ export const dynamicRoutes: RouteRecordRaw[] = [
title: "about-project",
hide: false,
keepAlive: true,
affix: true,
affix: false,
link: "",
iframe: false,
roles: ["admin", "common"],

View File

@ -1,5 +1,22 @@
import { defineStore } from "pinia";
import persistedstateConfig from "@/store/config/index";
interface ThemeConfig {
collapsed: Boolean;
refreshPage: Boolean;
language: string;
darkMode: Boolean;
isAccordion: Boolean;
isBreadcrumb: Boolean;
isTabs: Boolean;
isFooter: Boolean;
watermark: string;
watermarkStyle: any;
watermarkRotate: number;
watermarkGap: Array<number>;
layoutType: string;
}
/**
*
* @methods setCollapsed
@ -7,14 +24,23 @@ import persistedstateConfig from "@/store/config/index";
* @methods setLanguage
*/
export const useThemeConfig = defineStore("theme-config", {
state: (): any => ({
state: (): ThemeConfig => ({
collapsed: false, // 是否折叠菜单
refreshPage: true, // 刷新页面
language: "zh-CN", // 系统语言
darkMode: false, // 黑暗模式
isAccordion: true, // 菜单手风琴
isBreadcrumb: true, // 面包屑渲染
isTabs: true, // 标签栏渲染
isFooter: true // 页脚渲染
isFooter: true, // 页脚渲染
watermark: "dc admin", // 水印
watermarkStyle: {
fontSize: 12,
color: "rgba(0, 0, 0, 0.15)"
}, // 水印风格
watermarkRotate: 330, // 水印角度
watermarkGap: [100, 100], // 水印间隙
layoutType: "layoutDefaults" // 布局模式layoutDefaults、layoutHead、layoutMixing
}),
actions: {
// 折叠菜单