SettingDrawer.tsx 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546
  1. import { defineComponent, computed, unref, ref } from 'vue';
  2. import { BasicDrawer } from '/@/components/Drawer/index';
  3. import { Divider, Switch, Tooltip, InputNumber, Select } from 'ant-design-vue';
  4. import Button from '/@/components/Button/index.vue';
  5. import { MenuModeEnum, MenuTypeEnum } from '/@/enums/menuEnum';
  6. import { CopyOutlined, RedoOutlined, CheckOutlined } from '@ant-design/icons-vue';
  7. import { appStore } from '/@/store/modules/app';
  8. import { userStore } from '/@/store/modules/user';
  9. import { ProjectConfig } from '/@/types/config';
  10. import { useMessage } from '/@/hooks/web/useMessage';
  11. import { useCopyToClipboard } from '/@/hooks/web/useCopyToClipboard';
  12. import defaultSetting from '/@/settings/projectSetting';
  13. import mixImg from '/@/assets/images/layout/menu-mix.svg';
  14. import sidebarImg from '/@/assets/images/layout/menu-sidebar.svg';
  15. import menuTopImg from '/@/assets/images/layout/menu-top.svg';
  16. import { updateColorWeak, updateGrayMode } from '/@/setup/theme';
  17. import { baseHandler } from './handler';
  18. import {
  19. HandlerEnum,
  20. contentModeOptions,
  21. topMenuAlignOptions,
  22. menuTriggerOptions,
  23. routerTransitionOptions,
  24. } from './const';
  25. import { HEADER_PRESET_BG_COLOR_LIST, SIDE_BAR_BG_COLOR_LIST } from '/@/settings/colorSetting';
  26. interface SwitchOptions {
  27. config?: DeepPartial<ProjectConfig>;
  28. def?: any;
  29. disabled?: boolean;
  30. handler?: Fn;
  31. }
  32. interface SelectConfig {
  33. options?: LabelValueOptions;
  34. def?: any;
  35. disabled?: boolean;
  36. handler?: Fn;
  37. }
  38. interface ThemeOptions {
  39. def?: string;
  40. handler?: Fn;
  41. }
  42. export default defineComponent({
  43. name: 'SettingDrawer',
  44. setup(_, { attrs }) {
  45. const { createSuccessModal, createMessage } = useMessage();
  46. const getProjectConfigRef = computed(() => {
  47. return appStore.getProjectConfig;
  48. });
  49. const getIsHorizontalRef = computed(() => {
  50. return unref(getProjectConfigRef).menuSetting.mode === MenuModeEnum.HORIZONTAL;
  51. });
  52. const getShowHeaderRef = computed(() => {
  53. return unref(getProjectConfigRef).headerSetting.show;
  54. });
  55. const getShowMenuRef = computed(() => {
  56. return unref(getProjectConfigRef).menuSetting.show && !unref(getIsHorizontalRef);
  57. });
  58. const getShowTabsRef = computed(() => {
  59. return unref(getProjectConfigRef).multiTabsSetting.show;
  60. });
  61. function handleCopy() {
  62. const { isSuccessRef } = useCopyToClipboard(
  63. JSON.stringify(unref(getProjectConfigRef), null, 2)
  64. );
  65. unref(isSuccessRef) &&
  66. createSuccessModal({
  67. title: '操作成功',
  68. content: '复制成功,请到 src/settings/projectSetting.ts 中修改配置!',
  69. });
  70. }
  71. function handleResetSetting() {
  72. try {
  73. appStore.commitProjectConfigState(defaultSetting);
  74. const { colorWeak, grayMode } = defaultSetting;
  75. // updateTheme(themeColor);
  76. updateColorWeak(colorWeak);
  77. updateGrayMode(grayMode);
  78. createMessage.success('重置成功!');
  79. } catch (error) {
  80. createMessage.error(error);
  81. }
  82. }
  83. function handleClearAndRedo() {
  84. localStorage.clear();
  85. userStore.resumeAllState();
  86. location.reload();
  87. }
  88. function renderSidebar() {
  89. const {
  90. menuSetting: { type, split },
  91. } = unref(getProjectConfigRef);
  92. const typeList = ref([
  93. {
  94. title: '左侧菜单模式',
  95. mode: MenuModeEnum.INLINE,
  96. type: MenuTypeEnum.SIDEBAR,
  97. src: sidebarImg,
  98. },
  99. {
  100. title: '混合模式',
  101. mode: MenuModeEnum.INLINE,
  102. type: MenuTypeEnum.MIX,
  103. src: mixImg,
  104. },
  105. {
  106. title: '顶部菜单模式',
  107. mode: MenuModeEnum.HORIZONTAL,
  108. type: MenuTypeEnum.TOP_MENU,
  109. src: menuTopImg,
  110. },
  111. ]);
  112. return [
  113. <div class={`setting-drawer__siderbar`}>
  114. {unref(typeList).map((item) => {
  115. const { title, type: ItemType, mode, src } = item;
  116. return (
  117. <Tooltip title={title} placement="bottom" key={title}>
  118. {{
  119. default: () => (
  120. <div
  121. onClick={baseHandler.bind(null, HandlerEnum.CHANGE_LAYOUT, {
  122. mode: mode,
  123. type: ItemType,
  124. split: unref(getIsHorizontalRef) ? false : undefined,
  125. })}
  126. >
  127. <CheckOutlined class={['check-icon', type === ItemType ? 'active' : '']} />
  128. <img src={src} />
  129. </div>
  130. ),
  131. }}
  132. </Tooltip>
  133. );
  134. })}
  135. </div>,
  136. renderSwitchItem('分割菜单', {
  137. handler: (e) => {
  138. baseHandler(HandlerEnum.MENU_SPLIT, e);
  139. },
  140. def: split,
  141. disabled: !unref(getShowMenuRef) || type !== MenuTypeEnum.MIX,
  142. }),
  143. // renderSelectItem('顶栏主题', {
  144. // handler: (e) => {
  145. // baseHandler(HandlerEnum.HEADER_THEME, e);
  146. // },
  147. // def: headerTheme,
  148. // options: themeOptions,
  149. // disabled: !unref(getShowHeaderRef),
  150. // }),
  151. // renderSelectItem('菜单主题', {
  152. // handler: (e) => {
  153. // baseHandler(HandlerEnum.MENU_THEME, e);
  154. // },
  155. // def: menuTheme,
  156. // options: themeOptions,
  157. // disabled: !unref(getShowMenuRef),
  158. // }),
  159. ];
  160. }
  161. /**
  162. * @description:
  163. */
  164. function renderFeatures() {
  165. const {
  166. contentMode,
  167. headerSetting: { fixed },
  168. menuSetting: {
  169. hasDrag,
  170. collapsed,
  171. showSearch,
  172. menuWidth,
  173. topMenuAlign,
  174. collapsedShowTitle,
  175. trigger,
  176. accordion,
  177. } = {},
  178. } = appStore.getProjectConfig;
  179. return [
  180. renderSwitchItem('侧边菜单拖拽', {
  181. handler: (e) => {
  182. baseHandler(HandlerEnum.MENU_HAS_DRAG, e);
  183. },
  184. def: hasDrag,
  185. disabled: !unref(getShowMenuRef),
  186. }),
  187. renderSwitchItem('侧边菜单搜索', {
  188. handler: (e) => {
  189. baseHandler(HandlerEnum.MENU_SHOW_SEARCH, e);
  190. },
  191. def: showSearch,
  192. disabled: !unref(getShowMenuRef),
  193. }),
  194. renderSwitchItem('侧边菜单手风琴模式', {
  195. handler: (e) => {
  196. baseHandler(HandlerEnum.MENU_ACCORDION, e);
  197. },
  198. def: accordion,
  199. disabled: !unref(getShowMenuRef),
  200. }),
  201. renderSwitchItem('折叠菜单', {
  202. handler: (e) => {
  203. baseHandler(HandlerEnum.MENU_COLLAPSED, e);
  204. },
  205. def: collapsed,
  206. disabled: !unref(getShowMenuRef),
  207. }),
  208. renderSwitchItem('折叠菜单显示名称', {
  209. handler: (e) => {
  210. baseHandler(HandlerEnum.MENU_COLLAPSED_SHOW_TITLE, e);
  211. },
  212. def: collapsedShowTitle,
  213. disabled: !unref(getShowMenuRef) || !collapsed,
  214. }),
  215. renderSwitchItem('固定header', {
  216. handler: (e) => {
  217. baseHandler(HandlerEnum.HEADER_FIXED, e);
  218. },
  219. def: fixed,
  220. disabled: !unref(getShowHeaderRef),
  221. }),
  222. renderSelectItem('顶部菜单布局', {
  223. handler: (e) => {
  224. baseHandler(HandlerEnum.MENU_TOP_ALIGN, e);
  225. },
  226. def: topMenuAlign,
  227. options: topMenuAlignOptions,
  228. disabled: !unref(getShowHeaderRef),
  229. }),
  230. renderSelectItem('菜单折叠按钮', {
  231. handler: (e) => {
  232. baseHandler(HandlerEnum.MENU_TRIGGER, e);
  233. },
  234. def: trigger,
  235. options: menuTriggerOptions,
  236. }),
  237. renderSelectItem('内容区域宽度', {
  238. handler: (e) => {
  239. baseHandler(HandlerEnum.CONTENT_MODE, e);
  240. },
  241. def: contentMode,
  242. options: contentModeOptions,
  243. }),
  244. <div class={`setting-drawer__cell-item`}>
  245. <span>自动锁屏</span>
  246. <InputNumber
  247. style="width:120px"
  248. size="small"
  249. min={0}
  250. onChange={(e: any) => {
  251. baseHandler(HandlerEnum.LOCK_TIME, e);
  252. }}
  253. defaultValue={appStore.getProjectConfig.lockTime}
  254. formatter={(value: string) => {
  255. if (parseInt(value) === 0) {
  256. return '0(不自动锁屏)';
  257. }
  258. return `${value}分钟`;
  259. }}
  260. />
  261. </div>,
  262. <div class={`setting-drawer__cell-item`}>
  263. <span>菜单展开宽度</span>
  264. <InputNumber
  265. style="width:120px"
  266. size="small"
  267. max={600}
  268. min={100}
  269. step={10}
  270. disabled={!unref(getShowMenuRef)}
  271. defaultValue={menuWidth}
  272. formatter={(value: string) => `${parseInt(value)}px`}
  273. onChange={(e: any) => {
  274. baseHandler(HandlerEnum.MENU_WIDTH, e);
  275. }}
  276. />
  277. </div>,
  278. ];
  279. }
  280. function renderTransition() {
  281. const { routerTransition, openRouterTransition, openPageLoading } = appStore.getProjectConfig;
  282. return (
  283. <>
  284. {renderSwitchItem('页面切换loading', {
  285. handler: (e) => {
  286. baseHandler(HandlerEnum.OPEN_PAGE_LOADING, e);
  287. },
  288. def: openPageLoading,
  289. })}
  290. {renderSwitchItem('切换动画', {
  291. handler: (e) => {
  292. baseHandler(HandlerEnum.OPEN_ROUTE_TRANSITION, e);
  293. },
  294. def: openRouterTransition,
  295. })}
  296. {renderSelectItem('路由动画', {
  297. handler: (e) => {
  298. baseHandler(HandlerEnum.ROUTER_TRANSITION, e);
  299. },
  300. def: routerTransition,
  301. options: routerTransitionOptions,
  302. disabled: !openRouterTransition,
  303. })}
  304. </>
  305. );
  306. }
  307. function renderContent() {
  308. const {
  309. grayMode,
  310. colorWeak,
  311. fullContent,
  312. showLogo,
  313. headerSetting: { show: showHeader },
  314. menuSetting: { show: showMenu },
  315. multiTabsSetting: { show: showMultiple, showQuick, showIcon: showTabIcon },
  316. showBreadCrumb,
  317. showBreadCrumbIcon,
  318. } = unref(getProjectConfigRef);
  319. return [
  320. renderSwitchItem('面包屑', {
  321. handler: (e) => {
  322. baseHandler(HandlerEnum.SHOW_BREADCRUMB, e);
  323. },
  324. def: showBreadCrumb,
  325. disabled: !unref(getShowHeaderRef),
  326. }),
  327. renderSwitchItem('面包屑图标', {
  328. handler: (e) => {
  329. baseHandler(HandlerEnum.SHOW_BREADCRUMB_ICON, e);
  330. },
  331. def: showBreadCrumbIcon,
  332. disabled: !unref(getShowHeaderRef),
  333. }),
  334. renderSwitchItem('标签页', {
  335. handler: (e) => {
  336. baseHandler(HandlerEnum.TABS_SHOW, e);
  337. },
  338. def: showMultiple,
  339. }),
  340. renderSwitchItem('标签页快捷按钮', {
  341. handler: (e) => {
  342. baseHandler(HandlerEnum.TABS_SHOW_QUICK, e);
  343. },
  344. def: showQuick,
  345. disabled: !unref(getShowTabsRef),
  346. }),
  347. renderSwitchItem('标签页图标', {
  348. handler: (e) => {
  349. baseHandler(HandlerEnum.TABS_SHOW_ICON, e);
  350. },
  351. def: showTabIcon,
  352. disabled: !unref(getShowTabsRef),
  353. }),
  354. renderSwitchItem('左侧菜单', {
  355. handler: (e) => {
  356. baseHandler(HandlerEnum.MENU_SHOW_SIDEBAR, e);
  357. },
  358. def: showMenu,
  359. disabled: unref(getIsHorizontalRef),
  360. }),
  361. renderSwitchItem('顶栏', {
  362. handler: (e) => {
  363. baseHandler(HandlerEnum.HEADER_SHOW, e);
  364. },
  365. def: showHeader,
  366. }),
  367. renderSwitchItem('Logo', {
  368. handler: (e) => {
  369. baseHandler(HandlerEnum.SHOW_LOGO, e);
  370. },
  371. def: showLogo,
  372. }),
  373. renderSwitchItem('全屏内容', {
  374. handler: (e) => {
  375. baseHandler(HandlerEnum.FULL_CONTENT, e);
  376. },
  377. def: fullContent,
  378. }),
  379. renderSwitchItem('灰色模式', {
  380. handler: (e) => {
  381. baseHandler(HandlerEnum.GRAY_MODE, e);
  382. },
  383. def: grayMode,
  384. }),
  385. renderSwitchItem('色弱模式', {
  386. handler: (e) => {
  387. baseHandler(HandlerEnum.COLOR_WEAK, e);
  388. },
  389. def: colorWeak,
  390. }),
  391. ];
  392. }
  393. function renderSelectItem(text: string, config?: SelectConfig) {
  394. const { handler, def, disabled = false, options } = config || {};
  395. const opt = def ? { value: def, defaultValue: def } : {};
  396. return (
  397. <div class={`setting-drawer__cell-item`}>
  398. <span>{text}</span>
  399. <Select
  400. {...opt}
  401. disabled={disabled}
  402. size="small"
  403. style={{ width: '120px' }}
  404. onChange={(e) => {
  405. handler && handler(e);
  406. }}
  407. options={options}
  408. />
  409. </div>
  410. );
  411. }
  412. function renderSwitchItem(text: string, options?: SwitchOptions) {
  413. const { handler, def, disabled = false } = options || {};
  414. const opt = def ? { checked: def } : {};
  415. return (
  416. <div class={`setting-drawer__cell-item`}>
  417. <span>{text}</span>
  418. <Switch
  419. {...opt}
  420. disabled={disabled}
  421. onChange={(e: any) => {
  422. handler && handler(e);
  423. }}
  424. checkedChildren="开"
  425. unCheckedChildren="关"
  426. />
  427. </div>
  428. );
  429. }
  430. function renderTheme() {
  431. const { headerBgColor, menuBgColor } = unref(getProjectConfigRef);
  432. return (
  433. <>
  434. <Divider>{() => '顶栏主题'}</Divider>
  435. {renderThemeItem(HEADER_PRESET_BG_COLOR_LIST, {
  436. def: headerBgColor,
  437. handler: (e) => {
  438. baseHandler(HandlerEnum.HEADER_THEME, e);
  439. },
  440. })}
  441. <Divider>{() => '菜单主题'}</Divider>
  442. {renderThemeItem(SIDE_BAR_BG_COLOR_LIST, {
  443. def: menuBgColor,
  444. handler: (e) => {
  445. baseHandler(HandlerEnum.MENU_THEME, e);
  446. },
  447. })}
  448. </>
  449. );
  450. }
  451. function renderThemeItem(colorList: string[], opt: ThemeOptions) {
  452. const { def, handler } = opt;
  453. return (
  454. <div class={`setting-drawer__theme-item`}>
  455. {colorList.map((item) => {
  456. return (
  457. <span
  458. onClick={() => handler && handler(item)}
  459. key={item}
  460. class={[def === item ? 'active' : '']}
  461. style={{
  462. background: item,
  463. }}
  464. >
  465. <CheckOutlined class="icon" />
  466. </span>
  467. );
  468. })}
  469. </div>
  470. );
  471. }
  472. return () => (
  473. <BasicDrawer {...attrs} title="项目配置" width={300} wrapClassName="setting-drawer">
  474. {{
  475. default: () => (
  476. <>
  477. <Divider>{() => '导航栏模式'}</Divider>
  478. {renderSidebar()}
  479. {renderTheme()}
  480. <Divider>{() => '界面功能'}</Divider>
  481. {renderFeatures()}
  482. <Divider>{() => '界面显示'}</Divider>
  483. {renderContent()}
  484. <Divider>{() => '切换动画'}</Divider>
  485. {renderTransition()}
  486. <Divider />
  487. <div class="setting-drawer__footer">
  488. <Button type="primary" block onClick={handleCopy}>
  489. {() => (
  490. <>
  491. <CopyOutlined class="mr-2" />
  492. 拷贝
  493. </>
  494. )}
  495. </Button>
  496. <Button block class="mt-2" onClick={handleResetSetting} color="warning">
  497. {() => (
  498. <>
  499. <RedoOutlined class="mr-2" />
  500. 重置
  501. </>
  502. )}
  503. </Button>
  504. <Button block class="mt-2" onClick={handleClearAndRedo} color="error">
  505. {() => (
  506. <>
  507. <RedoOutlined class="mr-2" />
  508. 清空缓存并返回登录页
  509. </>
  510. )}
  511. </Button>
  512. </div>
  513. </>
  514. ),
  515. }}
  516. </BasicDrawer>
  517. );
  518. },
  519. });