diff --git a/.eslintrc.cjs b/.eslintrc.cjs index c711e9dc..73a97f64 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -33,6 +33,7 @@ module.exports = { defineExpose: 'readonly', withDefaults: 'readonly', defineOptions: 'readonly', + defineModel: 'readonly', }, rules: { 'no-undefined': ['error'], diff --git a/CHANGELOG.md b/CHANGELOG.md index 700d007a..cd051dee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,36 @@ # CHANGE LOG +## 4.4.2 + +这是一个具有破坏性更新的版本,如果你使用了该模板,那么你需要做一些改动。 + +详细拆分 `hooks` 包的方法。以前的划分方式不太合理,所以进行了一次大的重构。并且新增了一些方法。现在按照方法功能进行分包,更加详细。 + +剔除 `h` 函数渲染,因为该方法不会受到 `vue` 的编译优化。 + +补充了一些代码的注释(慢慢还账-,-)。 + +### Feats + +- 重新划分 `hooks` 包,按照功能进行拆分,并且新增一些包 + - `useAppMenu` 方法更名为 `useAppNavigation` + - `useMenuTag` 方法更名为 `useSiderBar` + - `useRootRoute` 方法更名为 `useAppRoot` + - `useMainPage` 包移除 + - 新增 `useMaximize`, `useSpinning`, `useTheme`, `useWatermark` 方法 + - 新增 `components` 包,用于存放模板二次封装组件、`NaiveUI` 组件的一些 `hooks` 方法 + - 每个方法包导出对应的 `ReturnType` 类型 +- `Breadcrumb` 组件新增过渡效果,现在切换路由时会有过渡动画,视觉效果更友好 +- 移除 `getVariable` 方法 +- 移除 `utils/element` 包部分方法 + - `on` + - `off` +- 移除 `changeSwitcher` 方法,使用 `updateSettingState` 方法代替 + +## Fixes + +- 修复 `setRootRoute` 方法执行时提示只读错误导致不能正常修改的问题 + ## 4.4.1 更新 `vite` 版本至 `5.0.4`。同步修复了一些小问题。 @@ -77,7 +108,7 @@ 紧跟尤大大脚步,更新 `vite` 版本至 `5.0.0` 版本!与此同时,更新了配套所有插件! -更新 ROOT_ROUTE 的一些使用方法,该配置方法与原有的方式不变,但是有一个新的功能点则是,该配置项会传递给 global-variable 的 globalRootRoute 属性。并且更改模板原有获取 path 的方法,改为响应式获取。当你要进行动态的维护 Root Route 的时候,该方法可能可以帮助到你 `useRootRoute`。 +更新 ROOT_ROUTE 的一些使用方法,该配置方法与原有的方式不变,但是有一个新的功能点则是,该配置项会传递给 global-variable 的 globalRootRoute 属性。并且更改模板原有获取 path 的方法,改为响应式获取。当你要进行动态的维护 Root Route 的时候,该方法可能可以帮助到你 `useAppRoot`。 如果你在更新版本后出现一些奇奇怪怪的问题,不要犹豫,直接删除 `node_modules` 后再重新安装依赖,这是缓存导致的问题。 diff --git a/README-ZH.md b/README-ZH.md index 1e60110d..5d3051e7 100644 --- a/README-ZH.md +++ b/README-ZH.md @@ -9,14 +9,14 @@ 简体中文 | [English](https://github.com/XiaoDaiGua-Ray/ray-template/blob/main/README.md) -一个 `免费`、`高效`、`特性完整` 并且基于 vite4.x & ts(x) & pinia & vue3.x 等最新技术的中后台模板。 +一个 `免费`、`高效`、`特性完整` 并且基于 vite5.x & ts(x) & pinia & vue3.x 等最新技术的中后台模板。 ## ✨ 特性 - **靠爱发电**:几乎包含市面常见的模板特性并且全部免费使用 -- **最新技术栈**:使用 vue3.x/vite4.x/pinia 等前端前沿技术开发 +- **最新技术栈**:使用 vue3.x/vite5.x/pinia 等前端前沿技术开发 - **TypeScript**:应用程序级 JavaScript 的语言 - **主题**:可配置的主题 - **国际化**:内置完善的国际化方案 diff --git a/README.md b/README.md index 19ceea0b..5681e392 100644 --- a/README.md +++ b/README.md @@ -9,14 +9,14 @@ English | [简体中文](https://github.com/XiaoDaiGua-Ray/ray-template/blob/main/README-ZH.md) -A `free`, `efficient`, `complete with features` middle and backend template based on the latest technologies such as vite4.x & ts(x) & pinia & vue3.x. +A `free`, `efficient`, `complete with features` middle and backend template based on the latest technologies such as vite5.x & ts(x) & pinia & vue3.x. ## ✨ Feature - **Power by love**: Contains almost all common template features on the market and all are free to use. -- **Latest Technology Stack**:Developed using front-end cutting-edge technologies such as vue3.x/vite4.x/pinia. +- **Latest Technology Stack**:Developed using front-end cutting-edge technologies such as vue3.x/vite5.x/pinia. - **TypeScript**:The language for application-level JavaScript. - **App Theme**:Configurable themes. - **Globalization**:Built-in complete internationalization solution. diff --git a/package.json b/package.json index e4419021..f25c3512 100755 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "ray-template", "private": false, - "version": "4.4.1", + "version": "4.4.2", "type": "module", "engines": { "node": "^18.0.0 || >=20.0.0", diff --git a/src/app-components/app/AppLockScreen/components/LockScreen/index.tsx b/src/app-components/app/AppLockScreen/components/LockScreen/index.tsx index e2517328..d3c1bfdf 100644 --- a/src/app-components/app/AppLockScreen/components/LockScreen/index.tsx +++ b/src/app-components/app/AppLockScreen/components/LockScreen/index.tsx @@ -27,7 +27,7 @@ const LockScreen = defineComponent({ const inputInstRef = ref(null) const { setLockAppScreen } = useAppLockScreen() - const { changeSwitcher } = useSettingActions() + const { updateSettingState } = useSettingActions() const state = reactive({ lockCondition: useCondition(), @@ -38,7 +38,7 @@ const LockScreen = defineComponent({ formInstRef.value?.validate((error) => { if (!error) { setLockAppScreen(true) - changeSwitcher(true, 'lockScreenSwitch') + updateSettingState('lockScreenSwitch', true) state.lockCondition = useCondition() } diff --git a/src/app-components/app/AppLockScreen/components/UnlockScreen/index.tsx b/src/app-components/app/AppLockScreen/components/UnlockScreen/index.tsx index 7054559f..cceb6f7a 100644 --- a/src/app-components/app/AppLockScreen/components/UnlockScreen/index.tsx +++ b/src/app-components/app/AppLockScreen/components/UnlockScreen/index.tsx @@ -29,7 +29,7 @@ export default defineComponent({ const inputInstRef = ref(null) const { logout } = useSigningActions() - const { changeSwitcher } = useSettingActions() + const { updateSettingState } = useSettingActions() const { setLockAppScreen } = useAppLockScreen() const { isTabletOrSmaller } = useDevice() @@ -64,7 +64,7 @@ export default defineComponent({ onPositiveClick: () => { logout() setTimeout(() => { - changeSwitcher(false, 'lockScreenSwitch') + updateSettingState('lockScreenSwitch', false) }) }, }) @@ -75,7 +75,7 @@ export default defineComponent({ formRef.value?.validate((error) => { if (!error) { setLockAppScreen(false) - changeSwitcher(false, 'lockScreenSwitch') + updateSettingState('lockScreenSwitch', false) state.lockCondition = useCondition() } diff --git a/src/app-components/app/AppLockScreen/index.tsx b/src/app-components/app/AppLockScreen/index.tsx index 8ec6f788..d353aa76 100644 --- a/src/app-components/app/AppLockScreen/index.tsx +++ b/src/app-components/app/AppLockScreen/index.tsx @@ -28,12 +28,12 @@ const AppLockScreen = defineComponent({ name: 'AppLockScreen', setup() { const { getLockAppScreen } = useAppLockScreen() - const { changeSwitcher } = useSettingActions() + const { updateSettingState } = useSettingActions() const { getLockScreenSwitch } = useSettingGetters() const lockScreenSwitchRef = computed({ get: () => getLockScreenSwitch.value, set: (val) => { - changeSwitcher(val, 'lockScreenSwitch') + updateSettingState('lockScreenSwitch', val) }, }) diff --git a/src/app-components/provider/AppWatermarkProvider/index.tsx b/src/app-components/provider/AppWatermarkProvider/index.tsx index cf749c5e..41784da8 100644 --- a/src/app-components/provider/AppWatermarkProvider/index.tsx +++ b/src/app-components/provider/AppWatermarkProvider/index.tsx @@ -15,25 +15,29 @@ * * 该组件启用时,会在全局(包括首页)展示 * 如果你不希望在登录页显示,可以手动将该组件放置于 Layout 中 + * + * 当然你也可以通过 useWatermark hook 自定义控制水印的显示以及内容 */ import { NWatermark } from 'naive-ui' -import { APP_WATERMARK_CONFIG } from '@/app-config/appConfig' import { useSettingGetters } from '@/store' export default defineComponent({ name: 'AppWatermarkProvider', setup() { - const { getWatermarkSwitch } = useSettingGetters() + const { getWatermarkSwitch, getWatermarkConfig } = useSettingGetters() return { getWatermarkSwitch, + getWatermarkConfig, } }, render() { - return this.getWatermarkSwitch ? ( - + const { getWatermarkConfig, getWatermarkSwitch } = this + + return getWatermarkSwitch ? ( + ) : null }, }) diff --git a/src/app-config/appConfig.ts b/src/app-config/appConfig.ts index b41370f8..eaac340a 100644 --- a/src/app-config/appConfig.ts +++ b/src/app-config/appConfig.ts @@ -52,7 +52,7 @@ export const PRE_LOADING_CONFIG: PreloadingConfig = { * 该变量的值,会传递给 globalRootRoute * 这么做也是为了能够在兼容老版本的模板,并且也是为了能够动态的维护根路由信息 * - * 有些时候,如果你希望动态的维护 Root Route 信息,可以使用 useRootRoute 方法 + * 有些时候,如果你希望动态的维护 Root Route 信息,可以使用 useAppRoot 方法 */ export const ROOT_ROUTE: RootRoute = { name: 'Dashboard', diff --git a/src/components/RIframe/src/index.tsx b/src/components/RIframe/src/index.tsx index 57dcd8b1..a268ece6 100644 --- a/src/components/RIframe/src/index.tsx +++ b/src/components/RIframe/src/index.tsx @@ -13,9 +13,10 @@ import './index.scss' import { NSpin } from 'naive-ui' -import { completeSize, on, off } from '@use-utils/element' +import { completeSize } from '@use-utils/element' import { call } from '@/utils/vue' import props from './props' +import { useEventListener } from '@vueuse/core' export default defineComponent({ name: 'RIframe', @@ -53,19 +54,13 @@ export default defineComponent({ } } + useEventListener(iframeRef, 'load', iframeLoadSuccess) + useEventListener(iframeRef, 'error', iframeLoadError) + expose({ iframeInst: iframeRef, }) - onMounted(() => { - on(iframeRef.value, 'load', iframeLoadSuccess.bind(this)) - on(iframeRef.value, 'error', iframeLoadError) - }) - onBeforeUnmount(() => { - off(iframeRef.value, 'load', iframeLoadSuccess) - off(iframeRef.value, 'error', iframeLoadError) - }) - return { cssVars, iframeRef, diff --git a/src/components/RModal/src/utils.ts b/src/components/RModal/src/utils.ts index 345ccb9e..9fc51871 100644 --- a/src/components/RModal/src/utils.ts +++ b/src/components/RModal/src/utils.ts @@ -9,6 +9,8 @@ import type { ModalProps } from 'naive-ui' * * 根据预设模态框设置拖拽效果 * 但是该效果有且仅有 card, dialog 有效 + * + * 默认添加 30ms 延迟,避免诡异问题 */ export const setupDraggable = ( bindModal: HTMLElement, diff --git a/src/components/RTable/src/Table.tsx b/src/components/RTable/src/Table.tsx index febb0d29..75f36ea4 100644 --- a/src/components/RTable/src/Table.tsx +++ b/src/components/RTable/src/Table.tsx @@ -35,13 +35,23 @@ export default defineComponent({ const rTableInst = ref(null) const wrapperRef = ref(null) - const uuidWrapper = uuid(16) - const uuidTable = uuid(16) + const uuidWrapper = uuid(16) // wrapper id + const uuidTable = uuid(16) // table id + /** + * + * x: 横坐标 + * y: 纵坐标 + * showContextMenu: 是否显示右键菜单 + */ const contextMenuReactive = reactive({ x: 0, y: 0, showContextMenu: false, }) + /** + * + * size: table size,内部私有状态管理 + */ const privateReactive = reactive({ size: props.size, }) @@ -90,10 +100,22 @@ export default defineComponent({ } } + /** + * + * @param size table size + * + * 修改 table size + */ const changeTableSize = (size: ComponentSize) => { privateReactive.size = size } + /** + * + * @param options table columns + * + * 更新 table columns,同时触发 onUpdateColumns 和 onUpdate:columns 事件 + */ const updateTableColumn = (options: CType[]) => { const { onUpdateColumns, 'onUpdate:columns': $onUpdateColumns } = props @@ -105,6 +127,11 @@ export default defineComponent({ } } + /** + * + * 处理自定义的 toolOptions + * 匹配所有符合条件的 toolOptions,然后执行 + */ const renderToolOptions = () => { const { toolOptions } = props @@ -113,6 +140,12 @@ export default defineComponent({ .map((curr) => (typeof curr === 'function' ? curr() : curr)) } + /** + * + * @param p props + * + * 处理 toolOptions,合并渲染所有的 toolOptions + */ const tool = (p: typeof props) => { const renderDefaultToolOptions = () => ( <> @@ -163,7 +196,6 @@ export default defineComponent({ } }, render() { - /* eslint-disable @typescript-eslint/no-explicit-any */ const { tool } = this return ( @@ -208,6 +240,7 @@ export default defineComponent({ }), 'header-extra': () => ( + {/* eslint-disable @typescript-eslint/no-explicit-any */} {tool(this.$props as any)} ), diff --git a/src/components/RTable/src/components/C.tsx b/src/components/RTable/src/components/C.tsx index 2cac9c2e..dd1a3462 100644 --- a/src/components/RTable/src/components/C.tsx +++ b/src/components/RTable/src/components/C.tsx @@ -32,11 +32,9 @@ import type { MaybeArray } from '@/types/modules/utils' type FixedClick = (type: 'left' | 'right', option: C, index: number) => void -const renderSwitcherIcon = () => - h(RIcon, { - name: 'draggable', - size: config.tableIconSize, - }) +const renderSwitcherIcon = () => ( + +) const RowIconRender = ({ icon, diff --git a/src/components/index.ts b/src/components/index.ts index e85a4aee..1ae43e8e 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -1,3 +1,4 @@ +// 导出所有自定义组件 export * from './RChart' export * from './RCollapseGrid' export * from './RIcon' @@ -7,3 +8,11 @@ export * from './RMoreDropdown' export * from './RQRCode' export * from './RTable' export * from './RTransitionComponent' + +// 导出自定义组件类型 +export type * from './RChart/src/type' +export type * from './RCollapseGrid/src/type' +export type * from './RIframe/src/type' +export type * from './RQRCode/src/type' +export type * from './RTable/src/type' +export type * from './RTransitionComponent/src/type' diff --git a/src/directives/modules/debounce/index.ts b/src/directives/modules/debounce/index.ts index 144fc9d6..0ed01513 100644 --- a/src/directives/modules/debounce/index.ts +++ b/src/directives/modules/debounce/index.ts @@ -15,7 +15,7 @@ */ import { debounce } from 'lodash-es' -import { on, off } from '@use-utils/element' +import { useEventListener } from '@vueuse/core' import type { DebounceBindingOptions } from './type' import type { AnyFC } from '@/types/modules/utils' @@ -27,6 +27,7 @@ const debounceDirective: CustomDirectiveFC< DebounceBindingOptions > = () => { let debounceFunction: DebouncedFunc | null + let cleanup: () => void return { beforeMount: (el, { value }) => { @@ -38,14 +39,14 @@ const debounceDirective: CustomDirectiveFC< debounceFunction = debounce(func, wait, Object.assign({}, options)) - on(el, trigger, debounceFunction) + cleanup = useEventListener(el, trigger, debounceFunction) }, beforeUnmount: (el, { value }) => { const { trigger = 'click' } = value if (debounceFunction) { debounceFunction.cancel() - off(el, trigger, debounceFunction) + cleanup?.() } debounceFunction = null diff --git a/src/directives/modules/throttle/index.ts b/src/directives/modules/throttle/index.ts index 6f58d124..86479a36 100644 --- a/src/directives/modules/throttle/index.ts +++ b/src/directives/modules/throttle/index.ts @@ -15,7 +15,7 @@ */ import { throttle } from 'lodash-es' -import { on, off } from '@use-utils/element' +import { useEventListener } from '@vueuse/core' import type { ThrottleBindingOptions } from './type' import type { AnyFC } from '@/types/modules/utils' @@ -27,6 +27,7 @@ const throttleDirective: CustomDirectiveFC< ThrottleBindingOptions > = () => { let throttleFunction: DebouncedFunc | null + let cleanup: () => void return { beforeMount: (el, { value }) => { @@ -38,14 +39,12 @@ const throttleDirective: CustomDirectiveFC< throttleFunction = throttle(func, wait, Object.assign({}, options)) - on(el, trigger, throttleFunction) + useEventListener(el, trigger, throttleFunction) }, - beforeUnmount: (el, { value }) => { - const { trigger = 'click' } = value - + beforeUnmount: () => { if (throttleFunction) { throttleFunction.cancel() - off(el, trigger, throttleFunction) + cleanup?.() } throttleFunction = null diff --git a/src/global-variable/index.ts b/src/global-variable/index.ts index 44df445d..d98eb5cd 100644 --- a/src/global-variable/index.ts +++ b/src/global-variable/index.ts @@ -9,10 +9,6 @@ * @remark 今天也是元气满满撸代码的一天 */ -import { setVariable, getVariable, getVariableToRefs } from './variable' +export * from './variable' -import type { VariableState, VariableStateKey } from './variable' - -export { setVariable, getVariable, getVariableToRefs } - -export type { VariableState, VariableStateKey } +export type * from './variable' diff --git a/src/global-variable/variable.ts b/src/global-variable/variable.ts index 6cb34e59..a29d5908 100644 --- a/src/global-variable/variable.ts +++ b/src/global-variable/variable.ts @@ -11,27 +11,37 @@ /** * - * 存放全局临时变量,脱离 pinia 使用的变量 + * 存放全局响应式变量,脱离 pinia 使用的变量 * 简单的全局变量,并不需要复杂的控制流程 * * 使用方法 + * * @example - * - * 获取普通变量 - * getVariable('target key', 'default value') - * * 获取响应式变量 * getVariableToRefs('target key') * * 更改 state 变量 * setVariable('key', 'value') + * + * 创建响应式说仓库 + * createVariableState({ your state }) */ -import { ROOT_ROUTE } from '@/app-config/appConfig' +import { ROOT_ROUTE, APP_WATERMARK_CONFIG } from '@/app-config/appConfig' import { cloneDeep } from 'lodash-es' import type { AnyFC } from '@/types/modules/utils' +import type { Mutable } from '@/types/modules/helper' +/** + * + * 全局响应式变量 + * 不推荐直接访问、设置该 state。并且更不建议在组件中直接使用该 state + * 请使用 `getVariable`、`getVariableToRefs`、`setVariable` 方法进行操作 + * + * 为什么会有该变量存在,是因为在有时候你希望在 vue 声明之后就调用该变量,但是又不想使用 pinia + * 你可以使用该变量,但是请注意,该变量不会被 pinia 管理,所以你需要自己手动管理 + */ const variableState = reactive({ globalSpinning: false, // 全局加载控制器 globalDrawerValue: false, // 全局抽屉控制器(小尺寸设备可用) @@ -45,6 +55,18 @@ export type VariableState = typeof variableState export type VariableStateKey = keyof VariableState +/** + * + * @param key variable key + * @param value variable value + * @param cb 设置完成后的回调函数 + * + * 手动设置 variableState 的值 + * + * @example + * setVariable('globalSpinning', true) // 设置全局加载状态为 true + * setVariable('globalSpinning', true, () => {}) // 设置全局加载状态为 true,并且在设置完成后执行回调函数 + */ export function setVariable( key: T, value: VariableState[T], @@ -55,15 +77,15 @@ export function setVariable( cb?.() } -export function getVariable( - key: VariableStateKey, - defaultValue?: VariableState[T], -) { - const v = variableState[key] - - return v ? readonly(variableState)[key] : defaultValue -} - +/** + * + * @param key 目标值 key + * + * 返回目标值的响应式 ref + * + * @example + * getVariableToRefs('globalSpinning') // 返回 ref + */ export function getVariableToRefs(key: K) { return readonly(toRef(variableState, key)) } diff --git a/src/hooks/components/index.ts b/src/hooks/components/index.ts new file mode 100644 index 00000000..7fb265e0 --- /dev/null +++ b/src/hooks/components/index.ts @@ -0,0 +1 @@ +export * from './useContextmenuCoordinate' diff --git a/src/hooks/components/useContextmenuCoordinate.ts b/src/hooks/components/useContextmenuCoordinate.ts new file mode 100644 index 00000000..9b64f571 --- /dev/null +++ b/src/hooks/components/useContextmenuCoordinate.ts @@ -0,0 +1,86 @@ +/** + * + * @author Ray + * + * @date 2023-12-01 + * + * @workspace ray-template + * + * @remark 今天也是元气满满撸代码的一天 + */ + +import { useEventListener, onClickOutside } from '@vueuse/core' + +import type { BasicTarget } from '@/types/modules/vue' +import type { MaybeElementRef, MaybeElement } from '@vueuse/core' + +/** + * + * @param target 绑定元素 + * + * 右键点击元素时,获取鼠标坐标。该方法结合 NDropdown 组件使用,可以实现右键菜单功能 + * + * @example + * const target = ref(null) + * const { x, y, show, stop } = useContextmenuCoordinate(target) + * + * 如果需要手动停止右键菜单,可以调用 stop 方法 + * stop() + * + * + */ +export const useContextmenuCoordinate = (target: BasicTarget) => { + const x = ref(0) // 鼠标 x 坐标 + const y = ref(0) // 鼠标 y 坐标 + const show = ref(false) // 是否显示右键菜单 + + /** + * + * @param evt 鼠标事件 + * + * 鼠标右键点击事件,并且阻止默认事件 + * 设置坐标后激活右键菜单 + */ + const bindContextMenuEvent = (evt: Event) => { + evt.preventDefault() + + show.value = false + + nextTick().then(() => { + const { clientX, clientY } = evt as MouseEvent + + x.value = clientX + y.value = clientY + show.value = true + }) + } + + onClickOutside(target as MaybeElementRef, () => { + show.value = false + }) + + const cleanupContextmenu = useEventListener( + target, + 'contextmenu', + bindContextMenuEvent, + ) + const cleanupClick = useEventListener(target, 'click', () => { + show.value = false + }) + + const stop = () => { + cleanupContextmenu() + cleanupClick() + } + + return { + stop, + x: readonly(x), + y: readonly(y), + show, + } +} + +export type UseContextmenuCoordinateReturnType = ReturnType< + typeof useContextmenuCoordinate +> diff --git a/src/hooks/template/index.ts b/src/hooks/template/index.ts index eabdabe1..124a40ba 100644 --- a/src/hooks/template/index.ts +++ b/src/hooks/template/index.ts @@ -1,11 +1,7 @@ -import { useAppMenu } from './useAppMenu' -import { useMainPage } from './useMainPage' -import { useMenuTag } from './useMenuTag' -import { useRootRoute } from './useRootRoute' -import { useAppSetting } from './useAppSetting' - -export type { MaximizeOptions } from './useMainPage' -export type { Target } from './useAppMenu' -export type { CloseMenuTag } from './useMenuTag' - -export { useAppMenu, useMainPage, useMenuTag, useRootRoute, useAppSetting } +export * from './useMaximize' +export * from './useSpinning' +export * from './useWatermark' +export * from './useTheme' +export * from './useSiderBar' +export * from './useAppNavigation' +export * from './useAppRoot' diff --git a/src/hooks/template/useAppMenu.ts b/src/hooks/template/useAppNavigation.ts similarity index 85% rename from src/hooks/template/useAppMenu.ts rename to src/hooks/template/useAppNavigation.ts index 2754d064..413762cc 100644 --- a/src/hooks/template/useAppMenu.ts +++ b/src/hooks/template/useAppNavigation.ts @@ -20,7 +20,7 @@ export type Target = number | AppMenuOption * 导航函数 * 但是该方法仅限于在登入后调用 */ -export function useAppMenu() { +export function useAppNavigation() { const { changeMenuModelValue } = useMenuActions() /** @@ -32,6 +32,10 @@ export function useAppMenu() { * - 如果传递参数需要导航的菜单项为非根菜单项,会自动的递归导航至第一个子菜单项 * * 当传递参数为 AppMenuOption 类型,会直接导航至目标页面。该方法可以不区分菜单层级 + * + * @example + * navigationTo(1) // 导航至第二个菜单项,如果为根菜单项,会自动的递归导航至第一个子菜单项 + * navigationTo({ AppMenuOption }) // 导航至目标菜单项 */ const navigationTo = (target: Target) => { if (typeof target === 'number') { @@ -80,3 +84,5 @@ export function useAppMenu() { navigationTo, } } + +export type UseAppNavigationReturnType = ReturnType diff --git a/src/hooks/template/useRootRoute.ts b/src/hooks/template/useAppRoot.ts similarity index 64% rename from src/hooks/template/useRootRoute.ts rename to src/hooks/template/useAppRoot.ts index 82d44792..538e87e3 100644 --- a/src/hooks/template/useRootRoute.ts +++ b/src/hooks/template/useAppRoot.ts @@ -10,10 +10,11 @@ */ import { setVariable, getVariableToRefs } from '@/global-variable' +import { cloneDeep } from 'lodash-es' import type { DeepMutable } from '@/types/modules/helper' -export function useRootRoute() { +export function useAppRoot() { const globalRootRoute = getVariableToRefs('globalRootRoute') /** @@ -32,11 +33,20 @@ export function useRootRoute() { */ const getRootName = computed(() => globalRootRoute.value.name) + /** + * + * @param route 根路由配置内容 + * + * 设置根路由 + * + * @example + * setRootRoute({ path: '/your root path', name: 'your root name' }) + */ const setRootRoute = (route: DeepMutable) => { - setVariable( - 'globalRootRoute', - Object.assign({}, globalRootRoute.value, route), - ) + const routeRef = getVariableToRefs('globalRootRoute') + const assignRoute = Object.assign(cloneDeep(routeRef.value), route) + + setVariable('globalRootRoute', assignRoute) } return { @@ -46,3 +56,5 @@ export function useRootRoute() { setRootRoute, } } + +export type UseAppRootReturnType = ReturnType diff --git a/src/hooks/template/useAppSetting.ts b/src/hooks/template/useAppSetting.ts deleted file mode 100644 index bd280b5a..00000000 --- a/src/hooks/template/useAppSetting.ts +++ /dev/null @@ -1,34 +0,0 @@ -/** - * - * @author Ray - * - * @date 2023-11-16 - * - * @workspace ray-template - * - * @remark 今天也是元气满满撸代码的一天 - */ - -import { useSettingActions } from '@/store' - -export function useAppSetting() { - /** - * - * @param theme 当前主题色 - * - * 当前主题有明暗两套 - * - * @example - * changeTheme(true) 切换至暗色主题 - * changeTheme(false) 切换至明色主题 - */ - const changeTheme = (theme: boolean) => { - const { changeSwitcher } = useSettingActions() - - changeSwitcher(theme, 'appTheme') - } - - return { - changeTheme, - } -} diff --git a/src/hooks/template/useMainPage.ts b/src/hooks/template/useMaximize.ts similarity index 69% rename from src/hooks/template/useMainPage.ts rename to src/hooks/template/useMaximize.ts index 227362d6..ea3cbb58 100644 --- a/src/hooks/template/useMainPage.ts +++ b/src/hooks/template/useMaximize.ts @@ -2,7 +2,7 @@ * * @author Ray * - * @date 2023-11-03 + * @date 2023-11-30 * * @workspace ray-template * @@ -15,50 +15,11 @@ import { addStyle, removeStyle } from '@/utils/element' import { unrefElement } from '@/utils/vue' import { useWindowSize } from '@vueuse/core' -import type { Ref } from 'vue' - export interface MaximizeOptions { zIndex?: string } -export function useMainPage() { - /** - * - * @param wait 延迟时长 - * - * 刷新当前路由(强制触发刷新) - * - * @example - * reload(1200) - */ - const reload = (wait = 800) => { - setVariable('globalMainLayoutLoad', false) - - setTimeout(() => setVariable('globalMainLayoutLoad', true), wait) - } - - /** - * - * 打开加载动画(不触发强制刷新) - * - * @example - * openSpin() - */ - const openSpin = () => { - setVariable('layoutContentSpinning', true) - } - - /** - * - * 关闭加载动画(不触发强制刷新) - * - * @example - * closeSpin() - */ - const closeSpin = () => { - setVariable('layoutContentSpinning', false) - } - +export const useMaximize = () => { /** * * 当前 LayoutContent 是处于否全屏状态 @@ -68,8 +29,9 @@ export function useMainPage() { * @example * isLayoutContentMaximized() // true or false */ - const isLayoutContentMaximized = () => - computed(() => getVariableToRefs('layoutContentMaximize').value) + const isLayoutContentMaximized = computed( + () => getVariableToRefs('layoutContentMaximize').value, + ) /** * @@ -113,10 +75,9 @@ export function useMainPage() { } return { - reload, - maximize, isLayoutContentMaximized, - openSpin, - closeSpin, + maximize, } } + +export type UseMaximizeReturnType = ReturnType diff --git a/src/hooks/template/useMenuTag.ts b/src/hooks/template/useSiderBar.ts similarity index 98% rename from src/hooks/template/useMenuTag.ts rename to src/hooks/template/useSiderBar.ts index 9c32a6ec..37e2e3b1 100644 --- a/src/hooks/template/useMenuTag.ts +++ b/src/hooks/template/useSiderBar.ts @@ -80,7 +80,7 @@ const normalMenuTagOption = (target: CloseMenuTag, fc: string) => { } } -export function useMenuTag() { +export function useSiderBar() { const { getMenuTagOptions, getMenuKey } = useMenuGetters() const { changeMenuModelValue, @@ -287,3 +287,5 @@ export function useMenuTag() { checkCloseLeft, } } + +export type UseSiderBarReturnType = ReturnType diff --git a/src/hooks/template/useSpinning.ts b/src/hooks/template/useSpinning.ts new file mode 100644 index 00000000..42bec339 --- /dev/null +++ b/src/hooks/template/useSpinning.ts @@ -0,0 +1,59 @@ +/** + * + * @author Ray + * + * @date 2023-11-30 + * + * @workspace ray-template + * + * @remark 今天也是元气满满撸代码的一天 + */ + +import { setVariable } from '@/global-variable' + +export const useSpinning = () => { + /** + * + * @param wait 延迟时长 + * + * 刷新当前路由(强制触发刷新) + * + * @example + * reload(1200) + */ + const reload = (wait = 800) => { + setVariable('globalMainLayoutLoad', false) + + setTimeout(() => setVariable('globalMainLayoutLoad', true), wait) + } + + /** + * + * 打开加载动画(不触发强制刷新) + * + * @example + * openSpin() + */ + const openSpin = () => { + setVariable('layoutContentSpinning', true) + } + + /** + * + * 关闭加载动画(不触发强制刷新) + * + * @example + * closeSpin() + */ + const closeSpin = () => { + setVariable('layoutContentSpinning', false) + } + + return { + reload, + openSpin, + closeSpin, + } +} + +export type UseSpinningReturnType = ReturnType diff --git a/src/hooks/template/useTheme.ts b/src/hooks/template/useTheme.ts new file mode 100644 index 00000000..c9139ce5 --- /dev/null +++ b/src/hooks/template/useTheme.ts @@ -0,0 +1,90 @@ +/** + * + * @author Ray + * + * @date 2023-11-30 + * + * @workspace ray-template + * + * @remark 今天也是元气满满撸代码的一天 + */ + +import { useSettingActions, useSettingGetters } from '@/store' +import { useI18n } from '@/hooks/web' + +export const useTheme = () => { + /** + * + * 获取当前主题色与主题色描述 + * 并且描述会根据当前语言环境自动切换 + * + * @example + * getAppTheme() // { theme: true, themeLabel: '暗色' | 'Dark' } + * getAppTheme() // { theme: false, themeLabel: '亮色' | 'Light' } + */ + const getAppTheme = () => { + const { getAppTheme } = useSettingGetters() + const { t } = useI18n() + + return { + theme: getAppTheme.value, + themeLabel: getAppTheme.value + ? t('headerSettingOptions.ThemeOptions.Dark') + : t('headerSettingOptions.ThemeOptions.Light'), + } + } + + /** + * + * 切换至暗色主题 + * + * @example + * changeDarkTheme() + */ + const changeDarkTheme = () => { + const { updateSettingState } = useSettingActions() + + updateSettingState('appTheme', true) + } + + /** + * + * 切换至明色主题 + * + * @example + * changeLightTheme() + */ + const changeLightTheme = () => { + const { updateSettingState } = useSettingActions() + + updateSettingState('appTheme', false) + } + + /** + * + * @param theme 当前主题色 + * + * 当前主题有明暗两套 + * + * @example + * 假设当前主题为暗色主题 + * toggleTheme() // 切换至明色主题 + * 假设当前主题为明色主题 + * toggleTheme() // 切换至暗色主题 + */ + const toggleTheme = () => { + const { theme } = getAppTheme() + const { updateSettingState } = useSettingActions() + + updateSettingState('appTheme', !theme) + } + + return { + changeDarkTheme, + changeLightTheme, + toggleTheme, + getAppTheme, + } +} + +export type UseThemeReturnType = ReturnType diff --git a/src/hooks/template/useWatermark.ts b/src/hooks/template/useWatermark.ts new file mode 100644 index 00000000..a86f3510 --- /dev/null +++ b/src/hooks/template/useWatermark.ts @@ -0,0 +1,92 @@ +/** + * + * @author Ray + * + * @date 2023-11-30 + * + * @workspace ray-template + * + * @remark 今天也是元气满满撸代码的一天 + */ + +import { useSettingActions, useSettingGetters } from '@/store' +import { setVariable, getVariableToRefs } from '@/global-variable' +import { cloneDeep } from 'lodash-es' + +export const useWatermark = () => { + /** + * + * @param content 水印内容 + * + * 设置水印内容 + * 如果覆盖的水印内容为: '', undefined, null, 0, false, NaN,则会沿用上次一次的水印内容 + * + * @example + * setWatermarkContent('Ray Template Yes!') + * setWatermarkContent('') // 沿用上次一次的水印内容 + * setWatermarkContent(undefined) // 沿用上次一次的水印内容 + */ + const setWatermarkContent = (content: string) => { + const { getWatermarkConfig } = useSettingGetters() + const assignWatermark = Object.assign(getWatermarkConfig.value, { + content, + }) + const { updateSettingState } = useSettingActions() + + updateSettingState('watermarkConfig', assignWatermark) + } + + /** + * + * 显示水印 + * + * @example + * showWatermark() + */ + const showWatermark = () => { + const { updateSettingState } = useSettingActions() + + updateSettingState('watermarkSwitch', true) + } + + /** + * + * 隐藏水印 + * + * @example + * hiddenWatermark() + */ + const hiddenWatermark = () => { + const { updateSettingState } = useSettingActions() + + updateSettingState('watermarkSwitch', false) + } + + /** + * + * @param value 是否显示水印 + * + * 切换水印显示状态 + * + * @example + * 假设当前水印显示状态为隐藏 + * toggleWatermark() // 显示水印 + * 假设当前水印显示状态为显示 + * toggleWatermark() // 隐藏水印 + */ + const toggleWatermark = () => { + const { getWatermarkSwitch } = useSettingGetters() + const { updateSettingState } = useSettingActions() + + updateSettingState('watermarkSwitch', !getWatermarkSwitch.value) + } + + return { + setWatermarkContent, + showWatermark, + hiddenWatermark, + toggleWatermark, + } +} + +export type UseWatermarkReturnType = ReturnType diff --git a/src/hooks/web/index.ts b/src/hooks/web/index.ts index 7441df21..2b5c2140 100644 --- a/src/hooks/web/index.ts +++ b/src/hooks/web/index.ts @@ -9,11 +9,7 @@ * @remark 今天也是元气满满撸代码的一天 */ -import { useI18n, t } from './useI18n' -import { useVueRouter } from '../web/useVueRouter' -import { useDayjs } from '../web/useDayjs' -import { useDevice } from './useDevice' - -export type { FormatOption, DateRange, LocalKey } from './useDayjs' - -export { useI18n, useVueRouter, useDayjs, t, useDevice } +export * from './useI18n' +export * from './useVueRouter' +export * from './useDayjs' +export * from './useDevice' diff --git a/src/hooks/web/useDayjs.ts b/src/hooks/web/useDayjs.ts index 01c9470e..8e0ae61d 100644 --- a/src/hooks/web/useDayjs.ts +++ b/src/hooks/web/useDayjs.ts @@ -155,3 +155,5 @@ export const useDayjs = () => { isDateInRange, } } + +export type UseDayjsReturnType = ReturnType diff --git a/src/hooks/web/useDevice.ts b/src/hooks/web/useDevice.ts index 5198876f..68de54c7 100644 --- a/src/hooks/web/useDevice.ts +++ b/src/hooks/web/useDevice.ts @@ -33,3 +33,5 @@ export function useDevice() { isTabletOrSmaller, } } + +export type UseDeviceReturnType = ReturnType diff --git a/src/hooks/web/useI18n.ts b/src/hooks/web/useI18n.ts index c8f5c50f..31adab74 100644 --- a/src/hooks/web/useI18n.ts +++ b/src/hooks/web/useI18n.ts @@ -63,3 +63,5 @@ export const useI18n = (namespace?: string) => { * 该插件识别 t 方法包裹 path 进行提示文案内容 */ export const t = (key: string) => key + +export type UseI18nReturnType = ReturnType diff --git a/src/hooks/web/useVueRouter.ts b/src/hooks/web/useVueRouter.ts index c80fbbc5..be7b29d1 100644 --- a/src/hooks/web/useVueRouter.ts +++ b/src/hooks/web/useVueRouter.ts @@ -33,3 +33,5 @@ export const useVueRouter = () => { throw new Error('router is not defined') } } + +export type UseVueRouterReturnType = ReturnType diff --git a/src/layout/components/MenuTag/index.tsx b/src/layout/components/MenuTag/index.tsx index 7766fd65..80caf9a5 100644 --- a/src/layout/components/MenuTag/index.tsx +++ b/src/layout/components/MenuTag/index.tsx @@ -48,10 +48,10 @@ import { useMenuGetters, useMenuActions } from '@/store' import { uuid } from '@/utils/basic' import { hasClass } from '@/utils/element' import { queryElements } from '@use-utils/element' -import { useMainPage } from '@/hooks/template' -import { useMenuTag } from '@/hooks/template' +import { useMaximize, useSpinning } from '@/hooks/template' +import { useSiderBar } from '@/hooks/template' import { throttle } from 'lodash-es' -import { useRootRoute } from '@/hooks/template' +import { useAppRoot } from '@/hooks/template' import type { ScrollbarInst } from 'naive-ui' import type { MenuTagOptions, AppMenuOption } from '@/types/modules/app' @@ -63,15 +63,16 @@ export default defineComponent({ const { getMenuKey, getMenuTagOptions } = useMenuGetters() const { changeMenuModelValue } = useMenuActions() - const { getRootPath } = useRootRoute() - const { reload, maximize } = useMainPage() + const { getRootPath } = useAppRoot() + const { maximize } = useMaximize() + const { reload } = useSpinning() const { close, closeAll: $closeAll, closeRight: $closeRight, closeLeft: $closeLeft, closeOther: $closeOther, - } = useMenuTag() + } = useSiderBar() const canDisabledOptions = [ 'closeAll', diff --git a/src/layout/components/SiderBar/components/Breadcrumb/index.scss b/src/layout/components/SiderBar/components/Breadcrumb/index.scss new file mode 100644 index 00000000..b8b1041c --- /dev/null +++ b/src/layout/components/SiderBar/components/Breadcrumb/index.scss @@ -0,0 +1,20 @@ +.n-breadcrumb .n-breadcrumb-item { + &.breadcrumb-enter-active, + &.breadcrumb-leave-active { + transition: all 0.5s; + } + + & .breadcrumb-move { + transition: all 0.5s; + } + + &.breadcrumb-enter-from, + &.breadcrumb-leave-active { + opacity: 0; + transform: translateX(20px); + } + + &.breadcrumb-leave-active { + position: absolute; + } +} diff --git a/src/layout/components/SiderBar/components/Breadcrumb/index.tsx b/src/layout/components/SiderBar/components/Breadcrumb/index.tsx index 7ed01dfa..7f920bff 100644 --- a/src/layout/components/SiderBar/components/Breadcrumb/index.tsx +++ b/src/layout/components/SiderBar/components/Breadcrumb/index.tsx @@ -18,7 +18,10 @@ * 添加 标签, 避免 Runtime directive used on component... 警告 */ +import './index.scss' + import { NDropdown, NBreadcrumb, NBreadcrumbItem } from 'naive-ui' +import { TransitionGroup } from 'vue' import { useMenuGetters, useMenuActions } from '@/store' import { useDevice } from '@/hooks/web' @@ -61,36 +64,41 @@ export default defineComponent({ } }, render() { - const { isTabletOrSmaller } = this + const { isTabletOrSmaller, getBreadcrumbOptions } = this + const { dropdownSelect, breadcrumbItemClick } = this return isTabletOrSmaller ? ( -
+
) : ( - {this.getBreadcrumbOptions.map((curr) => ( - - 1 ? curr.children : [] - } - onSelect={this.dropdownSelect.bind(this)} + + {getBreadcrumbOptions.map((curr) => ( + - {{ - default: () => ( - - {curr.label && typeof curr.label === 'function' - ? curr.label() - : curr.breadcrumbLabel} - - ), - }} - - - ))} + 1 + ? curr.children + : [] + } + onSelect={dropdownSelect.bind(this)} + > + {{ + default: () => ( + + {curr.label && typeof curr.label === 'function' + ? curr.label() + : curr.breadcrumbLabel} + + ), + }} + + + ))} + ) }, diff --git a/src/layout/components/SiderBar/components/GlobalSearch/index.tsx b/src/layout/components/SiderBar/components/GlobalSearch/index.tsx index c3767dbc..30c6ba75 100644 --- a/src/layout/components/SiderBar/components/GlobalSearch/index.tsx +++ b/src/layout/components/SiderBar/components/GlobalSearch/index.tsx @@ -23,11 +23,12 @@ import './index.scss' import { NInput, NModal, NResult, NScrollbar, NSpace } from 'naive-ui' import { RIcon } from '@/components' -import { on, off, queryElements, addClass, removeClass } from '@/utils/element' +import { queryElements, addClass, removeClass } from '@/utils/element' import { debounce } from 'lodash-es' import { useMenuGetters, useMenuActions } from '@/store' import { validMenuItemShow } from '@/router/helper/routerCopilot' import { useDevice } from '@/hooks/web' +import { useEventListener } from '@vueuse/core' import type { AppRouteMeta } from '@/router/type' import type { AppMenuOption } from '@/types/modules/app' @@ -275,17 +276,9 @@ export default defineComponent({ } }) - onMounted(() => { - on(window, 'keydown', (e: Event) => { - registerArouseKeyboard(e as KeyboardEvent) - registerChangeSearchElementIndex(e as KeyboardEvent) - }) - }) - onBeforeUnmount(() => { - off(window, 'keydown', (e: Event) => { - registerArouseKeyboard(e as KeyboardEvent) - registerChangeSearchElementIndex(e as KeyboardEvent) - }) + useEventListener(window, 'keydown', (e: KeyboardEvent) => { + registerArouseKeyboard(e) + registerChangeSearchElementIndex(e) }) return { diff --git a/src/layout/components/SiderBar/components/SettingDrawer/components/ThemeSwitch/index.tsx b/src/layout/components/SiderBar/components/SettingDrawer/components/ThemeSwitch/index.tsx index 30f1cd78..8cc20e87 100644 --- a/src/layout/components/SiderBar/components/SettingDrawer/components/ThemeSwitch/index.tsx +++ b/src/layout/components/SiderBar/components/SettingDrawer/components/ThemeSwitch/index.tsx @@ -12,16 +12,17 @@ import { NSpace, NSwitch, NTooltip } from 'naive-ui' import { RIcon } from '@/components' -import { useSettingGetters, useSettingActions } from '@/store' +import { useSettingGetters } from '@/store' +import { useTheme } from '@/hooks/template' export default defineComponent({ name: 'ThemeSwitch', setup() { - const { changeSwitcher } = useSettingActions() + const { changeDarkTheme, changeLightTheme } = useTheme() const { getAppTheme } = useSettingGetters() const modelAppThemeRef = ref(getAppTheme.value) - const handleRailStyle = ({ checked }: { checked: boolean }) => { + const railStyle = ({ checked }: { checked: boolean }) => { return checked ? { backgroundColor: '#000000', @@ -32,14 +33,15 @@ export default defineComponent({ } return { - changeSwitcher, + changeDarkTheme, + changeLightTheme, getAppTheme, - handleRailStyle, + railStyle, modelAppThemeRef, } }, render() { - const { $t } = this + const { $t, changeDarkTheme, changeLightTheme, railStyle } = this return ( @@ -48,28 +50,14 @@ export default defineComponent({ trigger: () => ( - this.changeSwitcher(bool, 'appTheme') + bool ? changeDarkTheme() : changeLightTheme() } > {{ - 'checked-icon': () => - h( - RIcon, - { - name: 'dark', - }, - {}, - ), - 'unchecked-icon': () => - h( - RIcon, - { - name: 'light', - }, - {}, - ), + 'checked-icon': () => , + 'unchecked-icon': () => , checked: () => '亮', unchecked: () => '暗', }} diff --git a/src/layout/components/SiderBar/components/SettingDrawer/index.tsx b/src/layout/components/SiderBar/components/SettingDrawer/index.tsx index 9ae78187..11a786d0 100644 --- a/src/layout/components/SiderBar/components/SettingDrawer/index.tsx +++ b/src/layout/components/SiderBar/components/SettingDrawer/index.tsx @@ -47,8 +47,7 @@ const SettingDrawer = defineComponent({ }, emits: ['update:show'], setup(props, { emit }) { - const { changePrimaryColor, changeSwitcher, updateContentTransition } = - useSettingActions() + const { changePrimaryColor, updateSettingState } = useSettingActions() const { getAppTheme, getPrimaryColorOverride, @@ -97,9 +96,8 @@ const SettingDrawer = defineComponent({ changePrimaryColor, getAppTheme, getPrimaryColorOverride, - changeSwitcher, contentTransitionOptions, - updateContentTransition, + updateSettingState, modelSwitchReactive, } }, @@ -133,7 +131,7 @@ const SettingDrawer = defineComponent({ v-model:value={this.modelSwitchReactive.getContentTransition} options={this.contentTransitionOptions} onUpdateValue={(value) => { - this.updateContentTransition(value) + this.updateSettingState('contentTransition', value) }} /> @@ -144,7 +142,7 @@ const SettingDrawer = defineComponent({ - this.changeSwitcher(bool, 'menuTagSwitch') + this.updateSettingState('menuTagSwitch', bool) } /> @@ -152,7 +150,7 @@ const SettingDrawer = defineComponent({ - this.changeSwitcher(bool, 'breadcrumbSwitch') + this.updateSettingState('breadcrumbSwitch', bool) } /> @@ -160,7 +158,7 @@ const SettingDrawer = defineComponent({ - this.changeSwitcher(bool, 'watermarkSwitch') + this.updateSettingState('watermarkSwitch', bool) } /> @@ -168,7 +166,7 @@ const SettingDrawer = defineComponent({ - this.changeSwitcher(bool, 'copyrightSwitch') + this.updateSettingState('copyrightSwitch', bool) } /> diff --git a/src/layout/components/SiderBar/index.tsx b/src/layout/components/SiderBar/index.tsx index f5b09fe0..9df8723e 100644 --- a/src/layout/components/SiderBar/index.tsx +++ b/src/layout/components/SiderBar/index.tsx @@ -38,7 +38,7 @@ import { useDevice } from '@/hooks/web' import { getVariableToRefs, setVariable } from '@/global-variable' import { useFullscreen } from 'vue-hooks-plus' import { useI18n } from '@/hooks/web' -import { useMainPage } from '@/hooks/template' +import { useSpinning } from '@/hooks/template' import { useSettingGetters, useSettingActions } from '@/store' import type { IconEventMapOptions, IconEventMap } from './type' @@ -46,9 +46,9 @@ import type { IconEventMapOptions, IconEventMap } from './type' export default defineComponent({ name: 'AppSiderBar', setup() { - const { updateLocale, changeSwitcher } = useSettingActions() + const { updateLocale, updateSettingState } = useSettingActions() const { t } = useI18n() - const { reload } = useMainPage() + const { reload } = useSpinning() const [isFullscreen, { toggleFullscreen, isEnabled }] = useFullscreen( document.getElementsByTagName('html')[0], @@ -107,7 +107,7 @@ export default defineComponent({ globalSearchShown.value = true }, lock: () => { - changeSwitcher(true, 'lockScreenSwitch') + updateSettingState('lockScreenSwitch', true) }, menu: () => { setVariable('globalDrawerValue', !globalDrawerValue.value) diff --git a/src/layout/components/SiderBar/shared.ts b/src/layout/components/SiderBar/shared.ts index 18044baa..ba9138fa 100644 --- a/src/layout/components/SiderBar/shared.ts +++ b/src/layout/components/SiderBar/shared.ts @@ -54,9 +54,9 @@ const avatarDropdownActionMap = { * 锁定屏幕 */ lockScreen: () => { - const { changeSwitcher } = useSettingActions() + const { updateSettingState } = useSettingActions() - changeSwitcher(true, 'lockScreenSwitch') + updateSettingState('lockScreenSwitch', true) }, } diff --git a/src/layout/default/ContentWrapper/index.tsx b/src/layout/default/ContentWrapper/index.tsx index db16151b..f63c2317 100644 --- a/src/layout/default/ContentWrapper/index.tsx +++ b/src/layout/default/ContentWrapper/index.tsx @@ -23,7 +23,7 @@ import AppRequestCancelerProvider from '@/app-components/provider/AppRequestCanc import { getVariableToRefs } from '@/global-variable' import { useSettingGetters } from '@/store' -import { useMainPage } from '@/hooks/template' +import { useMaximize } from '@/hooks/template' import type { GlobalThemeOverrides } from 'naive-ui' @@ -32,7 +32,7 @@ export default defineComponent({ setup() { const router = useRouter() - const { maximize } = useMainPage() + const { maximize } = useMaximize() const { getContentTransition } = useSettingGetters() const spinning = ref(false) const themeOverridesSpin: GlobalThemeOverrides['Spin'] = { diff --git a/src/locales/lang/en-US/menu.json b/src/locales/lang/en-US/menu.json index 1a6fbf89..f51d657f 100644 --- a/src/locales/lang/en-US/menu.json +++ b/src/locales/lang/en-US/menu.json @@ -22,5 +22,6 @@ "QRCode": "QRCode", "SvgIcon": "SVG Icon", "TemplateHooks": "Template Api", - "Modal": "Modal" + "Modal": "Modal", + "ContextMenu": "Right Click Menu" } diff --git a/src/locales/lang/zh-CN/menu.json b/src/locales/lang/zh-CN/menu.json index d51108bb..e759374b 100644 --- a/src/locales/lang/zh-CN/menu.json +++ b/src/locales/lang/zh-CN/menu.json @@ -22,5 +22,6 @@ "QRCode": "二维码", "SvgIcon": "SVG 图标", "TemplateHooks": "模板内置 Api", - "Modal": "模态框" + "Modal": "模态框", + "ContextMenu": "右键菜单" } diff --git a/src/router/helper/permission.ts b/src/router/helper/permission.ts index 79878f74..f56bf05c 100644 --- a/src/router/helper/permission.ts +++ b/src/router/helper/permission.ts @@ -26,7 +26,7 @@ import { redirectRouterToDashboard } from '@/router/helper/routerCopilot' import { WHITE_ROUTES } from '@/app-config/routerConfig' import { validRole } from '@/router/helper/routerCopilot' import { isValueType } from '@/utils/basic' -import { useRootRoute } from '@/hooks/template' +import { useAppRoot } from '@/hooks/template' import type { Router, RouteLocationNormalized } from 'vue-router' import type { AppRouteMeta } from '@/router/type' @@ -34,7 +34,7 @@ import type { AppRouteMeta } from '@/router/type' /** 路由守卫 */ export const permissionRouter = (router: Router) => { const { beforeEach } = router - const { getRootPath } = useRootRoute() + const { getRootPath } = useAppRoot() const isToLogin = ( to: RouteLocationNormalized, diff --git a/src/router/helper/routerCopilot.ts b/src/router/helper/routerCopilot.ts index 9b06cf3e..54fb836b 100644 --- a/src/router/helper/routerCopilot.ts +++ b/src/router/helper/routerCopilot.ts @@ -15,7 +15,7 @@ import { useVueRouter } from '@/hooks/web' import { setStorage } from '@/utils/cache' import { getAppEnvironment } from '@/utils/basic' import { useSigningGetters } from '@/store' -import { useRootRoute } from '@/hooks/template' +import { useAppRoot } from '@/hooks/template' import type { Router } from 'vue-router' import type { AppRouteMeta } from '@/router/type' @@ -132,7 +132,7 @@ export const redirectRouterToDashboard = (isReplace = true) => { const { router } = useVueRouter() const { push, replace } = router - const { getRootPath } = useRootRoute() + const { getRootPath } = useAppRoot() setStorage('menuKey', getRootPath.value) diff --git a/src/router/modules/demo/context-menu.ts b/src/router/modules/demo/context-menu.ts new file mode 100644 index 00000000..b687c791 --- /dev/null +++ b/src/router/modules/demo/context-menu.ts @@ -0,0 +1,16 @@ +import { t } from '@/hooks/web' + +import type { AppRouteRecordRaw } from '@/router/type' + +const contextMenu: AppRouteRecordRaw = { + path: '/context-menu', + name: 'ContextMenuDemo', + component: () => import('@/views/demo/context-menu/index'), + meta: { + i18nKey: t('menu.ContextMenu'), + icon: 'other', + order: 2, + }, +} + +export default contextMenu diff --git a/src/router/routes.ts b/src/router/routes.ts index 7f548b14..406fec65 100644 --- a/src/router/routes.ts +++ b/src/router/routes.ts @@ -1,9 +1,9 @@ import Layout from '@/layout' import { appExpandRoutes } from './appRouteModules' -import { useRootRoute } from '@/hooks/template' +import { useAppRoot } from '@/hooks/template' export default async () => { - const { getRootPath } = useRootRoute() + const { getRootPath } = useAppRoot() return [ /** diff --git a/src/store/hooks/useMenuStore.ts b/src/store/hooks/useMenuStore.ts index 9bb6c163..d782431c 100644 --- a/src/store/hooks/useMenuStore.ts +++ b/src/store/hooks/useMenuStore.ts @@ -10,7 +10,7 @@ */ import { piniaMenuStore } from '../modules/menu' -import { useRootRoute } from '@/hooks/template' +import { useAppRoot } from '@/hooks/template' export const useMenuGetters = () => { const variable = piniaMenuStore() @@ -35,7 +35,7 @@ export const useMenuGetters = () => { * @remark 获取菜单标签列表 */ const getMenuTagOptions = computed(() => { - const { getRootPath } = useRootRoute() + const { getRootPath } = useAppRoot() return variable.menuTagOptions.map((curr, _idx, currentArray) => { if (curr.key === getMenuKey.value && curr.key !== getRootPath.value) { diff --git a/src/store/hooks/useSettingStore.ts b/src/store/hooks/useSettingStore.ts index edfccc84..cfe2c8f9 100644 --- a/src/store/hooks/useSettingStore.ts +++ b/src/store/hooks/useSettingStore.ts @@ -65,6 +65,12 @@ export const useSettingGetters = () => { */ const getWatermarkSwitch = computed(() => variable.watermarkSwitch) + /** + * + * @remark 获取水印配置 + */ + const getWatermarkConfig = computed(() => variable.watermarkConfig) + return { getDrawerPlacement, getPrimaryColorOverride, @@ -76,21 +82,17 @@ export const useSettingGetters = () => { getCopyrightSwitch, getContentTransition, getWatermarkSwitch, + getWatermarkConfig, } } export const useSettingActions = () => { - const { - updateLocale, - changePrimaryColor, - changeSwitcher, - updateContentTransition, - } = piniaSettingStore() + const { updateLocale, changePrimaryColor, updateSettingState } = + piniaSettingStore() return { updateLocale, changePrimaryColor, - changeSwitcher, - updateContentTransition, + updateSettingState, } } diff --git a/src/store/modules/menu/helper.ts b/src/store/modules/menu/helper.ts index f0f7e61f..ebff5166 100644 --- a/src/store/modules/menu/helper.ts +++ b/src/store/modules/menu/helper.ts @@ -15,7 +15,7 @@ import { APP_MENU_CONFIG } from '@/app-config/appConfig' import { RIcon } from '@/components' import { isValueType } from '@/utils/basic' import { getStorage } from '@/utils/cache' -import { useRootRoute } from '@/hooks/template' +import { useAppRoot } from '@/hooks/template' import type { AppMenuOption, @@ -174,7 +174,7 @@ export const hasMenuIcon = (option: AppMenuOption) => { /** 获取缓存的 menu key, 如果未获取到则使用 getRootPath 当作默认激活路由菜单 */ export const getCatchMenuKey = () => { - const { getRootPath } = useRootRoute() + const { getRootPath } = useAppRoot() const cacheMenuKey = getStorage( 'menuKey', 'sessionStorage', diff --git a/src/store/modules/setting/index.ts b/src/store/modules/setting/index.ts index 45e92d79..6c4304ce 100644 --- a/src/store/modules/setting/index.ts +++ b/src/store/modules/setting/index.ts @@ -5,10 +5,12 @@ import { colorToRgba } from '@/utils/element' import { useI18n } from '@/hooks/web' import { APP_THEME } from '@/app-config/designConfig' import { useDayjs } from '@/hooks/web' +import { APP_WATERMARK_CONFIG } from '@/app-config/appConfig' +import { cloneDeep } from 'lodash-es' -import type { ConditionalPick } from '@/types/modules/helper' import type { SettingState } from '@/store/modules/setting/type' import type { LocalKey } from '@/hooks/web' +import type { AnyFC } from '@/types/modules/utils' export const piniaSettingStore = defineStore( 'setting', @@ -36,13 +38,9 @@ export const piniaSettingStore = defineStore( copyrightSwitch: true, // 底部区域开关 contentTransition: 'scale', // 切换过渡效果 watermarkSwitch: false, // 水印开关, + watermarkConfig: cloneDeep(APP_WATERMARK_CONFIG), }) - /** 更新过渡效果 */ - const updateContentTransition = (value: string) => { - settingState.contentTransition = value - } - /** 修改当前语言 */ const updateLocale = (key: string) => { locale(key) @@ -73,29 +71,38 @@ export const piniaSettingStore = defineStore( /** * - * @param bool 开关当前值 - * @param key `settingState` 对应开关属性值 + * @param key settingState 的 key + * @param value settingState 的 value + * @param cb 回调函数 * - * @remark 仅适用于值为 `boolean` 的切换 + * 更新 settingState 的值,如果 key 不存在与 settingState 中,则不会更新 + * 但是不论是否更新成功,都会执行回调函数 + * + * @example + * updateSettingState('drawerPlacement', 'left') + * updateSettingState('appTheme', true) */ - const changeSwitcher = ( - bool: boolean, - key: keyof ConditionalPick, + const updateSettingState = < + T extends keyof SettingState, + V extends typeof settingState, + C extends AnyFC, + >( + key: T, + value: V[T], + cb?: C, ) => { - if ( - Object.hasOwn(settingState, key) && - typeof settingState[key] === 'boolean' - ) { - settingState[key] = bool + if (Object.hasOwn(settingState, key)) { + settingState[key] = value } + + cb?.() } return { ...toRefs(settingState), updateLocale, changePrimaryColor, - changeSwitcher, - updateContentTransition, + updateSettingState, } }, { diff --git a/src/store/modules/setting/type.ts b/src/store/modules/setting/type.ts index 9b3585db..f1279540 100644 --- a/src/store/modules/setting/type.ts +++ b/src/store/modules/setting/type.ts @@ -1,5 +1,6 @@ import type { GlobalThemeOverrides } from 'naive-ui' import type { Placement } from '@/types/modules/component' +import type { APP_WATERMARK_CONFIG } from '@/app-config/appConfig' export interface SettingState { drawerPlacement: Placement @@ -12,4 +13,5 @@ export interface SettingState { watermarkSwitch: boolean copyrightSwitch: boolean contentTransition: string + watermarkConfig: typeof APP_WATERMARK_CONFIG } diff --git a/src/types/modules/helper.ts b/src/types/modules/helper.ts index 8bad7bf6..74ad5452 100644 --- a/src/types/modules/helper.ts +++ b/src/types/modules/helper.ts @@ -1,23 +1,66 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ + +/** + * + * 从目标类型中挑选出符合条件的属性名 + * + * @example + * ConditionalKeys<{ a: string, b: number }, string> // 'a' + */ export type ConditionalKeys = NonNullable< { [Key in keyof Base]: Base[Key] extends Condition ? Key : never }[keyof Base] > +/** + * + * 从目标类型中挑选出符合条件的属性 + * + * @example + * ConditionalPick<{ a: string, b: number }, string> // { a: string } + */ export type ConditionalPick = Pick< Base, ConditionalKeys > +/** + * + * 声明一个任意类型的对象 + * + * @example + * const A: Recordable = { a: 1, b: [] } + */ export type Recordable = Record +/** + * + * 获取目标类型的所有属性名 + * + * @example + * Keys<{ a: string, b: number }> // 'a' | 'b' + */ export type ValueOf = T[keyof T] +/** + * + * 将目标所有属性变为可修改 + * + * @example + * Mutable<{ readonly a: string }> // { a: string } + */ export type Mutable = { -readonly [P in keyof T]: T[P] } +/** + * + * 递归将目标所有属性变为可修改 + * + * @example + * DeepMutable<{ readonly a: { readonly b: { readonly c: string } } }> // { a: { b: { c: string } } } + */ export type DeepMutable = { -readonly [P in keyof T]: T[P] extends ReadonlyArray ? Array> diff --git a/src/types/modules/utils.ts b/src/types/modules/utils.ts index c5ca8504..28af968e 100644 --- a/src/types/modules/utils.ts +++ b/src/types/modules/utils.ts @@ -1,6 +1,5 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import type CryptoJS from 'crypto-js' -import type { BasicTarget } from './vue' export type StorageLike = 'sessionStorage' | 'localStorage' @@ -10,10 +9,6 @@ export type RemoveStorageKey = | 'all-sessionStorage' | 'all-localStorage' -export type EventListenerOrEventListenerObject = - | EventListener - | EventListenerObject - export type ValidateValueType = | 'BigUint64Array' | 'BigInt64Array' @@ -93,7 +88,3 @@ export type ElementSelector = string | `attr:${string}` export type MaybeArray = T | T[] export type DownloadAnyFileDataType = Blob | File | string | ArrayBuffer - -export type EventListenerTarget = BasicTarget< - HTMLElement | Element | Window | Document -> diff --git a/src/utils/basic.ts b/src/utils/basic.ts index b09cad1e..1ea97b7e 100644 --- a/src/utils/basic.ts +++ b/src/utils/basic.ts @@ -232,3 +232,27 @@ export function print>( watchEffectWithTarget(watcher) } + +/** + * + * @param targetObject 对象 + * @param targetKeys 待删除的 key + * + * 删除对象中的指定 key + * + * @example + * omit({ a: 1, b: 2, c: 3 }, 'a') => { b: 2, c: 3 } + * omit({ a: 1, b: 2, c: 3 }, ['a', 'b']) => { c: 3 } + */ +export const omit = , K extends keyof T>( + targetObject: T, + targetKeys: K | K[], +): Omit => { + const keys = Array.isArray(targetKeys) ? targetKeys : [targetKeys] + + keys.forEach((key) => { + delete targetObject[key] + }) + + return targetObject +} diff --git a/src/utils/element.ts b/src/utils/element.ts index 681efcfd..ae8a1ff2 100644 --- a/src/utils/element.ts +++ b/src/utils/element.ts @@ -2,83 +2,14 @@ import { isValueType } from '@/utils/basic' import { APP_REGEX } from '@/app-config/regexConfig' import { unrefElement } from '@/utils/vue' import { watchEffectWithTarget } from '@/utils/vue' +import { useCurrentElement } from '@vueuse/core' import type { - EventListenerOrEventListenerObject, PartialCSSStyleDeclaration, ElementSelector, } from '@/types/modules/utils' -import type { EventListenerTarget } from '@/types/modules/utils' import type { BasicTarget, TargetValue } from '@/types/modules/vue' -/** - * - * @param target Target element dom - * @param event 绑定事件类型 - * @param handler 事件触发方法 - * @param useCapture 是否冒泡 - * - * @remark 给元素绑定某个事件柄方法 - */ -export const on = ( - target: EventListenerTarget, - event: string, - handler: EventListenerOrEventListenerObject, - useCapture: boolean | AddEventListenerOptions = false, -) => { - const targetElement = computed(() => unrefElement(target, window)) - - const update = < - T extends TargetValue, - >( - element: T, - ) => { - if (element && event && handler) { - element.addEventListener(event, handler, useCapture) - } - } - - const watcher = watch(targetElement, (ndata) => update(ndata), { - immediate: true, - }) - - watchEffectWithTarget(watcher) -} - -/** - * - * @param target Target element dom - * @param event 卸载事件类型 - * @param handler 所需卸载方法 - * @param useCapture 是否冒泡 - * - * @remark 卸载元素上某个事件柄方法 - */ -export const off = ( - target: EventListenerTarget, - event: string, - handler: EventListenerOrEventListenerObject, - useCapture: boolean | AddEventListenerOptions = false, -) => { - const targetElement = computed(() => unrefElement(target, window)) - - const update = < - T extends TargetValue, - >( - element: T, - ) => { - if (element && event && handler) { - element.removeEventListener(event, handler, useCapture) - } - } - - const watcher = watch(targetElement, (ndata) => update(ndata), { - immediate: true, - }) - - watchEffectWithTarget(watcher) -} - /** * * @param target Target element dom diff --git a/src/views/dashboard/index.tsx b/src/views/dashboard/index.tsx index 449fa6ce..7bb95331 100644 --- a/src/views/dashboard/index.tsx +++ b/src/views/dashboard/index.tsx @@ -90,15 +90,7 @@ const Dashboard = defineComponent({ {{ - header: () => - h( - RIcon, - { - name: 'ray', - size: '64', - }, - {}, - ), + header: () => , default: () => '当你看见这个页面后, 就说明项目已经启动成功了~', }} diff --git a/src/views/demo/context-menu/index.tsx b/src/views/demo/context-menu/index.tsx new file mode 100644 index 00000000..5331811f --- /dev/null +++ b/src/views/demo/context-menu/index.tsx @@ -0,0 +1,78 @@ +/** + * + * @author Ray + * + * @date 2023-12-01 + * + * @workspace ray-template + * + * @remark 今天也是元气满满撸代码的一天 + */ + +import { NSpace, NCard, NDropdown } from 'naive-ui' + +import { useContextmenuCoordinate } from '@/hooks/components' + +export default defineComponent({ + name: 'ContextMenuDemo', + setup() { + const demoOneRef = ref(null) + const demoOneShow = ref(false) + const options = ref([ + { + label: '杰·盖茨比', + key: 'jay gatsby', + }, + { + label: '黛西·布坎南', + key: 'daisy buchanan', + }, + { + type: 'divider', + key: 'd1', + }, + { + label: '尼克·卡拉威', + key: 'nick carraway', + }, + ]) + + const { x, y, show } = useContextmenuCoordinate(demoOneRef) + + return { + demoOneRef, + demoOneShow, + x, + y, + show, + options, + } + }, + render() { + const { x, y, show } = this + + return ( + + + +

默认点击元素外部会关闭菜单。

+
+ 右击 +
+
+
+ +
+ ) + }, +}) diff --git a/src/views/demo/table/index.tsx b/src/views/demo/table/index.tsx index fd9971f0..45bf4dd8 100644 --- a/src/views/demo/table/index.tsx +++ b/src/views/demo/table/index.tsx @@ -55,18 +55,10 @@ const TableView = defineComponent({ key: 'tags', render: (row: RowData) => { const tags = row.tags.map((tagKey) => { - return h( - NTag, - { - style: { - marginRight: '6px', - }, - type: 'info', - bordered: false, - }, - { - default: () => tagKey, - }, + return ( + + {tagKey} + ) }) @@ -138,7 +130,7 @@ const TableView = defineComponent({ key: 'edit', }, { - label: () => h('span', { style: { color: 'red' } }, '删除'), + label: () => 删除, key: 'delete', }, ] diff --git a/src/views/demo/template-hooks/index.tsx b/src/views/demo/template-hooks/index.tsx index 0b9b06d1..384d75ea 100644 --- a/src/views/demo/template-hooks/index.tsx +++ b/src/views/demo/template-hooks/index.tsx @@ -9,19 +9,36 @@ * @remark 今天也是元气满满撸代码的一天 */ -import { NSpace, NCard, NButton } from 'naive-ui' +import { NSpace, NCard, NButton, NInput } from 'naive-ui' -import { useAppMenu, useMainPage } from '@/hooks/template' +import { + useAppNavigation, + useMaximize, + useSpinning, + useWatermark, + useTheme, +} from '@/hooks/template' import { getVariableToRefs } from '@/global-variable' +import { useSettingGetters } from '@/store' export default defineComponent({ name: 'TemplateHooks', setup() { const currentMenuOption = ref('') const maximizeRef = getVariableToRefs('layoutContentMaximize') + const watermark = ref(useSettingGetters().getWatermarkConfig.value.content) - const { navigationTo } = useAppMenu() - const { reload, maximize, openSpin, closeSpin } = useMainPage() + const { navigationTo } = useAppNavigation() + const { maximize, isLayoutContentMaximized } = useMaximize() + const { reload, openSpin, closeSpin } = useSpinning() + const { + showWatermark, + hiddenWatermark, + setWatermarkContent, + toggleWatermark, + } = useWatermark() + const { changeDarkTheme, changeLightTheme, toggleTheme, getAppTheme } = + useTheme() return { navigationTo, @@ -31,10 +48,35 @@ export default defineComponent({ maximizeRef, openSpin, closeSpin, + showWatermark, + hiddenWatermark, + setWatermarkContent, + watermark, + toggleWatermark, + changeDarkTheme, + changeLightTheme, + toggleTheme, + getAppTheme, + isLayoutContentMaximized, } }, render() { - const { navigationTo, reload, maximize, openSpin, closeSpin } = this + const { + navigationTo, + reload, + maximize, + openSpin, + closeSpin, + showWatermark, + hiddenWatermark, + setWatermarkContent, + toggleWatermark, + changeDarkTheme, + changeLightTheme, + toggleTheme, + getAppTheme, + isLayoutContentMaximized, + } = this return ( @@ -44,21 +86,38 @@ export default defineComponent({ 方法。这里不做过多的赘述,可以查看文档具体描述。 - + + +

getAppTheme 获取当前主题色: {getAppTheme().themeLabel}

+ + changeDarkTheme()}>切换暗黑主题 + changeLightTheme()}>切换明亮主题 + toggleTheme()}>切换主题 + +
+
+ + + { + setWatermarkContent(val) + }} + /> + + showWatermark()}>显示水印 + hiddenWatermark()}>隐藏水印 + toggleWatermark()}>切换水印 + + + +

- navigationTo - 参数为正整数时,会更具当前的菜单顺序进行自动导航匹配。但是此方法仅能导航一级菜单。并且如果导航菜单非根菜单项,会自动递归导航至一子菜单。 + 手动刷新内容区域,会使得当前路由页面内容强制重新加载(会执行完整的 + vue 生命周期)。默认 800ms 延迟。


- navigationTo(15)}>跳转至多级菜单 -
- - -

- 手动刷新内容区域,会使得当前路由页面内容强制重新加载(会执行完整的 - vue 生命周期)。默认 800ms 延迟。 -

-
+ { reload() @@ -77,16 +136,28 @@ export default defineComponent({ > 触发加载动画(不强制刷新) -
- - { - maximize(!this.maximizeRef) - }} - > - 最大化内容区域 - - +
+ + +

+ isLayoutContentMaximized 检测当前内容区域是否最大化: + {isLayoutContentMaximized ? '最大化' : '正常尺寸'} +

+ { + maximize(!this.maximizeRef) + }} + > + 最大化内容区域 + +
+ +

+ navigationTo + 参数为正整数时,会更具当前的菜单顺序进行自动导航匹配。但是此方法仅能导航一级菜单。并且如果导航菜单非根菜单项,会自动递归导航至一子菜单。 +

+
+ navigationTo(16)}>跳转至多级菜单
) diff --git a/src/views/login/components/Signing/index.tsx b/src/views/login/components/Signing/index.tsx index 0563bf50..0b1f914a 100644 --- a/src/views/login/components/Signing/index.tsx +++ b/src/views/login/components/Signing/index.tsx @@ -5,7 +5,7 @@ import { useI18n } from '@/hooks/web' import { APP_CATCH_KEY } from '@/app-config/appConfig' import { setVariable, getVariableToRefs } from '@/global-variable' import { useSigningActions } from '@/store' -import { useRootRoute } from '@/hooks/template' +import { useAppRoot } from '@/hooks/template' import type { FormInst } from 'naive-ui' @@ -16,7 +16,7 @@ export default defineComponent({ const { t } = useI18n() const { signing } = useSigningActions() - const { getRootPath } = useRootRoute() + const { getRootPath } = useAppRoot() const globalSpinning = getVariableToRefs('globalSpinning') const useSigningForm = () => ({ diff --git a/vite.pliugin.config.ts b/vite.pliugin.config.ts index e39abdab..31fd16d0 100644 --- a/vite.pliugin.config.ts +++ b/vite.pliugin.config.ts @@ -33,8 +33,8 @@ import config from './cfg' import type { PluginOption } from 'vite' -// 仅适用于构建模式(任何构建模式:preview、build、report...) -function onlyBuildOptions(mode: string) { +// 仅适用于报告模式 +function onlyReportOptions(mode: string) { return [ visualizer({ gzipSize: true, // 搜集 `gzip` 压缩包 @@ -43,6 +43,12 @@ function onlyBuildOptions(mode: string) { filename: 'visualizer.html', open: mode === 'report' ? true : false, // 以默认服务器代理打开文件 }), + ] +} + +// 仅适用于构建模式(任何构建模式:preview、build、report...) +function onlyBuildOptions(mode: string) { + return [ viteCDNPlugin({ // modules 顺序 vue, vue-demi 必须保持当前顺序加载,否则会出现加载错误问题 modules: [ @@ -180,6 +186,7 @@ function baseOptions(mode: string) { export default function (mode: string): PluginOption[] { const plugins = mode === 'development' ? onlyDevOptions(mode) : onlyBuildOptions(mode) + const reportPlugins = mode === 'report' ? onlyReportOptions(mode) : [] - return [...baseOptions(mode), ...plugins] + return [...baseOptions(mode), ...plugins, ...reportPlugins] }