Przeglądaj źródła

feat: 添加整体风格自适应操作系统浅色、深色、自动主题功能,让平台充满现代气息 (#869)

* feat: 添加整体风格自适应操作系统浅色、深色、自动主题功能,让平台更加现代化

* chore: update
xiaoming 1 rok temu
rodzic
commit
dd78313622

+ 1 - 1
README.md

@@ -47,7 +47,7 @@
   <img alt="PureAdmin" src="https://xiaoxian521.github.io/hyperlink/img/vue-pure-admin/2.jpg">
 </p>
 
-暗黑模式
+暗色风格
 
 <p align="center">
   <img alt="PureAdmin" src="https://xiaoxian521.github.io/hyperlink/img/vue-pure-admin/3.jpg">

+ 1 - 1
public/logo.svg

@@ -1 +1 @@
-<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" class="icon" viewBox="0 0 1024 1024"><path fill="#386BF3" d="M410.558.109c0 210.974-300.876 361.752-300.876 633.548 0 174.943 134.704 316.787 300.876 316.787s300.877-141.817 300.877-316.787C711.408 361.752 410.558 210.974 410.558.109"/><path fill="#C3D2FB" d="M613.469 73.665c0 211.055-300.877 361.914-300.877 633.547C312.592 882.156 447.296 1024 613.47 1024s300.876-141.817 300.876-316.788C914.29 435.58 613.469 284.72 613.469 73.665"/><path fill="#303F5B" d="M312.592 707.212c0-183.713 137.636-312.171 226.723-441.39 81.702 106.112 172.12 218.74 172.12 367.726A309.755 309.755 0 0 1 420.36 950.064a323.11 323.11 0 0 1-107.769-242.852z"/></svg>
+<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" class="icon" viewBox="0 0 1024 1024"><path fill="#386BF3" d="M410.558.109c0 210.974-300.876 361.752-300.876 633.548 0 174.943 134.704 316.787 300.876 316.787s300.877-141.817 300.877-316.787C711.408 361.752 410.558 210.974 410.558.109"/><path fill="#C3D2FB" d="M613.469 73.665c0 211.055-300.877 361.914-300.877 633.547C312.592 882.156 447.296 1024 613.47 1024s300.876-141.817 300.876-316.788C914.29 435.58 613.469 284.72 613.469 73.665"/><path fill="#303F5B" d="M312.592 707.212c0-183.713 137.636-312.171 226.723-441.39 81.702 106.112 172.12 218.74 172.12 367.726A309.755 309.755 0 0 1 420.36 950.064a323.1 323.1 0 0 1-107.769-242.852z"/></svg>

+ 1 - 0
public/platform-config.json

@@ -9,6 +9,7 @@
   "Layout": "vertical",
   "Theme": "light",
   "DarkMode": false,
+  "OverallStyle": "light",
   "Grey": false,
   "Weak": false,
   "HideTabs": false,

+ 1 - 1
src/assets/login/avatar.svg

@@ -1 +1 @@
-<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" class="icon" viewBox="0 0 1024 1024"><path fill="#386BF3" d="M410.558.109c0 210.974-300.876 361.752-300.876 633.548 0 174.943 134.704 316.787 300.876 316.787s300.877-141.817 300.877-316.787C711.408 361.752 410.558 210.974 410.558.109"/><path fill="#C3D2FB" d="M613.469 73.665c0 211.055-300.877 361.914-300.877 633.547C312.592 882.156 447.296 1024 613.47 1024s300.876-141.817 300.876-316.788C914.29 435.58 613.469 284.72 613.469 73.665"/><path fill="#303F5B" d="M312.592 707.212c0-183.713 137.636-312.171 226.723-441.39 81.702 106.112 172.12 218.74 172.12 367.726A309.755 309.755 0 0 1 420.36 950.064a323.11 323.11 0 0 1-107.769-242.852z"/></svg>
+<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" class="icon" viewBox="0 0 1024 1024"><path fill="#386BF3" d="M410.558.109c0 210.974-300.876 361.752-300.876 633.548 0 174.943 134.704 316.787 300.876 316.787s300.877-141.817 300.877-316.787C711.408 361.752 410.558 210.974 410.558.109"/><path fill="#C3D2FB" d="M613.469 73.665c0 211.055-300.877 361.914-300.877 633.547C312.592 882.156 447.296 1024 613.47 1024s300.876-141.817 300.876-316.788C914.29 435.58 613.469 284.72 613.469 73.665"/><path fill="#303F5B" d="M312.592 707.212c0-183.713 137.636-312.171 226.723-441.39 81.702 106.112 172.12 218.74 172.12 367.726A309.755 309.755 0 0 1 420.36 950.064a323.1 323.1 0 0 1-107.769-242.852z"/></svg>

+ 1 - 0
src/assets/svg/system.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" class="icon" viewBox="0 0 1024 1024"><path d="M554 849.574c0 23.365-18.635 42.307-42 42.307s-42-18.941-42-42.307V662.719c0-23.365 18.635-42.307 42-42.307v-7.051c23.365 0 42 25.993 42 49.358z"/><path d="M893 888.5c0 17.397-14.103 31.5-31.5 31.5h-700c-17.397 0-31.5-14.103-31.5-31.5s14.103-31.5 31.5-31.5h700c17.397 0 31.5 14.103 31.5 31.5m33-714.074C926 135.484 894.686 105 855.744 105H168.256C129.314 105 98 135.484 98 174.426V533h828zM98 630.988C98 669.931 129.314 702 168.256 702h687.488C894.686 702 926 669.931 926 630.988V596H98z"/></svg>

Plik diff jest za duży
+ 0 - 0
src/components/ReFlowChart/src/assets/iconfont/iconfont.svg


+ 7 - 1
src/components/ReSegmented/src/index.tsx

@@ -115,7 +115,13 @@ export default defineComponent({
             onClick={event => handleChange({ option, index }, event)}
           >
             <input type="radio" name="segmented" />
-            <div class="pure-segmented-item-label">
+            <div
+              class="pure-segmented-item-label"
+              v-tippy={{
+                content: option?.tip,
+                zIndex: 41000
+              }}
+            >
               {option.icon && !isFunction(option.label) ? (
                 <span
                   class="pure-segmented-item-icon"

+ 2 - 0
src/components/ReSegmented/src/type.ts

@@ -15,4 +15,6 @@ export interface OptionsType {
   value?: string | number;
   /** 是否禁用 */
   disabled?: boolean;
+  /** `tooltip` 提示 */
+  tip?: string;
 }

+ 61 - 11
src/layout/components/setting/index.vue

@@ -6,6 +6,7 @@ import {
   reactive,
   computed,
   nextTick,
+  onUnmounted,
   onBeforeMount
 } from "vue";
 import panel from "../panel/index.vue";
@@ -21,6 +22,7 @@ import { useDataThemeChange } from "@/layout/hooks/useDataThemeChange";
 import Check from "@iconify-icons/ep/check";
 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 { device } = useNav();
 const { isDark } = useDark();
@@ -32,6 +34,7 @@ const horizontalRef = ref();
 
 const {
   dataTheme,
+  overallStyle,
   layoutTheme,
   themeColors,
   toggleClass,
@@ -70,7 +73,7 @@ const getThemeColorStyle = computed(() => {
   };
 });
 
-/** 当网页为暗黑模式时不显示亮白色切换选项 */
+/** 当网页整体为暗色风格时不显示亮白色主题配色切换选项 */
 const showThemeColors = computed(() => {
   return themeColor => {
     return themeColor === "light" && isDark.value ? false : true;
@@ -162,13 +165,24 @@ const getThemeColor = computed(() => {
 const themeOptions = computed<Array<OptionsType>>(() => {
   return [
     {
-      label: "色",
+      label: "色",
       icon: dayIcon,
+      theme: "light",
+      tip: "清新启航,点亮舒适的工作界面",
       iconAttrs: { fill: isDark.value ? "#fff" : "#000" }
     },
     {
-      label: "色",
+      label: "色",
       icon: darkIcon,
+      theme: "dark",
+      tip: "月光序曲,沉醉于夜的静谧雅致",
+      iconAttrs: { fill: isDark.value ? "#fff" : "#000" }
+    },
+    {
+      label: "自动",
+      icon: systemIcon,
+      theme: "system",
+      tip: "同步时光,界面随晨昏自然呼应",
       iconAttrs: { fill: isDark.value ? "#fff" : "#000" }
     }
   ];
@@ -177,10 +191,12 @@ const themeOptions = computed<Array<OptionsType>>(() => {
 const markOptions: Array<OptionsType> = [
   {
     label: "灵动",
+    tip: "灵动标签,添趣生辉",
     value: "smart"
   },
   {
     label: "卡片",
+    tip: "卡片标签,高效浏览",
     value: "card"
   }
 ];
@@ -195,7 +211,8 @@ function setLayoutModel(layout: string) {
     darkMode: $storage.layout?.darkMode,
     sidebarStatus: $storage.layout?.sidebarStatus,
     epThemeColor: $storage.layout?.epThemeColor,
-    themeColor: layoutTheme.value.theme
+    themeColor: $storage.layout?.themeColor,
+    overallStyle: $storage.layout?.overallStyle
   };
   useAppStoreHook().setLayout(layout);
 }
@@ -220,9 +237,34 @@ watch($storage, ({ layout }) => {
   }
 });
 
+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")?.setAttribute("class", "html-grey");
     settings.weakVal &&
@@ -231,6 +273,8 @@ onBeforeMount(() => {
     settings.hideFooter && hideFooterChange();
   });
 });
+
+onUnmounted(() => removeMatchMedia);
 </script>
 
 <template>
@@ -238,12 +282,17 @@ onBeforeMount(() => {
     <div class="p-6">
       <p class="mb-3 font-medium text-sm dark:text-white">整体风格</p>
       <Segmented
-        :modelValue="dataTheme ? 1 : 0"
+        class="select-none"
+        :modelValue="overallStyle === 'system' ? 2 : dataTheme ? 1 : 0"
         :options="themeOptions"
         @change="
-          {
-            dataTheme = !dataTheme;
-            dataThemeChange();
+          theme => {
+            theme.index === 1 && theme.index !== 2
+              ? (dataTheme = true)
+              : (dataTheme = false);
+            overallStyle = theme.option.theme;
+            dataThemeChange(theme.option.theme);
+            theme.index === 2 && watchSystemThemeChange();
           }
         "
       />
@@ -272,7 +321,7 @@ onBeforeMount(() => {
         <li
           ref="verticalRef"
           v-tippy="{
-            content: '左侧菜单',
+            content: '左侧菜单,亲切熟悉',
             zIndex: 41000
           }"
           :class="layoutTheme.layout === 'vertical' ? 'is-select' : ''"
@@ -285,7 +334,7 @@ onBeforeMount(() => {
           v-if="device !== 'mobile'"
           ref="horizontalRef"
           v-tippy="{
-            content: '顶部菜单',
+            content: '顶部菜单,简洁概览',
             zIndex: 41000
           }"
           :class="layoutTheme.layout === 'horizontal' ? 'is-select' : ''"
@@ -298,7 +347,7 @@ onBeforeMount(() => {
           v-if="device !== 'mobile'"
           ref="mixRef"
           v-tippy="{
-            content: '混合菜单',
+            content: '混合菜单,灵活多变',
             zIndex: 41000
           }"
           :class="layoutTheme.layout === 'mix' ? 'is-select' : ''"
@@ -311,6 +360,7 @@ onBeforeMount(() => {
 
       <p class="mt-5 mb-3 font-medium text-base dark:text-white">页签风格</p>
       <Segmented
+        class="select-none"
         :modelValue="markValue === 'smart' ? 0 : 1"
         :options="markOptions"
         @change="onChange"

+ 7 - 3
src/layout/hooks/useDataThemeChange.ts

@@ -38,6 +38,7 @@ export function useDataThemeChange() {
 
   const { $storage } = useGlobal<GlobalPropertiesApi>();
   const dataTheme = ref<boolean>($storage?.layout?.darkMode);
+  const overallStyle = ref<string>($storage?.layout?.overallStyle);
   const body = document.documentElement as HTMLElement;
 
   function toggleClass(flag: boolean, clsName: string, target?: HTMLElement) {
@@ -64,7 +65,8 @@ export function useDataThemeChange() {
       darkMode: dataTheme.value,
       sidebarStatus: $storage.layout?.sidebarStatus,
       epThemeColor: $storage.layout?.epThemeColor,
-      themeColor: isClick ? theme : storageThemeColor
+      themeColor: isClick ? theme : storageThemeColor,
+      overallStyle: overallStyle.value
     };
 
     if (theme === "default" || theme === "light") {
@@ -94,8 +96,9 @@ export function useDataThemeChange() {
     }
   };
 
-  /** 亮色、暗色整体风格切换 */
-  function dataThemeChange() {
+  /** 浅色、深色整体风格切换 */
+  function dataThemeChange(overall?: string) {
+    overallStyle.value = overall;
     if (useEpThemeStoreHook().epTheme === "light" && dataTheme.value) {
       setLayoutThemeColor("default", false);
     } else {
@@ -130,6 +133,7 @@ export function useDataThemeChange() {
   return {
     body,
     dataTheme,
+    overallStyle,
     layoutTheme,
     themeColors,
     onReset,

+ 2 - 1
src/layout/hooks/useLayout.ts

@@ -28,7 +28,8 @@ export function useLayout() {
         darkMode: $config?.DarkMode ?? false,
         sidebarStatus: $config?.SidebarStatus ?? true,
         epThemeColor: $config?.EpThemeColor ?? "#409EFF",
-        themeColor: $config?.Theme ?? "light"
+        themeColor: $config?.Theme ?? "light",
+        overallStyle: $config?.OverallStyle ?? "light"
       };
     }
     /** 灰色模式、色弱模式、隐藏标签页 */

+ 3 - 2
src/layout/index.vue

@@ -73,7 +73,8 @@ function setTheme(layoutModel: string) {
     darkMode: $storage.layout?.darkMode,
     sidebarStatus: $storage.layout?.sidebarStatus,
     epThemeColor: $storage.layout?.epThemeColor,
-    themeColor: $storage.layout?.themeColor
+    themeColor: $storage.layout?.themeColor,
+    overallStyle: $storage.layout?.overallStyle
   };
 }
 
@@ -119,7 +120,7 @@ onMounted(() => {
 });
 
 onBeforeMount(() => {
-  useDataThemeChange().dataThemeChange();
+  useDataThemeChange().dataThemeChange($storage.layout?.overallStyle);
 });
 
 const layoutHeader = defineComponent({

+ 2 - 2
src/style/dark.scss

@@ -1,6 +1,6 @@
 @use "element-plus/theme-chalk/src/dark/css-vars.scss" as *;
 
-/* 暗黑模式适配 */
+/* 整体暗色风格适配 */
 html.dark {
   $border-style: #303030;
   $color-white: #fff;
@@ -126,7 +126,7 @@ html.dark {
     }
   }
 
-  /* 克隆并自定义 ElMessage 样式,不会影响 ElMessage 原本样式,在 src/utils/message.ts 中调用自定义样式 ElMessage 方法即可,非暗黑模式在 src/style/element-plus.scss 文件进行了适配 */
+  /* 克隆并自定义 ElMessage 样式,不会影响 ElMessage 原本样式,在 src/utils/message.ts 中调用自定义样式 ElMessage 方法即可,整体浅色风格在 src/style/element-plus.scss 文件进行了适配 */
   .pure-message {
     background-color: rgb(36 37 37) !important;
     background-image: initial !important;

+ 2 - 2
src/style/element-plus.scss

@@ -116,7 +116,7 @@
   }
 }
 
-/* 克隆并自定义 ElMessage 样式,不会影响 ElMessage 原本样式,在 src/utils/message.ts 中调用自定义样式 ElMessage 方法即可,暗黑模式在 src/style/dark.scss 文件进行了适配 */
+/* 克隆并自定义 ElMessage 样式,不会影响 ElMessage 原本样式,在 src/utils/message.ts 中调用自定义样式 ElMessage 方法即可,整体暗色风格在 src/style/dark.scss 文件进行了适配 */
 .pure-message {
   padding: 10px 13px !important;
   background: #fff !important;
@@ -189,7 +189,7 @@
   }
 }
 
-/* 仿 el-scrollbar 滚动条样式,支持大多数浏览器,如Chrome、Edge、Firefox、Safari等。暗黑模式在 src/style/dark.scss 文件进行了适配 */
+/* 仿 el-scrollbar 滚动条样式,支持大多数浏览器,如Chrome、Edge、Firefox、Safari等。整体暗色风格在 src/style/dark.scss 文件进行了适配 */
 .pure-scrollbar {
   /* Firefox */
   scrollbar-width: thin; /* 可选值为 'auto', 'thin', 'none' */

+ 2 - 1
src/utils/responsive.ts

@@ -19,7 +19,8 @@ export const injectResponsiveStorage = (app: App, config: PlatformConfigs) => {
         darkMode: config.DarkMode ?? false,
         sidebarStatus: config.SidebarStatus ?? true,
         epThemeColor: config.EpThemeColor ?? "#409EFF",
-        themeColor: config.Theme ?? "light" // 主题色(对应项目配置中的主题色,与theme不同的是它不会受到亮色、暗色整体风格切换的影响,只会在手动点击主题色时改变)
+        themeColor: config.Theme ?? "light", // 主题色(对应项目配置中的主题色,与theme不同的是它不会受到浅色、深色整体风格切换的影响,只会在手动点击主题色时改变)
+        overallStyle: config.OverallStyle ?? "light" // 整体风格(浅色:light、深色:dark、自动:system)
       },
       // 项目配置-界面显示
       configure: Storage.getData("configure", nameSpace) ?? {

+ 1 - 1
src/views/components/message/index.vue

@@ -117,7 +117,7 @@ defineOptions({
     <h4 class="mb-4">
       类似 Ant Design 风格的消息提示,点击弹出提示信息(基于 ElMessage
       样式改版,不会影响 ElMessage
-      原本样式,使用和打包大小成本极低并适配暗黑模式
+      原本样式,使用和打包大小成本极低并适配整体暗色风格
     </h4>
 
     <el-space wrap>

+ 2 - 2
src/views/login/index.vue

@@ -56,8 +56,8 @@ const currentPage = computed(() => {
 const { t } = useI18n();
 const { initStorage } = useLayout();
 initStorage();
-const { dataTheme, dataThemeChange } = useDataThemeChange();
-dataThemeChange();
+const { dataTheme, overallStyle, dataThemeChange } = useDataThemeChange();
+dataThemeChange(overallStyle.value);
 const { title, getDropdownItemStyle, getDropdownItemClass } = useNav();
 const { locale, translationCh, translationEn } = useTranslationLang();
 

+ 3 - 0
types/global.d.ts

@@ -83,6 +83,7 @@ declare global {
     Layout?: string;
     Theme?: string;
     DarkMode?: boolean;
+    OverallStyle?: string;
     Grey?: boolean;
     Weak?: boolean;
     HideTabs?: boolean;
@@ -127,6 +128,7 @@ declare global {
     sidebarStatus?: boolean;
     epThemeColor?: string;
     themeColor?: string;
+    overallStyle?: string;
     showLogo?: boolean;
     showModel?: string;
     mapConfigure?: {
@@ -154,6 +156,7 @@ declare global {
       sidebarStatus?: boolean;
       epThemeColor?: string;
       themeColor?: string;
+      overallStyle?: string;
     };
     configure: {
       grey?: boolean;

Niektóre pliki nie zostały wyświetlone z powodu dużej ilości zmienionych plików