You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

544 lines
13 KiB

  1. <script setup lang="ts">
  2. import {
  3. reactive,
  4. ref,
  5. unref,
  6. watch,
  7. computed,
  8. nextTick,
  9. useCssModule,
  10. getCurrentInstance
  11. } from "vue";
  12. import rgbHex from "rgb-hex";
  13. import { find } from "lodash-es";
  14. import panel from "../panel/index.vue";
  15. import { getConfig } from "/@/config";
  16. import { useRouter } from "vue-router";
  17. import { emitter } from "/@/utils/mitt";
  18. import { templateRef } from "@vueuse/core";
  19. import dayIcon from "/@/assets/svg/day.svg";
  20. import { debounce } from "/@/utils/debounce";
  21. import darkIcon from "/@/assets/svg/dark.svg";
  22. import { themeColorsType } from "../../types";
  23. import { useAppStoreHook } from "/@/store/modules/app";
  24. import { useEpThemeStoreHook } from "/@/store/modules/epTheme";
  25. import { storageLocal, storageSession } from "/@/utils/storage";
  26. import { useMultiTagsStoreHook } from "/@/store/modules/multiTags";
  27. import { createNewStyle, writeNewStyle } from "../../theme/element-plus";
  28. import { toggleTheme } from "@zougt/vite-plugin-theme-preprocessor/dist/browser-utils";
  29. const router = useRouter();
  30. const { isSelect } = useCssModule();
  31. const instance =
  32. getCurrentInstance().appContext.app.config.globalProperties.$storage;
  33. const instanceConfig =
  34. getCurrentInstance().appContext.app.config.globalProperties.$config;
  35. let themeColors = ref<Array<themeColorsType>>([
  36. // 道奇蓝(默认)
  37. { rgb: "27, 42, 71", themeColor: "default" },
  38. // 亮白色
  39. { rgb: "255, 255, 255", themeColor: "light" },
  40. // 猩红色
  41. { rgb: "245, 34, 45", themeColor: "dusk" },
  42. // 橙红色
  43. { rgb: "250, 84, 28", themeColor: "volcano" },
  44. // 金色
  45. { rgb: "250, 219, 20", themeColor: "yellow" },
  46. // 绿宝石
  47. { rgb: "19, 194, 194", themeColor: "mingQing" },
  48. // 酸橙绿
  49. { rgb: "82, 196, 26", themeColor: "auroraGreen" },
  50. // 深粉色
  51. { rgb: "235, 47, 150", themeColor: "pink" },
  52. // 深紫罗兰色
  53. { rgb: "114, 46, 209", themeColor: "saucePurple" }
  54. ]);
  55. const verticalRef = templateRef<HTMLElement | null>("verticalRef", null);
  56. const horizontalRef = templateRef<HTMLElement | null>("horizontalRef", null);
  57. let layoutTheme =
  58. ref(storageLocal.getItem("responsive-layout")) ||
  59. ref({
  60. layout: instanceConfig?.Layout ?? "vertical",
  61. theme: instanceConfig?.Theme ?? "default"
  62. });
  63. // body添加layout属性,作用于src/style/sidebar.scss
  64. if (unref(layoutTheme)) {
  65. let layout = unref(layoutTheme).layout;
  66. let theme = unref(layoutTheme).theme;
  67. toggleTheme({
  68. scopeName: `layout-theme-${theme}`
  69. });
  70. setLayoutModel(layout);
  71. }
  72. // 默认灵动模式
  73. const markValue = ref(storageLocal.getItem("showModel") || "smart");
  74. const logoVal = ref(storageLocal.getItem("logoVal") || "1");
  75. const epThemeColor = ref(useEpThemeStoreHook().getEpThemeColor);
  76. const settings = reactive({
  77. greyVal: instance.sets.grey,
  78. weakVal: instance.sets.weak,
  79. tabsVal: instance.sets.hideTabs,
  80. multiTagsCache: instance.sets.multiTagsCache
  81. });
  82. function toggleClass(flag: boolean, clsName: string, target?: HTMLElement) {
  83. const targetEl = target || document.body;
  84. let { className } = targetEl;
  85. className = className.replace(clsName, "");
  86. targetEl.className = flag ? `${className} ${clsName} ` : className;
  87. }
  88. // 灰色模式设置
  89. const greyChange = (value): void => {
  90. toggleClass(settings.greyVal, "html-grey", document.querySelector("html"));
  91. instance.sets = {
  92. grey: value,
  93. weak: instance.sets.weak,
  94. hideTabs: instance.sets.hideTabs,
  95. multiTagsCache: instance.sets.multiTagsCache
  96. };
  97. };
  98. // 色弱模式设置
  99. const weekChange = (value): void => {
  100. toggleClass(
  101. settings.weakVal,
  102. "html-weakness",
  103. document.querySelector("html")
  104. );
  105. instance.sets = {
  106. grey: instance.sets.grey,
  107. weak: value,
  108. hideTabs: instance.sets.hideTabs,
  109. multiTagsCache: instance.sets.multiTagsCache
  110. };
  111. };
  112. const tagsChange = () => {
  113. let showVal = settings.tabsVal;
  114. instance.sets = {
  115. grey: instance.sets.grey,
  116. weak: instance.sets.weak,
  117. hideTabs: showVal,
  118. multiTagsCache: instance.sets.multiTagsCache
  119. };
  120. emitter.emit("tagViewsChange", showVal);
  121. };
  122. const multiTagsCacheChange = () => {
  123. let multiTagsCache = settings.multiTagsCache;
  124. instance.sets = {
  125. grey: instance.sets.grey,
  126. weak: instance.sets.weak,
  127. hideTabs: instance.sets.hideTabs,
  128. multiTagsCache: multiTagsCache
  129. };
  130. useMultiTagsStoreHook().multiTagsCacheChange(multiTagsCache);
  131. };
  132. // 清空缓存并返回登录页
  133. function onReset() {
  134. toggleClass(getConfig().Grey, "html-grey", document.querySelector("html"));
  135. toggleClass(
  136. getConfig().Weak,
  137. "html-weakness",
  138. document.querySelector("html")
  139. );
  140. useMultiTagsStoreHook().handleTags("equal", [
  141. {
  142. path: "/welcome",
  143. parentPath: "/",
  144. meta: {
  145. title: "message.hshome",
  146. icon: "el-icon-s-home",
  147. i18n: true,
  148. showLink: true
  149. }
  150. }
  151. ]);
  152. useMultiTagsStoreHook().multiTagsCacheChange(getConfig().MultiTagsCache);
  153. useEpThemeStoreHook().setEpThemeColor("#409EFF");
  154. storageLocal.clear();
  155. storageSession.clear();
  156. router.push("/login");
  157. }
  158. function onChange(label) {
  159. storageLocal.setItem("showModel", label);
  160. emitter.emit("tagViewsShowModel", label);
  161. }
  162. // 侧边栏Logo
  163. function logoChange() {
  164. unref(logoVal) === "1"
  165. ? storageLocal.setItem("logoVal", "1")
  166. : storageLocal.setItem("logoVal", "-1");
  167. emitter.emit("logoChange", unref(logoVal));
  168. }
  169. function setFalse(Doms): any {
  170. Doms.forEach(v => {
  171. toggleClass(false, isSelect, unref(v));
  172. });
  173. }
  174. watch(instance, ({ layout }) => {
  175. switch (layout["layout"]) {
  176. case "vertical":
  177. toggleClass(true, isSelect, unref(verticalRef));
  178. debounce(setFalse([horizontalRef]), 50);
  179. break;
  180. case "horizontal":
  181. toggleClass(true, isSelect, unref(horizontalRef));
  182. debounce(setFalse([verticalRef]), 50);
  183. break;
  184. }
  185. });
  186. // 主题色 激活选择项
  187. const getThemeColor = computed(() => {
  188. return current => {
  189. if (
  190. current === layoutTheme.value.theme &&
  191. layoutTheme.value.theme !== "light"
  192. ) {
  193. return "#fff";
  194. } else if (
  195. current === layoutTheme.value.theme &&
  196. layoutTheme.value.theme === "light"
  197. ) {
  198. return "#1d2b45";
  199. } else {
  200. return "transparent";
  201. }
  202. };
  203. });
  204. // 设置导航模式
  205. function setLayoutModel(layout: string) {
  206. layoutTheme.value.layout = layout;
  207. window.document.body.setAttribute("layout", layout);
  208. instance.layout = {
  209. layout,
  210. theme: layoutTheme.value.theme,
  211. darkMode: instance.layout.darkMode
  212. };
  213. useAppStoreHook().setLayout(layout);
  214. }
  215. // 设置导航主题色
  216. function setLayoutThemeColor(theme: string) {
  217. layoutTheme.value.theme = theme;
  218. toggleTheme({
  219. scopeName: `layout-theme-${theme}`
  220. });
  221. instance.layout = {
  222. layout: useAppStoreHook().layout,
  223. theme,
  224. darkMode: dataTheme.value
  225. };
  226. if (theme === "default" || theme === "light") {
  227. setEpThemeColor("#409EFF");
  228. } else {
  229. const colors = find(themeColors.value, { themeColor: theme });
  230. const color = "#" + rgbHex(colors.rgb);
  231. setEpThemeColor(color);
  232. }
  233. }
  234. // 设置ep主题色
  235. const setEpThemeColor = (color: string) => {
  236. writeNewStyle(createNewStyle(color));
  237. useEpThemeStoreHook().setEpThemeColor(color);
  238. };
  239. let dataTheme = ref<boolean>(instance.layout.darkMode);
  240. // 日间、夜间主题切换
  241. function dataThemeChange() {
  242. const body = document.documentElement as HTMLElement;
  243. if (dataTheme.value) {
  244. body.setAttribute("data-theme", "dark");
  245. setLayoutThemeColor("light");
  246. } else {
  247. body.setAttribute("data-theme", "");
  248. instance.layout = {
  249. layout: useAppStoreHook().layout,
  250. theme: instance.layout.theme,
  251. darkMode: dataTheme.value
  252. };
  253. }
  254. }
  255. //初始化项目配置
  256. nextTick(() => {
  257. settings.greyVal &&
  258. document.querySelector("html")?.setAttribute("class", "html-grey");
  259. settings.weakVal &&
  260. document.querySelector("html")?.setAttribute("class", "html-weakness");
  261. settings.tabsVal && tagsChange();
  262. writeNewStyle(createNewStyle(epThemeColor.value));
  263. dataThemeChange();
  264. });
  265. </script>
  266. <template>
  267. <panel>
  268. <el-divider>主题</el-divider>
  269. <el-switch
  270. v-model="dataTheme"
  271. inline-prompt
  272. class="pure-datatheme"
  273. :active-icon="dayIcon"
  274. :inactive-icon="darkIcon"
  275. @change="dataThemeChange"
  276. >
  277. </el-switch>
  278. <el-divider>导航栏模式</el-divider>
  279. <ul class="pure-theme">
  280. <el-tooltip class="item" content="左侧菜单模式" placement="bottom">
  281. <li
  282. :class="layoutTheme.layout === 'vertical' ? $style.isSelect : ''"
  283. ref="verticalRef"
  284. @click="setLayoutModel('vertical')"
  285. >
  286. <div></div>
  287. <div></div>
  288. </li>
  289. </el-tooltip>
  290. <el-tooltip class="item" content="顶部菜单模式" placement="bottom">
  291. <li
  292. :class="layoutTheme.layout === 'horizontal' ? $style.isSelect : ''"
  293. ref="horizontalRef"
  294. @click="setLayoutModel('horizontal')"
  295. >
  296. <div></div>
  297. <div></div>
  298. </li>
  299. </el-tooltip>
  300. </ul>
  301. <el-divider v-show="!dataTheme">主题色</el-divider>
  302. <ul class="theme-color" v-show="!dataTheme">
  303. <li
  304. v-for="(item, index) in themeColors"
  305. :key="index"
  306. :style="{ background: `rgb(${item.rgb})` }"
  307. @click="setLayoutThemeColor(item.themeColor)"
  308. >
  309. <el-icon
  310. style="margin: 0.1em 0.1em 0 0"
  311. :size="17"
  312. :color="getThemeColor(item.themeColor)"
  313. >
  314. <Check />
  315. </el-icon>
  316. </li>
  317. </ul>
  318. <el-divider>界面显示</el-divider>
  319. <ul class="setting">
  320. <li v-show="!dataTheme">
  321. <span>灰色模式</span>
  322. <el-switch
  323. v-model="settings.greyVal"
  324. inline-prompt
  325. inactive-color="#a6a6a6"
  326. active-text="开"
  327. inactive-text="关"
  328. @change="greyChange"
  329. >
  330. </el-switch>
  331. </li>
  332. <li v-show="!dataTheme">
  333. <span>色弱模式</span>
  334. <el-switch
  335. v-model="settings.weakVal"
  336. inline-prompt
  337. inactive-color="#a6a6a6"
  338. active-text="开"
  339. inactive-text="关"
  340. @change="weekChange"
  341. >
  342. </el-switch>
  343. </li>
  344. <li>
  345. <span>隐藏标签页</span>
  346. <el-switch
  347. v-model="settings.tabsVal"
  348. inline-prompt
  349. inactive-color="#a6a6a6"
  350. active-text="开"
  351. inactive-text="关"
  352. @change="tagsChange"
  353. >
  354. </el-switch>
  355. </li>
  356. <li>
  357. <span>侧边栏Logo</span>
  358. <el-switch
  359. v-model="logoVal"
  360. inline-prompt
  361. active-value="1"
  362. inactive-value="-1"
  363. inactive-color="#a6a6a6"
  364. active-text="开"
  365. inactive-text="关"
  366. @change="logoChange"
  367. >
  368. </el-switch>
  369. </li>
  370. <li>
  371. <span>标签页持久化</span>
  372. <el-switch
  373. v-model="settings.multiTagsCache"
  374. inline-prompt
  375. inactive-color="#a6a6a6"
  376. active-text="开"
  377. inactive-text="关"
  378. @change="multiTagsCacheChange"
  379. >
  380. </el-switch>
  381. </li>
  382. <li>
  383. <span>标签风格</span>
  384. <el-radio-group v-model="markValue" size="small" @change="onChange">
  385. <el-radio label="card">卡片</el-radio>
  386. <el-radio label="smart">灵动</el-radio>
  387. </el-radio-group>
  388. </li>
  389. </ul>
  390. <el-divider />
  391. <el-button
  392. type="danger"
  393. style="width: 90%; margin: 24px 15px"
  394. @click="onReset"
  395. >
  396. <i class="fa fa-sign-out"></i>
  397. 清空缓存并返回登录页</el-button
  398. >
  399. </panel>
  400. </template>
  401. <style scoped module>
  402. .isSelect {
  403. border: 2px solid #0960bd;
  404. }
  405. </style>
  406. <style lang="scss" scoped>
  407. .setting {
  408. width: 100%;
  409. li {
  410. display: flex;
  411. justify-content: space-between;
  412. align-items: center;
  413. margin: 25px;
  414. }
  415. }
  416. :deep(.el-divider__text) {
  417. font-size: 16px;
  418. font-weight: 700;
  419. }
  420. .pure-datatheme {
  421. width: 100%;
  422. height: 50px;
  423. text-align: center;
  424. display: block;
  425. padding-top: 25px;
  426. }
  427. .pure-theme {
  428. margin-top: 25px;
  429. width: 100%;
  430. height: 100px;
  431. display: flex;
  432. flex-wrap: wrap;
  433. justify-content: space-around;
  434. li {
  435. margin: 10px;
  436. width: 36%;
  437. height: 70px;
  438. background: #f0f2f5;
  439. position: relative;
  440. overflow: hidden;
  441. cursor: pointer;
  442. border-radius: 4px;
  443. box-shadow: 0 1px 2.5px 0 rgb(0 0 0 / 18%);
  444. &:nth-child(1) {
  445. div {
  446. &:nth-child(1) {
  447. width: 30%;
  448. height: 100%;
  449. background: #1b2a47;
  450. }
  451. &:nth-child(2) {
  452. width: 70%;
  453. height: 30%;
  454. top: 0;
  455. right: 0;
  456. background: #fff;
  457. box-shadow: 0 0 1px #888;
  458. position: absolute;
  459. }
  460. }
  461. }
  462. &:nth-child(2) {
  463. div {
  464. &:nth-child(1) {
  465. width: 100%;
  466. height: 30%;
  467. background: #1b2a47;
  468. box-shadow: 0 0 1px #888;
  469. }
  470. }
  471. }
  472. }
  473. }
  474. .theme-color {
  475. width: 100%;
  476. height: 40px;
  477. margin-top: 20px;
  478. display: flex;
  479. justify-content: center;
  480. li {
  481. float: left;
  482. width: 20px;
  483. height: 20px;
  484. margin-top: 8px;
  485. margin-right: 8px;
  486. font-weight: 700;
  487. text-align: center;
  488. border-radius: 2px;
  489. cursor: pointer;
  490. &:nth-child(2) {
  491. border: 1px solid #ddd;
  492. }
  493. }
  494. }
  495. </style>