| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721 |
- <script setup lang="ts">
- import {
- ref,
- unref,
- watch,
- reactive,
- computed,
- nextTick,
- onUnmounted,
- onBeforeMount
- } from "vue";
- import { useI18n } from "vue-i18n";
- import { emitter } from "@/utils/mitt";
- import LayPanel from "../lay-panel/index.vue";
- import { useNav } from "@/layout/hooks/useNav";
- import { useAppStoreHook } from "@/store/modules/app";
- import { useMultiTagsStoreHook } from "@/store/modules/multiTags";
- import Segmented, { type OptionsType } from "@/components/ReSegmented";
- import { useDataThemeChange } from "@/layout/hooks/useDataThemeChange";
- import { useDark, useGlobal, debounce, isNumber } from "@pureadmin/utils";
- import Check from "~icons/ep/check";
- import LeftArrow from "~icons/ri/arrow-left-s-line?width=20&height=20";
- import RightArrow from "~icons/ri/arrow-right-s-line?width=20&height=20";
- import DayIcon from "@/assets/svg/day.svg?component";
- import DarkIcon from "@/assets/svg/dark.svg?component";
- import SystemIcon from "@/assets/svg/system.svg?component";
- const { t } = useI18n();
- const { device } = useNav();
- const { isDark } = useDark();
- const { $storage } = useGlobal<GlobalPropertiesApi>();
- const mixRef = ref();
- const verticalRef = ref();
- const horizontalRef = ref();
- const {
- dataTheme,
- overallStyle,
- layoutTheme,
- themeColors,
- toggleClass,
- dataThemeChange,
- setLayoutThemeColor
- } = useDataThemeChange();
- /* body添加layout属性,作用于src/style/sidebar.scss */
- if (unref(layoutTheme)) {
- const layout = unref(layoutTheme).layout;
- const theme = unref(layoutTheme).theme;
- document.documentElement.setAttribute("data-theme", theme);
- setLayoutModel(layout);
- }
- /** 默认灵动模式 */
- const markValue = ref($storage.configure?.showModel ?? "smart");
- const logoVal = ref($storage.configure?.showLogo ?? true);
- const settings = reactive({
- greyVal: $storage.configure.grey,
- weakVal: $storage.configure.weak,
- tabsVal: $storage.configure.hideTabs,
- showLogo: $storage.configure.showLogo,
- showModel: $storage.configure.showModel,
- hideFooter: $storage.configure.hideFooter,
- multiTagsCache: $storage.configure.multiTagsCache,
- stretch: $storage.configure.stretch
- });
- const getThemeColorStyle = computed(() => {
- return color => {
- return { background: color };
- };
- });
- /** 当网页整体为暗色风格时不显示亮白色主题配色切换选项 */
- const showThemeColors = computed(() => {
- return themeColor => {
- return themeColor === "light" && isDark.value ? false : true;
- };
- });
- function storageConfigureChange<T>(key: string, val: T): void {
- const storageConfigure = $storage.configure;
- storageConfigure[key] = val;
- $storage.configure = storageConfigure;
- }
- /** 灰色模式设置 */
- const greyChange = (value): void => {
- const htmlEl = document.querySelector("html");
- toggleClass(settings.greyVal, "html-grey", htmlEl);
- storageConfigureChange("grey", value);
- };
- /** 色弱模式设置 */
- const weekChange = (value): void => {
- const htmlEl = document.querySelector("html");
- toggleClass(settings.weakVal, "html-weakness", htmlEl);
- storageConfigureChange("weak", value);
- };
- /** 隐藏标签页设置 */
- const tagsChange = () => {
- const showVal = settings.tabsVal;
- storageConfigureChange("hideTabs", showVal);
- emitter.emit("tagViewsChange", showVal as unknown as string);
- };
- /** 隐藏页脚设置 */
- const hideFooterChange = () => {
- const hideFooter = settings.hideFooter;
- storageConfigureChange("hideFooter", hideFooter);
- };
- /** 标签页持久化设置 */
- const multiTagsCacheChange = () => {
- const multiTagsCache = settings.multiTagsCache;
- storageConfigureChange("multiTagsCache", multiTagsCache);
- useMultiTagsStoreHook().multiTagsCacheChange(multiTagsCache);
- };
- function onChange({ option }) {
- const { value } = option;
- markValue.value = value;
- storageConfigureChange("showModel", value);
- emitter.emit("tagViewsShowModel", value);
- }
- /** 侧边栏Logo */
- function logoChange() {
- unref(logoVal)
- ? storageConfigureChange("showLogo", true)
- : storageConfigureChange("showLogo", false);
- emitter.emit("logoChange", unref(logoVal));
- }
- function setFalse(Doms): any {
- Doms.forEach(v => {
- toggleClass(false, "is-select", unref(v));
- });
- }
- /** 页宽 */
- const stretchTypeOptions = computed<Array<OptionsType>>(() => {
- return [
- {
- label: t("panel.pureStretchFixed"),
- tip: t("panel.pureStretchFixedTip"),
- value: "fixed"
- },
- {
- label: t("panel.pureStretchCustom"),
- tip: t("panel.pureStretchCustomTip"),
- value: "custom"
- }
- ];
- });
- const setStretch = value => {
- settings.stretch = value;
- storageConfigureChange("stretch", value);
- };
- const stretchTypeChange = ({ option }) => {
- const { value } = option;
- value === "custom" ? setStretch(1440) : setStretch(false);
- };
- /** 主题色 激活选择项 */
- const getThemeColor = computed(() => {
- return current => {
- if (
- current === layoutTheme.value.theme &&
- layoutTheme.value.theme !== "light"
- ) {
- return "#fff";
- } else if (
- current === layoutTheme.value.theme &&
- layoutTheme.value.theme === "light"
- ) {
- return "#1d2b45";
- } else {
- return "transparent";
- }
- };
- });
- const pClass = computed(() => {
- return "panel-title";
- });
- const themeOptions = computed<Array<OptionsType>>(() => {
- return [
- {
- label: t("panel.pureOverallStyleLight"),
- icon: DayIcon,
- theme: "light",
- tip: t("panel.pureOverallStyleLightTip"),
- iconAttrs: { fill: isDark.value ? "#fff" : "#000" }
- },
- {
- label: t("panel.pureOverallStyleDark"),
- icon: DarkIcon,
- theme: "dark",
- tip: t("panel.pureOverallStyleDarkTip"),
- iconAttrs: { fill: isDark.value ? "#fff" : "#000" }
- },
- {
- label: t("panel.pureOverallStyleSystem"),
- icon: SystemIcon,
- theme: "system",
- tip: t("panel.pureOverallStyleSystemTip"),
- iconAttrs: { fill: isDark.value ? "#fff" : "#000" }
- }
- ];
- });
- const markOptions = computed<Array<OptionsType>>(() => {
- return [
- {
- label: t("panel.pureTagsStyleSmart"),
- tip: t("panel.pureTagsStyleSmartTip"),
- value: "smart"
- },
- {
- label: t("panel.pureTagsStyleCard"),
- tip: t("panel.pureTagsStyleCardTip"),
- value: "card"
- },
- {
- label: t("panel.pureTagsStyleChrome"),
- tip: t("panel.pureTagsStyleChromeTip"),
- value: "chrome"
- }
- ];
- });
- /** 设置导航模式 */
- function setLayoutModel(layout: string) {
- layoutTheme.value.layout = layout;
- window.document.body.setAttribute("layout", layout);
- $storage.layout = {
- layout,
- theme: layoutTheme.value.theme,
- darkMode: $storage.layout?.darkMode,
- sidebarStatus: $storage.layout?.sidebarStatus,
- epThemeColor: $storage.layout?.epThemeColor,
- themeColor: $storage.layout?.themeColor,
- overallStyle: $storage.layout?.overallStyle
- };
- useAppStoreHook().setLayout(layout);
- }
- watch($storage, ({ layout }) => {
- switch (layout["layout"]) {
- case "vertical":
- toggleClass(true, "is-select", unref(verticalRef));
- debounce(setFalse([horizontalRef]), 50);
- debounce(setFalse([mixRef]), 50);
- break;
- case "horizontal":
- toggleClass(true, "is-select", unref(horizontalRef));
- debounce(setFalse([verticalRef]), 50);
- debounce(setFalse([mixRef]), 50);
- break;
- case "mix":
- toggleClass(true, "is-select", unref(mixRef));
- debounce(setFalse([verticalRef]), 50);
- debounce(setFalse([horizontalRef]), 50);
- break;
- }
- });
- const mediaQueryList = window.matchMedia("(prefers-color-scheme: dark)");
- /** 根据操作系统主题设置平台整体风格 */
- function updateTheme() {
- if (overallStyle.value !== "system") return;
- if (mediaQueryList.matches) {
- dataTheme.value = true;
- } else {
- dataTheme.value = false;
- }
- dataThemeChange(overallStyle.value);
- }
- function removeMatchMedia() {
- mediaQueryList.removeEventListener("change", updateTheme);
- }
- /** 监听操作系统主题改变 */
- function watchSystemThemeChange() {
- updateTheme();
- removeMatchMedia();
- mediaQueryList.addEventListener("change", updateTheme);
- }
- onBeforeMount(() => {
- /* 初始化系统配置 */
- nextTick(() => {
- watchSystemThemeChange();
- settings.greyVal &&
- document.querySelector("html")?.classList.add("html-grey");
- settings.weakVal &&
- document.querySelector("html")?.classList.add("html-weakness");
- settings.tabsVal && tagsChange();
- settings.hideFooter && hideFooterChange();
- });
- });
- onUnmounted(() => removeMatchMedia);
- </script>
- <template>
- <LayPanel>
- <div class="panel-container">
- <p :class="pClass">{{ t("panel.pureOverallStyle") }}</p>
- <Segmented
- resize
- class="select-none"
- :modelValue="overallStyle === 'system' ? 2 : dataTheme ? 1 : 0"
- :options="themeOptions"
- @change="
- theme => {
- theme.index === 1 && theme.index !== 2
- ? (dataTheme = true)
- : (dataTheme = false);
- overallStyle = theme.option.theme;
- dataThemeChange(theme.option.theme);
- theme.index === 2 && watchSystemThemeChange();
- }
- "
- />
- <p class="panel-title mt-5">{{ t("panel.pureThemeColor") }}</p>
- <ul class="theme-color">
- <li
- v-for="(item, index) in themeColors"
- v-show="showThemeColors(item.themeColor)"
- :key="index"
- :style="getThemeColorStyle(item.color)"
- @click="setLayoutThemeColor(item.themeColor)"
- >
- <el-icon class="check-icon" :color="getThemeColor(item.themeColor)">
- <IconifyIconOffline :icon="Check" />
- </el-icon>
- </li>
- </ul>
- <p class="panel-title mt-5">{{ t("panel.pureLayoutModel") }}</p>
- <ul class="pure-theme">
- <li
- ref="verticalRef"
- v-tippy="{
- content: t('panel.pureVerticalTip'),
- zIndex: 41000
- }"
- :class="layoutTheme.layout === 'vertical' ? 'is-select' : ''"
- @click="setLayoutModel('vertical')"
- >
- <div />
- <div />
- </li>
- <li
- v-if="device !== 'mobile'"
- ref="horizontalRef"
- v-tippy="{
- content: t('panel.pureHorizontalTip'),
- zIndex: 41000
- }"
- :class="layoutTheme.layout === 'horizontal' ? 'is-select' : ''"
- @click="setLayoutModel('horizontal')"
- >
- <div />
- <div />
- </li>
- <li
- v-if="device !== 'mobile'"
- ref="mixRef"
- v-tippy="{
- content: t('panel.pureMixTip'),
- zIndex: 41000
- }"
- :class="layoutTheme.layout === 'mix' ? 'is-select' : ''"
- @click="setLayoutModel('mix')"
- >
- <div />
- <div />
- </li>
- </ul>
- <span v-if="useAppStoreHook().getViewportWidth > 1280">
- <p class="panel-title mt-5">{{ t("panel.pureStretch") }}</p>
- <Segmented
- resize
- class="segmented mb-2 select-none"
- :modelValue="isNumber(settings.stretch) ? 1 : 0"
- :options="stretchTypeOptions"
- @change="stretchTypeChange"
- />
- <el-input-number
- v-if="isNumber(settings.stretch)"
- v-model="settings.stretch as number"
- :min="1280"
- :max="1600"
- controls-position="right"
- @change="value => setStretch(value)"
- />
- <button
- v-else
- v-ripple="{ class: 'text-gray-300' }"
- class="stretch-btn"
- @click="setStretch(!settings.stretch)"
- >
- <div
- class="stretch-btn-content"
- :class="[
- settings.stretch ? 'stretch-expanded' : 'stretch-collapsed'
- ]"
- >
- <IconifyIconOffline
- :icon="settings.stretch ? RightArrow : LeftArrow"
- />
- <div class="stretch-line" />
- <IconifyIconOffline
- :icon="settings.stretch ? LeftArrow : RightArrow"
- />
- </div>
- </button>
- </span>
- <p class="panel-title mt-4">{{ t("panel.pureTagsStyle") }}</p>
- <Segmented
- resize
- class="select-none"
- :modelValue="markValue === 'smart' ? 0 : markValue === 'card' ? 1 : 2"
- :options="markOptions"
- @change="onChange"
- />
- <p class="panel-title mt-5 interface-title">
- {{ t("panel.pureInterfaceDisplay") }}
- </p>
- <ul class="setting">
- <li>
- <span class="setting-text">{{ t("panel.pureGreyModel") }}</span>
- <el-switch
- v-model="settings.greyVal"
- inline-prompt
- :active-text="t('buttons.pureOpenText')"
- :inactive-text="t('buttons.pureCloseText')"
- @change="greyChange"
- />
- </li>
- <li>
- <span class="setting-text">{{ t("panel.pureWeakModel") }}</span>
- <el-switch
- v-model="settings.weakVal"
- inline-prompt
- :active-text="t('buttons.pureOpenText')"
- :inactive-text="t('buttons.pureCloseText')"
- @change="weekChange"
- />
- </li>
- <li>
- <span class="setting-text">{{ t("panel.pureHiddenTags") }}</span>
- <el-switch
- v-model="settings.tabsVal"
- inline-prompt
- :active-text="t('buttons.pureOpenText')"
- :inactive-text="t('buttons.pureCloseText')"
- @change="tagsChange"
- />
- </li>
- <li>
- <span class="setting-text">{{ t("panel.pureHiddenFooter") }}</span>
- <el-switch
- v-model="settings.hideFooter"
- inline-prompt
- :active-text="t('buttons.pureOpenText')"
- :inactive-text="t('buttons.pureCloseText')"
- @change="hideFooterChange"
- />
- </li>
- <li>
- <span class="setting-text">Logo</span>
- <el-switch
- v-model="logoVal"
- inline-prompt
- :active-value="true"
- :inactive-value="false"
- :active-text="t('buttons.pureOpenText')"
- :inactive-text="t('buttons.pureCloseText')"
- @change="logoChange"
- />
- </li>
- <li>
- <span class="setting-text">
- {{ t("panel.pureMultiTagsCache") }}
- </span>
- <el-switch
- v-model="settings.multiTagsCache"
- inline-prompt
- :active-text="t('buttons.pureOpenText')"
- :inactive-text="t('buttons.pureCloseText')"
- @change="multiTagsCacheChange"
- />
- </li>
- </ul>
- </div>
- </LayPanel>
- </template>
- <style lang="scss" scoped>
- :deep(.el-divider__text) {
- font-size: 16px;
- font-weight: 700;
- }
- :deep(.el-switch__core) {
- --el-switch-off-color: var(--pure-switch-off-color);
- min-width: 36px;
- height: 18px;
- }
- :deep(.el-switch__core .el-switch__action) {
- height: 14px;
- }
- .panel-container {
- padding: 1.25rem; /* p-5 */
- }
- .panel-title {
- margin-bottom: 12px !important; /* mb-[12px]! */
- font-weight: 500; /* font-medium */
- font-size: 0.875rem; /* text-sm */
- }
- .dark .panel-title,
- .dark .setting-text {
- color: white; /* dark:text-white */
- }
- .mt-5 {
- margin-top: 1.25rem !important; /* mt-5! */
- }
- .mt-4 {
- margin-top: 1rem !important; /* mt-4! */
- }
- .mb-2 {
- margin-bottom: 0.5rem !important; /* mb-2 */
- }
- .theme-color {
- height: 20px;
- list-style: none;
- padding: 0;
- margin: 0;
- li {
- float: left;
- height: 20px;
- margin-right: 8px;
- cursor: pointer;
- border-radius: 4px;
- &:nth-child(1) {
- border: 1px solid #ddd;
- }
- }
- }
- .check-icon {
- margin: 0.1em 0.1em 0 0;
- font-size: 17px; /* size=17 */
- }
- .pure-theme {
- display: flex;
- gap: 12px;
- list-style: none;
- padding: 0;
- margin: 0;
- li {
- position: relative;
- width: 46px;
- height: 36px;
- overflow: hidden;
- cursor: pointer;
- background: #f0f2f5;
- border-radius: 4px;
- box-shadow: 0 1px 2.5px 0 rgb(0 0 0 / 18%);
- &:nth-child(1) {
- div {
- &:nth-child(1) {
- width: 30%;
- height: 100%;
- background: #1b2a47;
- }
- &:nth-child(2) {
- position: absolute;
- top: 0;
- right: 0;
- width: 70%;
- height: 30%;
- background: #fff;
- box-shadow: 0 0 1px #888;
- }
- }
- }
- &:nth-child(2) {
- div {
- &:nth-child(1) {
- width: 100%;
- height: 30%;
- background: #1b2a47;
- box-shadow: 0 0 1px #888;
- }
- }
- }
- &:nth-child(3) {
- div {
- &:nth-child(1) {
- width: 100%;
- height: 30%;
- background: #1b2a47;
- box-shadow: 0 0 1px #888;
- }
- &:nth-child(2) {
- position: absolute;
- bottom: 0;
- left: 0;
- width: 30%;
- height: 70%;
- background: #fff;
- box-shadow: 0 0 1px #888;
- }
- }
- }
- }
- }
- .is-select {
- border: 2px solid var(--el-color-primary);
- }
- .setting {
- list-style: none;
- padding: 0;
- margin: 0;
- li {
- display: flex;
- align-items: center;
- justify-content: space-between;
- padding: 3px 0;
- font-size: 14px;
- }
- }
- .select-none {
- user-select: none;
- }
- .stretch-btn {
- background: transparent;
- display: flex;
- align-items: center;
- justify-content: center;
- width: 100%;
- height: 5rem; /* h-20 */
- border-radius: 0.375rem; /* rounded-md */
- border: 1px solid var(--pure-border-color);
- padding: 0;
- cursor: pointer;
- }
- .stretch-btn-content {
- display: flex;
- align-items: center;
- justify-content: space-between;
- width: 100%;
- transition: all 0.3s;
- color: var(--el-color-primary);
- padding: 0 1rem;
- }
- .stretch-expanded {
- width: 24%;
- }
- .stretch-collapsed {
- width: 50%;
- }
- .stretch-line {
- flex-grow: 1;
- border: 0;
- border-bottom: 1px dashed;
- border-color: var(--el-color-primary);
- margin: 0 0.5rem;
- }
- .interface-title {
- font-weight: 500;
- font-size: 0.875rem;
- }
- </style>
|