Commit da04913ef324fff122732b445c1b1d1d662b87a3

Authored by vben
1 parent 7d9b521c

feat: added settingButtonPosition configuration close #275

CHANGELOG.zh_CN.md
  1 +## Wip
  2 +
  3 +### ✨ Features
  4 +
  5 +- 新增 `settingButtonPosition`配置项,用于配置`设置`按钮位置
  6 +
1 7 ## 2.0.0 (2021-02-18)
2 8  
3 9 ## (破坏性更新) Breaking changes
... ...
build/vite/plugin/compress.ts
1 1 /**
2 2 * Used to package and output gzip. Note that this does not work properly in Vite, the specific reason is still being investigated
  3 + * https://github.com/anncwb/vite-plugin-compression
3 4 */
4 5 import type { Plugin } from 'vite';
5 6  
... ...
build/vite/plugin/imagemin.ts
1 1 // Image resource files used to compress the output of the production environment
  2 +// https://github.com/anncwb/vite-plugin-imagemin
2 3  
3 4 import viteImagemin from 'vite-plugin-imagemin';
4 5  
... ...
build/vite/plugin/index.ts
... ... @@ -4,7 +4,6 @@ import vue from '@vitejs/plugin-vue';
4 4 import vueJsx from '@vitejs/plugin-vue-jsx';
5 5 import legacy from '@vitejs/plugin-legacy';
6 6  
7   -import windiCSS from 'vite-plugin-windicss';
8 7 import PurgeIcons from 'vite-plugin-purge-icons';
9 8  
10 9 import { ViteEnv } from '../../utils';
... ... @@ -16,6 +15,7 @@ import { configStyleImportPlugin } from './styleImport';
16 15 import { configVisualizerConfig } from './visualizer';
17 16 import { configThemePlugin } from './theme';
18 17 import { configImageminPlugin } from './imagemin';
  18 +import { configWindiCssPlugin } from './windicss';
19 19  
20 20 export function createVitePlugins(viteEnv: ViteEnv, isBuild: boolean) {
21 21 const { VITE_USE_IMAGEMIN, VITE_USE_MOCK, VITE_LEGACY, VITE_BUILD_COMPRESS } = viteEnv;
... ... @@ -25,7 +25,6 @@ export function createVitePlugins(viteEnv: ViteEnv, isBuild: boolean) {
25 25 vue(),
26 26 // have to
27 27 vueJsx(),
28   - ...windiCSS(),
29 28 ];
30 29  
31 30 // @vitejs/plugin-legacy
... ... @@ -34,6 +33,9 @@ export function createVitePlugins(viteEnv: ViteEnv, isBuild: boolean) {
34 33 // vite-plugin-html
35 34 vitePlugins.push(configHtmlPlugin(viteEnv, isBuild));
36 35  
  36 + // vite-plugin-windicss
  37 + vitePlugins.push(configWindiCssPlugin());
  38 +
37 39 // vite-plugin-mock
38 40 VITE_USE_MOCK && vitePlugins.push(configMockPlugin(isBuild));
39 41  
... ...
build/vite/plugin/windicss.ts 0 → 100644
  1 +import windiCSS from 'vite-plugin-windicss';
  2 +
  3 +import type { Plugin } from 'vite';
  4 +
  5 +export function configWindiCssPlugin(): Plugin[] {
  6 + return windiCSS({
  7 + safelist: 'shadow shadow-xl',
  8 + preflight: {
  9 + enableAll: true,
  10 + },
  11 + });
  12 +}
... ...
package.json
... ... @@ -107,7 +107,7 @@
107 107 "vite-plugin-pwa": "^0.4.7",
108 108 "vite-plugin-style-import": "^0.7.2",
109 109 "vite-plugin-theme": "^0.4.3",
110   - "vite-plugin-windicss": "0.2.2",
  110 + "vite-plugin-windicss": "0.3.3",
111 111 "vue-eslint-parser": "^7.5.0",
112 112 "yargs": "^16.2.0"
113 113 },
... ...
src/enums/appEnum.ts
... ... @@ -22,6 +22,12 @@ export enum ThemeEnum {
22 22 LIGHT = 'light',
23 23 }
24 24  
  25 +export enum SettingButtonPositionEnum {
  26 + AUTO = 'auto',
  27 + HEADER = 'header',
  28 + FIXED = 'fixed',
  29 +}
  30 +
25 31 /**
26 32 * 权限模式
27 33 */
... ...
src/hooks/event/useKeyPress.ts
... ... @@ -160,13 +160,5 @@ export function getTargetElement(
160 160 if (!target) {
161 161 return defaultElement;
162 162 }
163   -
164   - let targetElement: TargetElement | undefined | null;
165   -
166   - if (isFunction(target)) {
167   - targetElement = target();
168   - } else {
169   - targetElement = unref(target);
170   - }
171   - return targetElement;
  163 + return isFunction(target) ? target() : unref(target);
172 164 }
... ...
src/hooks/setting/useRootSetting.ts
... ... @@ -16,6 +16,8 @@ const getPageLoading = computed(() => appStore.getPageLoading);
16 16  
17 17 const getOpenKeepAlive = computed(() => unref(getRootSetting).openKeepAlive);
18 18  
  19 +const getSettingButtonPosition = computed(() => unref(getRootSetting).settingButtonPosition);
  20 +
19 21 const getCanEmbedIFramePage = computed(() => unref(getRootSetting).canEmbedIFramePage);
20 22  
21 23 const getPermissionMode = computed(() => unref(getRootSetting).permissionMode);
... ... @@ -58,6 +60,7 @@ export function useRootSetting() {
58 60 return {
59 61 setRootSetting,
60 62  
  63 + getSettingButtonPosition,
61 64 getFullContent,
62 65 getColorWeak,
63 66 getGrayMode,
... ...
src/hooks/web/useFullScreen.ts
... ... @@ -57,12 +57,7 @@ export function useFullscreen(
57 57  
58 58 async function toggleFullscreen(): Promise<void> {
59 59 if (!unref(target)) return;
60   -
61   - if (isFullscreen()) {
62   - return exitFullscreen();
63   - } else {
64   - return enterFullscreen();
65   - }
  60 + return isFullscreen() ? exitFullscreen() : enterFullscreen();
66 61 }
67 62  
68 63 return {
... ...
src/layouts/default/feature/index.vue
1   -<template>
2   - <LayoutLockPage />
3   - <BackTop v-if="getUseOpenBackTop" :target="getTarget" />
4   -</template>
5 1 <script lang="ts">
6   - import { defineComponent } from 'vue';
7   - import { createAsyncComponent } from '/@/utils/factory/createAsyncComponent';
  2 + import { defineComponent, computed, unref } from 'vue';
8 3 import { BackTop } from 'ant-design-vue';
  4 +
9 5 import { useRootSetting } from '/@/hooks/setting/useRootSetting';
  6 + import { useHeaderSetting } from '/@/hooks/setting/useHeaderSetting';
  7 + import { useDesign } from '/@/hooks/web/useDesign';
  8 +
  9 + import { SettingButtonPositionEnum } from '/@/enums/appEnum';
  10 + import { createAsyncComponent } from '/@/utils/factory/createAsyncComponent';
10 11  
11 12 export default defineComponent({
12 13 name: 'LayoutFeatures',
13 14 components: {
14 15 BackTop,
15 16 LayoutLockPage: createAsyncComponent(() => import('/@/views/sys/lock/index.vue')),
  17 + SettingDrawer: createAsyncComponent(() => import('/@/layouts/default/setting/index.vue')),
16 18 },
17 19 setup() {
18   - const { getUseOpenBackTop } = useRootSetting();
  20 + const {
  21 + getUseOpenBackTop,
  22 + getShowSettingButton,
  23 + getSettingButtonPosition,
  24 + getFullContent,
  25 + } = useRootSetting();
  26 +
  27 + const { prefixCls } = useDesign('setting-drawer-fearure');
  28 + const { getShowHeader } = useHeaderSetting();
  29 +
  30 + const getIsFixedSettingDrawer = computed(() => {
  31 + if (!unref(getShowSettingButton)) {
  32 + return false;
  33 + }
  34 + const settingButtonPosition = unref(getSettingButtonPosition);
  35 +
  36 + if (settingButtonPosition === SettingButtonPositionEnum.AUTO) {
  37 + return !unref(getShowHeader) || unref(getFullContent);
  38 + }
  39 + return settingButtonPosition === SettingButtonPositionEnum.FIXED;
  40 + });
19 41  
20 42 return {
21 43 getTarget: () => document.body,
22 44 getUseOpenBackTop,
  45 + getIsFixedSettingDrawer,
  46 + prefixCls,
23 47 };
24 48 },
25 49 });
26 50 </script>
  51 +
  52 +<template>
  53 + <LayoutLockPage />
  54 + <BackTop v-if="getUseOpenBackTop" :target="getTarget" />
  55 + <SettingDrawer v-if="getIsFixedSettingDrawer" :class="prefixCls" />
  56 +</template>
  57 +
  58 +<style lang="less">
  59 + @prefix-cls: ~'@{namespace}-setting-drawer-fearure';
  60 +
  61 + .@{prefix-cls} {
  62 + position: absolute;
  63 + top: 45%;
  64 + right: 0;
  65 + z-index: 10;
  66 + display: flex;
  67 + padding: 10px;
  68 + color: @white;
  69 + cursor: pointer;
  70 + background: @primary-color;
  71 + border-radius: 6px 0 0 6px;
  72 + justify-content: center;
  73 + align-items: center;
  74 +
  75 + svg {
  76 + width: 1em;
  77 + height: 1em;
  78 + }
  79 + }
  80 +</style>
... ...
src/layouts/default/header/components/user-dropdown/index.vue
... ... @@ -22,7 +22,7 @@
22 22 icon="ion:lock-closed-outline"
23 23 />
24 24 <MenuItem
25   - key="loginOut"
  25 + key="logout"
26 26 :text="t('layout.header.dropdownItemLoginOut')"
27 27 icon="ion:power-outline"
28 28 />
... ... @@ -51,7 +51,7 @@
51 51  
52 52 import { createAsyncComponent } from '/@/utils/factory/createAsyncComponent';
53 53  
54   - type MenuEvent = 'loginOut' | 'doc' | 'lock';
  54 + type MenuEvent = 'logout' | 'doc' | 'lock';
55 55  
56 56 export default defineComponent({
57 57 name: 'UserDropdown',
... ... @@ -93,7 +93,7 @@
93 93  
94 94 function handleMenuClick(e: { key: MenuEvent }) {
95 95 switch (e.key) {
96   - case 'loginOut':
  96 + case 'logout':
97 97 handleLoginOut();
98 98 break;
99 99 case 'doc':
... ...
src/layouts/default/header/index.vue
... ... @@ -50,7 +50,7 @@
50 50  
51 51 <UserDropDown :theme="getHeaderTheme" />
52 52  
53   - <SettingDrawer v-if="getShowSettingButton" :class="`${prefixCls}-action__item`" />
  53 + <SettingDrawer v-if="getShowSetting" :class="`${prefixCls}-action__item`" />
54 54 </div>
55 55 </Header>
56 56 </template>
... ... @@ -72,6 +72,7 @@
72 72 import { useLocaleSetting } from '/@/hooks/setting/useLocaleSetting';
73 73  
74 74 import { MenuModeEnum, MenuSplitTyeEnum } from '/@/enums/menuEnum';
  75 + import { SettingButtonPositionEnum } from '/@/enums/appEnum';
75 76 import { AppLocalePicker } from '/@/components/Application';
76 77  
77 78 import { UserDropDown, LayoutBreadcrumb, FullScreen, Notify, ErrorAction } from './components';
... ... @@ -112,7 +113,11 @@
112 113 getIsMixSidebar,
113 114 } = useMenuSetting();
114 115 const { getShowLocale } = useLocaleSetting();
115   - const { getUseErrorHandle, getShowSettingButton } = useRootSetting();
  116 + const {
  117 + getUseErrorHandle,
  118 + getShowSettingButton,
  119 + getSettingButtonPosition,
  120 + } = useRootSetting();
116 121  
117 122 const {
118 123 getHeaderTheme,
... ... @@ -122,6 +127,7 @@
122 127 getShowContent,
123 128 getShowBread,
124 129 getShowHeaderLogo,
  130 + getShowHeader,
125 131 } = useHeaderSetting();
126 132  
127 133 const { getIsMobile } = useAppInject();
... ... @@ -138,6 +144,18 @@
138 144 ];
139 145 });
140 146  
  147 + const getShowSetting = computed(() => {
  148 + if (!unref(getShowSettingButton)) {
  149 + return false;
  150 + }
  151 + const settingButtonPosition = unref(getSettingButtonPosition);
  152 +
  153 + if (settingButtonPosition === SettingButtonPositionEnum.AUTO) {
  154 + return unref(getShowHeader);
  155 + }
  156 + return settingButtonPosition === SettingButtonPositionEnum.HEADER;
  157 + });
  158 +
141 159 const getLogoWidth = computed(() => {
142 160 if (!unref(getIsMixMode) || unref(getIsMobile)) {
143 161 return {};
... ... @@ -175,6 +193,7 @@
175 193 getLogoWidth,
176 194 getIsMixSidebar,
177 195 getShowSettingButton,
  196 + getShowSetting,
178 197 };
179 198 },
180 199 });
... ...
src/layouts/default/setting/index.vue
1 1 <template>
2   - <div @click="openDrawer" :class="prefixCls">
  2 + <div @click="openDrawer">
3 3 <Icon icon="ion:settings-outline" />
4 4 <SettingDrawer @register="register" />
5 5 </div>
... ... @@ -10,7 +10,6 @@
10 10 import Icon from '/@/components/Icon';
11 11  
12 12 import { useDrawer } from '/@/components/Drawer';
13   - import { useDesign } from '/@/hooks/web/useDesign';
14 13  
15 14 export default defineComponent({
16 15 name: 'SettingButton',
... ... @@ -18,9 +17,7 @@
18 17 setup() {
19 18 const [register, { openDrawer }] = useDrawer();
20 19  
21   - const { prefixCls } = useDesign('setting-button');
22 20 return {
23   - prefixCls,
24 21 register,
25 22 openDrawer,
26 23 };
... ...
src/locales/lang/en/sys/app.ts
1 1 export default {
2   - loginOutTip: 'Reminder',
3   - loginOutMessage: 'Confirm to exit the system?',
  2 + logoutTip: 'Reminder',
  3 + logoutMessage: 'Confirm to exit the system?',
4 4 menuLoading: 'Menu loading...',
5 5 };
... ...
src/locales/lang/zh_CN/sys/app.ts
1 1 export default {
2   - loginOutTip: '温馨提醒',
3   - loginOutMessage: '是否确认退出系统?',
  2 + logoutTip: '温馨提醒',
  3 + logoutMessage: '是否确认退出系统?',
4 4 menuLoading: '菜单加载中...',
5 5 };
... ...
src/settings/projectSetting.ts
... ... @@ -2,7 +2,13 @@ import type { ProjectConfig } from &#39;/@/types/config&#39;;
2 2  
3 3 import { MenuTypeEnum, MenuModeEnum, TriggerEnum, MixSidebarTriggerEnum } from '/@/enums/menuEnum';
4 4 import { CacheTypeEnum } from '/@/enums/cacheEnum';
5   -import { ContentEnum, PermissionModeEnum, ThemeEnum, RouterTransitionEnum } from '/@/enums/appEnum';
  5 +import {
  6 + ContentEnum,
  7 + PermissionModeEnum,
  8 + ThemeEnum,
  9 + RouterTransitionEnum,
  10 + SettingButtonPositionEnum,
  11 +} from '/@/enums/appEnum';
6 12 import { primaryColor, themeMode } from '../../build/config/themeConfig';
7 13  
8 14 // ! You need to clear the browser cache after the change
... ... @@ -10,6 +16,9 @@ const setting: ProjectConfig = {
10 16 // Whether to show the configuration button
11 17 showSettingButton: true,
12 18  
  19 + // `Settings` button position
  20 + settingButtonPosition: SettingButtonPositionEnum.AUTO,
  21 +
13 22 // Permission mode
14 23 permissionMode: PermissionModeEnum.ROLE,
15 24  
... ...
src/store/modules/permission.ts
... ... @@ -98,22 +98,22 @@ class Permission extends VuexModule {
98 98 if (!roles) return true;
99 99 return roleList.some((role) => roles.includes(role));
100 100 });
101   - // 如果确定不需要做后台动态权限,请将下面整个判断注释
  101 + // If you are sure that you do not need to do background dynamic permissions, please comment the entire judgment below
102 102 } else if (permissionMode === PermissionModeEnum.BACK) {
103 103 createMessage.loading({
104 104 content: t('sys.app.menuLoading'),
105 105 duration: 1,
106 106 });
107   - // 这里获取后台路由菜单逻辑自行修改
  107 + // Here to get the background routing menu logic to modify by yourself
108 108 const paramId = id || userStore.getUserInfoState.userId;
109 109 if (!paramId) {
110 110 throw new Error('paramId is undefined!');
111 111 }
112 112 let routeList = (await getMenuListById({ id: paramId })) as AppRouteRecordRaw[];
113 113  
114   - // 动态引入组件
  114 + // Dynamically introduce components
115 115 routeList = transformObjToRoute(routeList);
116   - // 后台路由转菜单结构
  116 + // Background routing to menu structure
117 117 const backMenuList = transformRouteToMenu(routeList);
118 118  
119 119 this.commitBackMenuListState(backMenuList);
... ...
src/store/modules/user.ts
... ... @@ -131,10 +131,10 @@ class User extends VuexModule {
131 131 }
132 132  
133 133 /**
134   - * @description: login out
  134 + * @description: logout
135 135 */
136 136 @Action
137   - async loginOut(goLogin = false) {
  137 + async logout(goLogin = false) {
138 138 goLogin && router.push(PageEnum.BASE_LOGIN);
139 139 }
140 140  
... ... @@ -147,10 +147,10 @@ class User extends VuexModule {
147 147 const { t } = useI18n();
148 148 createConfirm({
149 149 iconType: 'warning',
150   - title: t('sys.app.loginOutTip'),
151   - content: t('sys.app.loginOutMessage'),
  150 + title: t('sys.app.logoutTip'),
  151 + content: t('sys.app.logoutMessage'),
152 152 onOk: async () => {
153   - await this.loginOut(true);
  153 + await this.logout(true);
154 154 },
155 155 });
156 156 }
... ...
src/types/config.d.ts
1 1 import { MenuTypeEnum, MenuModeEnum, TriggerEnum, MixSidebarTriggerEnum } from '/@/enums/menuEnum';
2   -import { ContentEnum, PermissionModeEnum, ThemeEnum, RouterTransitionEnum } from '/@/enums/appEnum';
  2 +import {
  3 + ContentEnum,
  4 + PermissionModeEnum,
  5 + ThemeEnum,
  6 + RouterTransitionEnum,
  7 + SettingButtonPositionEnum,
  8 +} from '/@/enums/appEnum';
3 9 import { CacheTypeEnum } from '/@/enums/cacheEnum';
4 10 import type { LocaleType } from '/@/locales/types';
5 11 import { ThemeMode } from '../../build/config/lessModifyVars';
... ... @@ -88,6 +94,7 @@ export interface ProjectConfig {
88 94  
89 95 // 是否显示配置按钮
90 96 showSettingButton: boolean;
  97 + settingButtonPosition: SettingButtonPositionEnum;
91 98 // 权限模式
92 99 permissionMode: PermissionModeEnum;
93 100 // 网站灰色模式,用于可能悼念的日期开启
... ...
src/utils/helper/treeHelper.ts
... ... @@ -100,9 +100,9 @@ export function findPath&lt;T = any&gt;(
100 100  
101 101 export function findPathAll(tree: any, func: Fn, config: Partial<TreeHelperConfig> = {}) {
102 102 config = getConfig(config);
103   - const path = [];
  103 + const path: any[] = [];
104 104 const list = [...tree];
105   - const result = [];
  105 + const result: any[] = [];
106 106 const visitedSet = new Set(),
107 107 { children } = config;
108 108 while (list.length) {
... ... @@ -153,14 +153,14 @@ export function forEach&lt;T = any&gt;(
153 153 }
154 154  
155 155 /**
156   - * @description: 提取tree指定结构
  156 + * @description: Extract tree specified structure
157 157 */
158 158 export function treeMap<T = any>(treeData: T[], opt: { children?: string; conversion: Fn }): T[] {
159 159 return treeData.map((item) => treeMapEach(item, opt));
160 160 }
161 161  
162 162 /**
163   - * @description: 提取tree指定结构
  163 + * @description: Extract tree specified structure
164 164 */
165 165 export function treeMapEach(
166 166 data: any,
... ...
src/utils/http/axios/checkStatus.ts
... ... @@ -15,7 +15,7 @@ export function checkStatus(status: number, msg: string): void {
15 15 // Return to the current page after successful login. This step needs to be operated on the login page.
16 16 case 401:
17 17 error(t('sys.api.errMsg401'));
18   - userStore.loginOut(true);
  18 + userStore.logout(true);
19 19 break;
20 20 case 403:
21 21 error(t('sys.api.errMsg403'));
... ...
src/utils/index.ts
... ... @@ -23,17 +23,11 @@ export function getPopupContainer(node?: HTMLElement): HTMLElement {
23 23 */
24 24 export function setObjToUrlParams(baseUrl: string, obj: any): string {
25 25 let parameters = '';
26   - let url = '';
27 26 for (const key in obj) {
28 27 parameters += key + '=' + encodeURIComponent(obj[key]) + '&';
29 28 }
30 29 parameters = parameters.replace(/&$/, '');
31   - if (/\?$/.test(baseUrl)) {
32   - url = baseUrl + parameters;
33   - } else {
34   - url = baseUrl.replace(/\/?$/, '?') + parameters;
35   - }
36   - return url;
  30 + return /\?$/.test(baseUrl) ? baseUrl + parameters : baseUrl.replace(/\/?$/, '?') + parameters;
37 31 }
38 32  
39 33 export function deepMerge<T = any>(src: any = {}, target: any = {}): T {
... ... @@ -45,7 +39,7 @@ export function deepMerge&lt;T = any&gt;(src: any = {}, target: any = {}): T {
45 39 }
46 40  
47 41 /**
48   - * @description: 根据数组中某个对象值去重
  42 + * @description: Deduplication according to the value of an object in the array
49 43 */
50 44 export function unique<T = any>(arr: T[], key: string): T[] {
51 45 const map = new Map();
... ... @@ -56,7 +50,7 @@ export function unique&lt;T = any&gt;(arr: T[], key: string): T[] {
56 50 }
57 51  
58 52 /**
59   - * @description: es6数组去重复
  53 + * @description: es6 array to repeat
60 54 */
61 55 export function es6Unique<T>(arr: T[]): T[] {
62 56 return Array.from(new Set(arr));
... ...
src/views/sys/lock/LockPage.vue
... ... @@ -115,7 +115,7 @@
115 115 }
116 116  
117 117 function goLogin() {
118   - userStore.loginOut(true);
  118 + userStore.logout(true);
119 119 lockStore.resetLockInfo();
120 120 }
121 121  
... ... @@ -287,6 +287,7 @@
287 287  
288 288 &-img {
289 289 width: 70px;
  290 + margin: 0 auto;
290 291 border-radius: 50%;
291 292 }
292 293  
... ...
yarn.lock
... ... @@ -9370,12 +9370,13 @@ vite-plugin-theme@^0.4.3:
9370 9370 es-module-lexer "^0.3.26"
9371 9371 tinycolor2 "^1.4.2"
9372 9372  
9373   -vite-plugin-windicss@0.2.2:
9374   - version "0.2.2"
9375   - resolved "https://registry.npmjs.org/vite-plugin-windicss/-/vite-plugin-windicss-0.2.2.tgz#2abf1533153f5dc214a9e1a06fb58274e5892c19"
9376   - integrity sha512-P+iyrcuLjLfjiYP+bBisfKbg9bmeQMUBpjsTFJ9kWWX2fyqo968CHmS3euz+MzRcK5ZECccpOxx60ZXzc12VAw==
  9373 +vite-plugin-windicss@0.3.3:
  9374 + version "0.3.3"
  9375 + resolved "https://registry.npmjs.org/vite-plugin-windicss/-/vite-plugin-windicss-0.3.3.tgz#9ff2fc485dd5cf1717cde6eed462fd9893ea9eab"
  9376 + integrity sha512-2gm0sTexkmvx9PR4NP1UESly8hX2souOruQztu1qghfw4M3tlUUvwneRpJG5HVJCKCctmAgYRcW4e04TE5R1fA==
9377 9377 dependencies:
9378 9378 fast-glob "^3.2.5"
  9379 + micromatch "^4.0.2"
9379 9380 windicss "^2.1.11"
9380 9381  
9381 9382 vite@2.0.1:
... ...