version: v4.4.2

This commit is contained in:
XiaoDaiGua-Ray 2023-12-01 23:18:55 +08:00
parent 9e3cbac091
commit f272832a8c
65 changed files with 961 additions and 429 deletions

View File

@ -33,6 +33,7 @@ module.exports = {
defineExpose: 'readonly', defineExpose: 'readonly',
withDefaults: 'readonly', withDefaults: 'readonly',
defineOptions: 'readonly', defineOptions: 'readonly',
defineModel: 'readonly',
}, },
rules: { rules: {
'no-undefined': ['error'], 'no-undefined': ['error'],

View File

@ -1,5 +1,36 @@
# CHANGE LOG # 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 ## 4.4.1
更新 `vite` 版本至 `5.0.4`。同步修复了一些小问题。 更新 `vite` 版本至 `5.0.4`。同步修复了一些小问题。
@ -77,7 +108,7 @@
紧跟尤大大脚步,更新 `vite` 版本至 `5.0.0` 版本!与此同时,更新了配套所有插件! 紧跟尤大大脚步,更新 `vite` 版本至 `5.0.0` 版本!与此同时,更新了配套所有插件!
更新 ROOT_ROUTE 的一些使用方法,该配置方法与原有的方式不变,但是有一个新的功能点则是,该配置项会传递给 global-variable 的 globalRootRoute 属性。并且更改模板原有获取 path 的方法,改为响应式获取。当你要进行动态的维护 Root Route 的时候,该方法可能可以帮助到你 `useRootRoute`。 更新 ROOT_ROUTE 的一些使用方法,该配置方法与原有的方式不变,但是有一个新的功能点则是,该配置项会传递给 global-variable 的 globalRootRoute 属性。并且更改模板原有获取 path 的方法,改为响应式获取。当你要进行动态的维护 Root Route 的时候,该方法可能可以帮助到你 `useAppRoot`。
如果你在更新版本后出现一些奇奇怪怪的问题,不要犹豫,直接删除 `node_modules` 后再重新安装依赖,这是缓存导致的问题。 如果你在更新版本后出现一些奇奇怪怪的问题,不要犹豫,直接删除 `node_modules` 后再重新安装依赖,这是缓存导致的问题。

View File

@ -9,14 +9,14 @@
简体中文 | [English](https://github.com/XiaoDaiGua-Ray/ray-template/blob/main/README.md) 简体中文 | [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 等最新技术的中后台模板。
</div> </div>
## ✨ 特性 ## ✨ 特性
- **靠爱发电**:几乎包含市面常见的模板特性并且全部免费使用 - **靠爱发电**:几乎包含市面常见的模板特性并且全部免费使用
- **最新技术栈**:使用 vue3.x/vite4.x/pinia 等前端前沿技术开发 - **最新技术栈**:使用 vue3.x/vite5.x/pinia 等前端前沿技术开发
- **TypeScript**:应用程序级 JavaScript 的语言 - **TypeScript**:应用程序级 JavaScript 的语言
- **主题**:可配置的主题 - **主题**:可配置的主题
- **国际化**:内置完善的国际化方案 - **国际化**:内置完善的国际化方案

View File

@ -9,14 +9,14 @@
English | [简体中文](https://github.com/XiaoDaiGua-Ray/ray-template/blob/main/README-ZH.md) 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.
</div> </div>
## ✨ Feature ## ✨ Feature
- **Power by love**: Contains almost all common template features on the market and all are free to use. - **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. - **TypeScript**The language for application-level JavaScript.
- **App Theme**Configurable themes. - **App Theme**Configurable themes.
- **Globalization**Built-in complete internationalization solution. - **Globalization**Built-in complete internationalization solution.

View File

@ -1,7 +1,7 @@
{ {
"name": "ray-template", "name": "ray-template",
"private": false, "private": false,
"version": "4.4.1", "version": "4.4.2",
"type": "module", "type": "module",
"engines": { "engines": {
"node": "^18.0.0 || >=20.0.0", "node": "^18.0.0 || >=20.0.0",

View File

@ -27,7 +27,7 @@ const LockScreen = defineComponent({
const inputInstRef = ref<InputInst | null>(null) const inputInstRef = ref<InputInst | null>(null)
const { setLockAppScreen } = useAppLockScreen() const { setLockAppScreen } = useAppLockScreen()
const { changeSwitcher } = useSettingActions() const { updateSettingState } = useSettingActions()
const state = reactive({ const state = reactive({
lockCondition: useCondition(), lockCondition: useCondition(),
@ -38,7 +38,7 @@ const LockScreen = defineComponent({
formInstRef.value?.validate((error) => { formInstRef.value?.validate((error) => {
if (!error) { if (!error) {
setLockAppScreen(true) setLockAppScreen(true)
changeSwitcher(true, 'lockScreenSwitch') updateSettingState('lockScreenSwitch', true)
state.lockCondition = useCondition() state.lockCondition = useCondition()
} }

View File

@ -29,7 +29,7 @@ export default defineComponent({
const inputInstRef = ref<InputInst | null>(null) const inputInstRef = ref<InputInst | null>(null)
const { logout } = useSigningActions() const { logout } = useSigningActions()
const { changeSwitcher } = useSettingActions() const { updateSettingState } = useSettingActions()
const { setLockAppScreen } = useAppLockScreen() const { setLockAppScreen } = useAppLockScreen()
const { isTabletOrSmaller } = useDevice() const { isTabletOrSmaller } = useDevice()
@ -64,7 +64,7 @@ export default defineComponent({
onPositiveClick: () => { onPositiveClick: () => {
logout() logout()
setTimeout(() => { setTimeout(() => {
changeSwitcher(false, 'lockScreenSwitch') updateSettingState('lockScreenSwitch', false)
}) })
}, },
}) })
@ -75,7 +75,7 @@ export default defineComponent({
formRef.value?.validate((error) => { formRef.value?.validate((error) => {
if (!error) { if (!error) {
setLockAppScreen(false) setLockAppScreen(false)
changeSwitcher(false, 'lockScreenSwitch') updateSettingState('lockScreenSwitch', false)
state.lockCondition = useCondition() state.lockCondition = useCondition()
} }

View File

@ -28,12 +28,12 @@ const AppLockScreen = defineComponent({
name: 'AppLockScreen', name: 'AppLockScreen',
setup() { setup() {
const { getLockAppScreen } = useAppLockScreen() const { getLockAppScreen } = useAppLockScreen()
const { changeSwitcher } = useSettingActions() const { updateSettingState } = useSettingActions()
const { getLockScreenSwitch } = useSettingGetters() const { getLockScreenSwitch } = useSettingGetters()
const lockScreenSwitchRef = computed({ const lockScreenSwitchRef = computed({
get: () => getLockScreenSwitch.value, get: () => getLockScreenSwitch.value,
set: (val) => { set: (val) => {
changeSwitcher(val, 'lockScreenSwitch') updateSettingState('lockScreenSwitch', val)
}, },
}) })

View File

@ -15,25 +15,29 @@
* *
* *
* Layout * Layout
*
* useWatermark hook
*/ */
import { NWatermark } from 'naive-ui' import { NWatermark } from 'naive-ui'
import { APP_WATERMARK_CONFIG } from '@/app-config/appConfig'
import { useSettingGetters } from '@/store' import { useSettingGetters } from '@/store'
export default defineComponent({ export default defineComponent({
name: 'AppWatermarkProvider', name: 'AppWatermarkProvider',
setup() { setup() {
const { getWatermarkSwitch } = useSettingGetters() const { getWatermarkSwitch, getWatermarkConfig } = useSettingGetters()
return { return {
getWatermarkSwitch, getWatermarkSwitch,
getWatermarkConfig,
} }
}, },
render() { render() {
return this.getWatermarkSwitch ? ( const { getWatermarkConfig, getWatermarkSwitch } = this
<NWatermark cross fullscreen {...APP_WATERMARK_CONFIG} />
return getWatermarkSwitch ? (
<NWatermark cross fullscreen {...getWatermarkConfig} />
) : null ) : null
}, },
}) })

View File

@ -52,7 +52,7 @@ export const PRE_LOADING_CONFIG: PreloadingConfig = {
* globalRootRoute * globalRootRoute
* *
* *
* Root Route 使 useRootRoute * Root Route 使 useAppRoot
*/ */
export const ROOT_ROUTE: RootRoute = { export const ROOT_ROUTE: RootRoute = {
name: 'Dashboard', name: 'Dashboard',

View File

@ -13,9 +13,10 @@ import './index.scss'
import { NSpin } from 'naive-ui' 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 { call } from '@/utils/vue'
import props from './props' import props from './props'
import { useEventListener } from '@vueuse/core'
export default defineComponent({ export default defineComponent({
name: 'RIframe', name: 'RIframe',
@ -53,19 +54,13 @@ export default defineComponent({
} }
} }
useEventListener(iframeRef, 'load', iframeLoadSuccess)
useEventListener(iframeRef, 'error', iframeLoadError)
expose({ expose({
iframeInst: iframeRef, 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 { return {
cssVars, cssVars,
iframeRef, iframeRef,

View File

@ -9,6 +9,8 @@ import type { ModalProps } from 'naive-ui'
* *
* *
* card, dialog * card, dialog
*
* 30ms
*/ */
export const setupDraggable = ( export const setupDraggable = (
bindModal: HTMLElement, bindModal: HTMLElement,

View File

@ -35,13 +35,23 @@ export default defineComponent({
const rTableInst = ref<DataTableInst | null>(null) const rTableInst = ref<DataTableInst | null>(null)
const wrapperRef = ref<HTMLElement | null>(null) const wrapperRef = ref<HTMLElement | null>(null)
const uuidWrapper = uuid(16) const uuidWrapper = uuid(16) // wrapper id
const uuidTable = uuid(16) const uuidTable = uuid(16) // table id
/**
*
* x: 横坐标
* y: 纵坐标
* showContextMenu: 是否显示右键菜单
*/
const contextMenuReactive = reactive({ const contextMenuReactive = reactive({
x: 0, x: 0,
y: 0, y: 0,
showContextMenu: false, showContextMenu: false,
}) })
/**
*
* size: table size
*/
const privateReactive = reactive({ const privateReactive = reactive({
size: props.size, size: props.size,
}) })
@ -90,10 +100,22 @@ export default defineComponent({
} }
} }
/**
*
* @param size table size
*
* table size
*/
const changeTableSize = (size: ComponentSize) => { const changeTableSize = (size: ComponentSize) => {
privateReactive.size = size privateReactive.size = size
} }
/**
*
* @param options table columns
*
* table columns onUpdateColumns onUpdate:columns
*/
const updateTableColumn = (options: CType[]) => { const updateTableColumn = (options: CType[]) => {
const { onUpdateColumns, 'onUpdate:columns': $onUpdateColumns } = props const { onUpdateColumns, 'onUpdate:columns': $onUpdateColumns } = props
@ -105,6 +127,11 @@ export default defineComponent({
} }
} }
/**
*
* toolOptions
* toolOptions
*/
const renderToolOptions = () => { const renderToolOptions = () => {
const { toolOptions } = props const { toolOptions } = props
@ -113,6 +140,12 @@ export default defineComponent({
.map((curr) => (typeof curr === 'function' ? curr() : curr)) .map((curr) => (typeof curr === 'function' ? curr() : curr))
} }
/**
*
* @param p props
*
* toolOptions toolOptions
*/
const tool = (p: typeof props) => { const tool = (p: typeof props) => {
const renderDefaultToolOptions = () => ( const renderDefaultToolOptions = () => (
<> <>
@ -163,7 +196,6 @@ export default defineComponent({
} }
}, },
render() { render() {
/* eslint-disable @typescript-eslint/no-explicit-any */
const { tool } = this const { tool } = this
return ( return (
@ -208,6 +240,7 @@ export default defineComponent({
}), }),
'header-extra': () => ( 'header-extra': () => (
<NSpace wrapItem={false} align="center"> <NSpace wrapItem={false} align="center">
{/* eslint-disable @typescript-eslint/no-explicit-any */}
{tool(this.$props as any)} {tool(this.$props as any)}
</NSpace> </NSpace>
), ),

View File

@ -32,11 +32,9 @@ import type { MaybeArray } from '@/types/modules/utils'
type FixedClick = (type: 'left' | 'right', option: C, index: number) => void type FixedClick = (type: 'left' | 'right', option: C, index: number) => void
const renderSwitcherIcon = () => const renderSwitcherIcon = () => (
h(RIcon, { <RIcon name="draggable" size={config.tableIconSize} />
name: 'draggable', )
size: config.tableIconSize,
})
const RowIconRender = ({ const RowIconRender = ({
icon, icon,

View File

@ -1,3 +1,4 @@
// 导出所有自定义组件
export * from './RChart' export * from './RChart'
export * from './RCollapseGrid' export * from './RCollapseGrid'
export * from './RIcon' export * from './RIcon'
@ -7,3 +8,11 @@ export * from './RMoreDropdown'
export * from './RQRCode' export * from './RQRCode'
export * from './RTable' export * from './RTable'
export * from './RTransitionComponent' 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'

View File

@ -15,7 +15,7 @@
*/ */
import { debounce } from 'lodash-es' import { debounce } from 'lodash-es'
import { on, off } from '@use-utils/element' import { useEventListener } from '@vueuse/core'
import type { DebounceBindingOptions } from './type' import type { DebounceBindingOptions } from './type'
import type { AnyFC } from '@/types/modules/utils' import type { AnyFC } from '@/types/modules/utils'
@ -27,6 +27,7 @@ const debounceDirective: CustomDirectiveFC<
DebounceBindingOptions DebounceBindingOptions
> = () => { > = () => {
let debounceFunction: DebouncedFunc<AnyFC> | null let debounceFunction: DebouncedFunc<AnyFC> | null
let cleanup: () => void
return { return {
beforeMount: (el, { value }) => { beforeMount: (el, { value }) => {
@ -38,14 +39,14 @@ const debounceDirective: CustomDirectiveFC<
debounceFunction = debounce(func, wait, Object.assign({}, options)) debounceFunction = debounce(func, wait, Object.assign({}, options))
on(el, trigger, debounceFunction) cleanup = useEventListener(el, trigger, debounceFunction)
}, },
beforeUnmount: (el, { value }) => { beforeUnmount: (el, { value }) => {
const { trigger = 'click' } = value const { trigger = 'click' } = value
if (debounceFunction) { if (debounceFunction) {
debounceFunction.cancel() debounceFunction.cancel()
off(el, trigger, debounceFunction) cleanup?.()
} }
debounceFunction = null debounceFunction = null

View File

@ -15,7 +15,7 @@
*/ */
import { throttle } from 'lodash-es' import { throttle } from 'lodash-es'
import { on, off } from '@use-utils/element' import { useEventListener } from '@vueuse/core'
import type { ThrottleBindingOptions } from './type' import type { ThrottleBindingOptions } from './type'
import type { AnyFC } from '@/types/modules/utils' import type { AnyFC } from '@/types/modules/utils'
@ -27,6 +27,7 @@ const throttleDirective: CustomDirectiveFC<
ThrottleBindingOptions ThrottleBindingOptions
> = () => { > = () => {
let throttleFunction: DebouncedFunc<AnyFC> | null let throttleFunction: DebouncedFunc<AnyFC> | null
let cleanup: () => void
return { return {
beforeMount: (el, { value }) => { beforeMount: (el, { value }) => {
@ -38,14 +39,12 @@ const throttleDirective: CustomDirectiveFC<
throttleFunction = throttle(func, wait, Object.assign({}, options)) throttleFunction = throttle(func, wait, Object.assign({}, options))
on(el, trigger, throttleFunction) useEventListener(el, trigger, throttleFunction)
}, },
beforeUnmount: (el, { value }) => { beforeUnmount: () => {
const { trigger = 'click' } = value
if (throttleFunction) { if (throttleFunction) {
throttleFunction.cancel() throttleFunction.cancel()
off(el, trigger, throttleFunction) cleanup?.()
} }
throttleFunction = null throttleFunction = null

View File

@ -9,10 +9,6 @@
* @remark * @remark
*/ */
import { setVariable, getVariable, getVariableToRefs } from './variable' export * from './variable'
import type { VariableState, VariableStateKey } from './variable' export type * from './variable'
export { setVariable, getVariable, getVariableToRefs }
export type { VariableState, VariableStateKey }

View File

@ -11,27 +11,37 @@
/** /**
* *
* pinia 使 * pinia 使
* *
* *
* 使 * 使
*
* @example * @example
*
*
* getVariable('target key', 'default value')
*
* *
* getVariableToRefs('target key') * getVariableToRefs('target key')
* *
* state * state
* setVariable('key', 'value') * 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 { cloneDeep } from 'lodash-es'
import type { AnyFC } from '@/types/modules/utils' 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({ const variableState = reactive({
globalSpinning: false, // 全局加载控制器 globalSpinning: false, // 全局加载控制器
globalDrawerValue: false, // 全局抽屉控制器(小尺寸设备可用) globalDrawerValue: false, // 全局抽屉控制器(小尺寸设备可用)
@ -45,6 +55,18 @@ export type VariableState = typeof variableState
export type VariableStateKey = keyof 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<T extends VariableStateKey, FC extends AnyFC>( export function setVariable<T extends VariableStateKey, FC extends AnyFC>(
key: T, key: T,
value: VariableState[T], value: VariableState[T],
@ -55,15 +77,15 @@ export function setVariable<T extends VariableStateKey, FC extends AnyFC>(
cb?.() cb?.()
} }
export function getVariable<T extends VariableStateKey>( /**
key: VariableStateKey, *
defaultValue?: VariableState[T], * @param key key
) { *
const v = variableState[key] * ref
*
return v ? readonly(variableState)[key] : defaultValue * @example
} * getVariableToRefs('globalSpinning') // 返回 ref<boolean>
*/
export function getVariableToRefs<K extends VariableStateKey>(key: K) { export function getVariableToRefs<K extends VariableStateKey>(key: K) {
return readonly(toRef<VariableState, K>(variableState, key)) return readonly(toRef<VariableState, K>(variableState, key))
} }

View File

@ -0,0 +1 @@
export * from './useContextmenuCoordinate'

View File

@ -0,0 +1,86 @@
/**
*
* @author Ray <https://github.com/XiaoDaiGua-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<HTMLElement | null>(null)
* const { x, y, show, stop } = useContextmenuCoordinate(target)
*
* stop
* stop()
*
* <NDropdown show={show} x={x} y={y} trigger="manual" placement="bottom-start" />
*/
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<MaybeElement>, () => {
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
>

View File

@ -1,11 +1,7 @@
import { useAppMenu } from './useAppMenu' export * from './useMaximize'
import { useMainPage } from './useMainPage' export * from './useSpinning'
import { useMenuTag } from './useMenuTag' export * from './useWatermark'
import { useRootRoute } from './useRootRoute' export * from './useTheme'
import { useAppSetting } from './useAppSetting' export * from './useSiderBar'
export * from './useAppNavigation'
export type { MaximizeOptions } from './useMainPage' export * from './useAppRoot'
export type { Target } from './useAppMenu'
export type { CloseMenuTag } from './useMenuTag'
export { useAppMenu, useMainPage, useMenuTag, useRootRoute, useAppSetting }

View File

@ -20,7 +20,7 @@ export type Target = number | AppMenuOption
* *
* *
*/ */
export function useAppMenu() { export function useAppNavigation() {
const { changeMenuModelValue } = useMenuActions() const { changeMenuModelValue } = useMenuActions()
/** /**
@ -32,6 +32,10 @@ export function useAppMenu() {
* - * -
* *
* AppMenuOption * AppMenuOption
*
* @example
* navigationTo(1) // 导航至第二个菜单项,如果为根菜单项,会自动的递归导航至第一个子菜单项
* navigationTo({ AppMenuOption }) // 导航至目标菜单项
*/ */
const navigationTo = (target: Target) => { const navigationTo = (target: Target) => {
if (typeof target === 'number') { if (typeof target === 'number') {
@ -80,3 +84,5 @@ export function useAppMenu() {
navigationTo, navigationTo,
} }
} }
export type UseAppNavigationReturnType = ReturnType<typeof useAppNavigation>

View File

@ -10,10 +10,11 @@
*/ */
import { setVariable, getVariableToRefs } from '@/global-variable' import { setVariable, getVariableToRefs } from '@/global-variable'
import { cloneDeep } from 'lodash-es'
import type { DeepMutable } from '@/types/modules/helper' import type { DeepMutable } from '@/types/modules/helper'
export function useRootRoute() { export function useAppRoot() {
const globalRootRoute = getVariableToRefs('globalRootRoute') const globalRootRoute = getVariableToRefs('globalRootRoute')
/** /**
@ -32,11 +33,20 @@ export function useRootRoute() {
*/ */
const getRootName = computed(() => globalRootRoute.value.name) const getRootName = computed(() => globalRootRoute.value.name)
/**
*
* @param route
*
*
*
* @example
* setRootRoute({ path: '/your root path', name: 'your root name' })
*/
const setRootRoute = (route: DeepMutable<typeof globalRootRoute.value>) => { const setRootRoute = (route: DeepMutable<typeof globalRootRoute.value>) => {
setVariable( const routeRef = getVariableToRefs('globalRootRoute')
'globalRootRoute', const assignRoute = Object.assign(cloneDeep(routeRef.value), route)
Object.assign({}, globalRootRoute.value, route),
) setVariable('globalRootRoute', assignRoute)
} }
return { return {
@ -46,3 +56,5 @@ export function useRootRoute() {
setRootRoute, setRootRoute,
} }
} }
export type UseAppRootReturnType = ReturnType<typeof useAppRoot>

View File

@ -1,34 +0,0 @@
/**
*
* @author Ray <https://github.com/XiaoDaiGua-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,
}
}

View File

@ -2,7 +2,7 @@
* *
* @author Ray <https://github.com/XiaoDaiGua-Ray> * @author Ray <https://github.com/XiaoDaiGua-Ray>
* *
* @date 2023-11-03 * @date 2023-11-30
* *
* @workspace ray-template * @workspace ray-template
* *
@ -15,50 +15,11 @@ import { addStyle, removeStyle } from '@/utils/element'
import { unrefElement } from '@/utils/vue' import { unrefElement } from '@/utils/vue'
import { useWindowSize } from '@vueuse/core' import { useWindowSize } from '@vueuse/core'
import type { Ref } from 'vue'
export interface MaximizeOptions { export interface MaximizeOptions {
zIndex?: string zIndex?: string
} }
export function useMainPage() { export const useMaximize = () => {
/**
*
* @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)
}
/** /**
* *
* LayoutContent * LayoutContent
@ -68,8 +29,9 @@ export function useMainPage() {
* @example * @example
* isLayoutContentMaximized() // true or false * isLayoutContentMaximized() // true or false
*/ */
const isLayoutContentMaximized = () => const isLayoutContentMaximized = computed(
computed(() => getVariableToRefs('layoutContentMaximize').value) () => getVariableToRefs('layoutContentMaximize').value,
)
/** /**
* *
@ -113,10 +75,9 @@ export function useMainPage() {
} }
return { return {
reload,
maximize,
isLayoutContentMaximized, isLayoutContentMaximized,
openSpin, maximize,
closeSpin,
} }
} }
export type UseMaximizeReturnType = ReturnType<typeof useMaximize>

View File

@ -80,7 +80,7 @@ const normalMenuTagOption = (target: CloseMenuTag, fc: string) => {
} }
} }
export function useMenuTag() { export function useSiderBar() {
const { getMenuTagOptions, getMenuKey } = useMenuGetters() const { getMenuTagOptions, getMenuKey } = useMenuGetters()
const { const {
changeMenuModelValue, changeMenuModelValue,
@ -287,3 +287,5 @@ export function useMenuTag() {
checkCloseLeft, checkCloseLeft,
} }
} }
export type UseSiderBarReturnType = ReturnType<typeof useSiderBar>

View File

@ -0,0 +1,59 @@
/**
*
* @author Ray <https://github.com/XiaoDaiGua-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<typeof useSpinning>

View File

@ -0,0 +1,90 @@
/**
*
* @author Ray <https://github.com/XiaoDaiGua-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<typeof useTheme>

View File

@ -0,0 +1,92 @@
/**
*
* @author Ray <https://github.com/XiaoDaiGua-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<typeof useWatermark>

View File

@ -9,11 +9,7 @@
* @remark * @remark
*/ */
import { useI18n, t } from './useI18n' export * from './useI18n'
import { useVueRouter } from '../web/useVueRouter' export * from './useVueRouter'
import { useDayjs } from '../web/useDayjs' export * from './useDayjs'
import { useDevice } from './useDevice' export * from './useDevice'
export type { FormatOption, DateRange, LocalKey } from './useDayjs'
export { useI18n, useVueRouter, useDayjs, t, useDevice }

View File

@ -155,3 +155,5 @@ export const useDayjs = () => {
isDateInRange, isDateInRange,
} }
} }
export type UseDayjsReturnType = ReturnType<typeof useDayjs>

View File

@ -33,3 +33,5 @@ export function useDevice() {
isTabletOrSmaller, isTabletOrSmaller,
} }
} }
export type UseDeviceReturnType = ReturnType<typeof useDevice>

View File

@ -63,3 +63,5 @@ export const useI18n = (namespace?: string) => {
* t path * t path
*/ */
export const t = (key: string) => key export const t = (key: string) => key
export type UseI18nReturnType = ReturnType<typeof useI18n>

View File

@ -33,3 +33,5 @@ export const useVueRouter = () => {
throw new Error('router is not defined') throw new Error('router is not defined')
} }
} }
export type UseVueRouterReturnType = ReturnType<typeof useVueRouter>

View File

@ -48,10 +48,10 @@ import { useMenuGetters, useMenuActions } from '@/store'
import { uuid } from '@/utils/basic' import { uuid } from '@/utils/basic'
import { hasClass } from '@/utils/element' import { hasClass } from '@/utils/element'
import { queryElements } from '@use-utils/element' import { queryElements } from '@use-utils/element'
import { useMainPage } from '@/hooks/template' import { useMaximize, useSpinning } from '@/hooks/template'
import { useMenuTag } from '@/hooks/template' import { useSiderBar } from '@/hooks/template'
import { throttle } from 'lodash-es' import { throttle } from 'lodash-es'
import { useRootRoute } from '@/hooks/template' import { useAppRoot } from '@/hooks/template'
import type { ScrollbarInst } from 'naive-ui' import type { ScrollbarInst } from 'naive-ui'
import type { MenuTagOptions, AppMenuOption } from '@/types/modules/app' import type { MenuTagOptions, AppMenuOption } from '@/types/modules/app'
@ -63,15 +63,16 @@ export default defineComponent({
const { getMenuKey, getMenuTagOptions } = useMenuGetters() const { getMenuKey, getMenuTagOptions } = useMenuGetters()
const { changeMenuModelValue } = useMenuActions() const { changeMenuModelValue } = useMenuActions()
const { getRootPath } = useRootRoute() const { getRootPath } = useAppRoot()
const { reload, maximize } = useMainPage() const { maximize } = useMaximize()
const { reload } = useSpinning()
const { const {
close, close,
closeAll: $closeAll, closeAll: $closeAll,
closeRight: $closeRight, closeRight: $closeRight,
closeLeft: $closeLeft, closeLeft: $closeLeft,
closeOther: $closeOther, closeOther: $closeOther,
} = useMenuTag() } = useSiderBar()
const canDisabledOptions = [ const canDisabledOptions = [
'closeAll', 'closeAll',

View File

@ -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;
}
}

View File

@ -18,7 +18,10 @@
* <span> , Runtime directive used on component... * <span> , Runtime directive used on component...
*/ */
import './index.scss'
import { NDropdown, NBreadcrumb, NBreadcrumbItem } from 'naive-ui' import { NDropdown, NBreadcrumb, NBreadcrumbItem } from 'naive-ui'
import { TransitionGroup } from 'vue'
import { useMenuGetters, useMenuActions } from '@/store' import { useMenuGetters, useMenuActions } from '@/store'
import { useDevice } from '@/hooks/web' import { useDevice } from '@/hooks/web'
@ -61,36 +64,41 @@ export default defineComponent({
} }
}, },
render() { render() {
const { isTabletOrSmaller } = this const { isTabletOrSmaller, getBreadcrumbOptions } = this
const { dropdownSelect, breadcrumbItemClick } = this
return isTabletOrSmaller ? ( return isTabletOrSmaller ? (
<div></div> <div style="display: none;"></div>
) : ( ) : (
<NBreadcrumb> <NBreadcrumb>
{this.getBreadcrumbOptions.map((curr) => ( <TransitionGroup tag="li" name="breadcrumb" appear>
<NBreadcrumbItem {getBreadcrumbOptions.map((curr) => (
key={curr.key} <NBreadcrumbItem
onClick={this.breadcrumbItemClick.bind(this, curr)} key={curr.path}
> onClick={breadcrumbItemClick.bind(this, curr)}
<NDropdown
labelField="breadcrumbLabel"
options={
curr.children && curr.children?.length > 1 ? curr.children : []
}
onSelect={this.dropdownSelect.bind(this)}
> >
{{ <NDropdown
default: () => ( labelField="breadcrumbLabel"
<span> options={
{curr.label && typeof curr.label === 'function' curr.children && curr.children?.length > 1
? curr.label() ? curr.children
: curr.breadcrumbLabel} : []
</span> }
), onSelect={dropdownSelect.bind(this)}
}} >
</NDropdown> {{
</NBreadcrumbItem> default: () => (
))} <span>
{curr.label && typeof curr.label === 'function'
? curr.label()
: curr.breadcrumbLabel}
</span>
),
}}
</NDropdown>
</NBreadcrumbItem>
))}
</TransitionGroup>
</NBreadcrumb> </NBreadcrumb>
) )
}, },

View File

@ -23,11 +23,12 @@ import './index.scss'
import { NInput, NModal, NResult, NScrollbar, NSpace } from 'naive-ui' import { NInput, NModal, NResult, NScrollbar, NSpace } from 'naive-ui'
import { RIcon } from '@/components' 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 { debounce } from 'lodash-es'
import { useMenuGetters, useMenuActions } from '@/store' import { useMenuGetters, useMenuActions } from '@/store'
import { validMenuItemShow } from '@/router/helper/routerCopilot' import { validMenuItemShow } from '@/router/helper/routerCopilot'
import { useDevice } from '@/hooks/web' import { useDevice } from '@/hooks/web'
import { useEventListener } from '@vueuse/core'
import type { AppRouteMeta } from '@/router/type' import type { AppRouteMeta } from '@/router/type'
import type { AppMenuOption } from '@/types/modules/app' import type { AppMenuOption } from '@/types/modules/app'
@ -275,17 +276,9 @@ export default defineComponent({
} }
}) })
onMounted(() => { useEventListener(window, 'keydown', (e: KeyboardEvent) => {
on(window, 'keydown', (e: Event) => { registerArouseKeyboard(e)
registerArouseKeyboard(e as KeyboardEvent) registerChangeSearchElementIndex(e)
registerChangeSearchElementIndex(e as KeyboardEvent)
})
})
onBeforeUnmount(() => {
off(window, 'keydown', (e: Event) => {
registerArouseKeyboard(e as KeyboardEvent)
registerChangeSearchElementIndex(e as KeyboardEvent)
})
}) })
return { return {

View File

@ -12,16 +12,17 @@
import { NSpace, NSwitch, NTooltip } from 'naive-ui' import { NSpace, NSwitch, NTooltip } from 'naive-ui'
import { RIcon } from '@/components' import { RIcon } from '@/components'
import { useSettingGetters, useSettingActions } from '@/store' import { useSettingGetters } from '@/store'
import { useTheme } from '@/hooks/template'
export default defineComponent({ export default defineComponent({
name: 'ThemeSwitch', name: 'ThemeSwitch',
setup() { setup() {
const { changeSwitcher } = useSettingActions() const { changeDarkTheme, changeLightTheme } = useTheme()
const { getAppTheme } = useSettingGetters() const { getAppTheme } = useSettingGetters()
const modelAppThemeRef = ref(getAppTheme.value) const modelAppThemeRef = ref(getAppTheme.value)
const handleRailStyle = ({ checked }: { checked: boolean }) => { const railStyle = ({ checked }: { checked: boolean }) => {
return checked return checked
? { ? {
backgroundColor: '#000000', backgroundColor: '#000000',
@ -32,14 +33,15 @@ export default defineComponent({
} }
return { return {
changeSwitcher, changeDarkTheme,
changeLightTheme,
getAppTheme, getAppTheme,
handleRailStyle, railStyle,
modelAppThemeRef, modelAppThemeRef,
} }
}, },
render() { render() {
const { $t } = this const { $t, changeDarkTheme, changeLightTheme, railStyle } = this
return ( return (
<NSpace justify="center"> <NSpace justify="center">
@ -48,28 +50,14 @@ export default defineComponent({
trigger: () => ( trigger: () => (
<NSwitch <NSwitch
v-model:value={this.modelAppThemeRef} v-model:value={this.modelAppThemeRef}
railStyle={this.handleRailStyle.bind(this)} railStyle={railStyle.bind(this)}
onUpdateValue={(bool: boolean) => onUpdateValue={(bool: boolean) =>
this.changeSwitcher(bool, 'appTheme') bool ? changeDarkTheme() : changeLightTheme()
} }
> >
{{ {{
'checked-icon': () => 'checked-icon': () => <RIcon name="dark" />,
h( 'unchecked-icon': () => <RIcon name="light" />,
RIcon,
{
name: 'dark',
},
{},
),
'unchecked-icon': () =>
h(
RIcon,
{
name: 'light',
},
{},
),
checked: () => '亮', checked: () => '亮',
unchecked: () => '暗', unchecked: () => '暗',
}} }}

View File

@ -47,8 +47,7 @@ const SettingDrawer = defineComponent({
}, },
emits: ['update:show'], emits: ['update:show'],
setup(props, { emit }) { setup(props, { emit }) {
const { changePrimaryColor, changeSwitcher, updateContentTransition } = const { changePrimaryColor, updateSettingState } = useSettingActions()
useSettingActions()
const { const {
getAppTheme, getAppTheme,
getPrimaryColorOverride, getPrimaryColorOverride,
@ -97,9 +96,8 @@ const SettingDrawer = defineComponent({
changePrimaryColor, changePrimaryColor,
getAppTheme, getAppTheme,
getPrimaryColorOverride, getPrimaryColorOverride,
changeSwitcher,
contentTransitionOptions, contentTransitionOptions,
updateContentTransition, updateSettingState,
modelSwitchReactive, modelSwitchReactive,
} }
}, },
@ -133,7 +131,7 @@ const SettingDrawer = defineComponent({
v-model:value={this.modelSwitchReactive.getContentTransition} v-model:value={this.modelSwitchReactive.getContentTransition}
options={this.contentTransitionOptions} options={this.contentTransitionOptions}
onUpdateValue={(value) => { onUpdateValue={(value) => {
this.updateContentTransition(value) this.updateSettingState('contentTransition', value)
}} }}
/> />
<NDivider titlePlacement="center"> <NDivider titlePlacement="center">
@ -144,7 +142,7 @@ const SettingDrawer = defineComponent({
<NSwitch <NSwitch
v-model:value={this.modelSwitchReactive.getMenuTagSwitch} v-model:value={this.modelSwitchReactive.getMenuTagSwitch}
onUpdateValue={(bool: boolean) => onUpdateValue={(bool: boolean) =>
this.changeSwitcher(bool, 'menuTagSwitch') this.updateSettingState('menuTagSwitch', bool)
} }
/> />
</NDescriptionsItem> </NDescriptionsItem>
@ -152,7 +150,7 @@ const SettingDrawer = defineComponent({
<NSwitch <NSwitch
v-model:value={this.modelSwitchReactive.getBreadcrumbSwitch} v-model:value={this.modelSwitchReactive.getBreadcrumbSwitch}
onUpdateValue={(bool: boolean) => onUpdateValue={(bool: boolean) =>
this.changeSwitcher(bool, 'breadcrumbSwitch') this.updateSettingState('breadcrumbSwitch', bool)
} }
/> />
</NDescriptionsItem> </NDescriptionsItem>
@ -160,7 +158,7 @@ const SettingDrawer = defineComponent({
<NSwitch <NSwitch
v-model:value={this.modelSwitchReactive.getWatermarkSwitch} v-model:value={this.modelSwitchReactive.getWatermarkSwitch}
onUpdateValue={(bool: boolean) => onUpdateValue={(bool: boolean) =>
this.changeSwitcher(bool, 'watermarkSwitch') this.updateSettingState('watermarkSwitch', bool)
} }
/> />
</NDescriptionsItem> </NDescriptionsItem>
@ -168,7 +166,7 @@ const SettingDrawer = defineComponent({
<NSwitch <NSwitch
v-model:value={this.modelSwitchReactive.getCopyrightSwitch} v-model:value={this.modelSwitchReactive.getCopyrightSwitch}
onUpdateValue={(bool: boolean) => onUpdateValue={(bool: boolean) =>
this.changeSwitcher(bool, 'copyrightSwitch') this.updateSettingState('copyrightSwitch', bool)
} }
/> />
</NDescriptionsItem> </NDescriptionsItem>

View File

@ -38,7 +38,7 @@ import { useDevice } from '@/hooks/web'
import { getVariableToRefs, setVariable } from '@/global-variable' import { getVariableToRefs, setVariable } from '@/global-variable'
import { useFullscreen } from 'vue-hooks-plus' import { useFullscreen } from 'vue-hooks-plus'
import { useI18n } from '@/hooks/web' import { useI18n } from '@/hooks/web'
import { useMainPage } from '@/hooks/template' import { useSpinning } from '@/hooks/template'
import { useSettingGetters, useSettingActions } from '@/store' import { useSettingGetters, useSettingActions } from '@/store'
import type { IconEventMapOptions, IconEventMap } from './type' import type { IconEventMapOptions, IconEventMap } from './type'
@ -46,9 +46,9 @@ import type { IconEventMapOptions, IconEventMap } from './type'
export default defineComponent({ export default defineComponent({
name: 'AppSiderBar', name: 'AppSiderBar',
setup() { setup() {
const { updateLocale, changeSwitcher } = useSettingActions() const { updateLocale, updateSettingState } = useSettingActions()
const { t } = useI18n() const { t } = useI18n()
const { reload } = useMainPage() const { reload } = useSpinning()
const [isFullscreen, { toggleFullscreen, isEnabled }] = useFullscreen( const [isFullscreen, { toggleFullscreen, isEnabled }] = useFullscreen(
document.getElementsByTagName('html')[0], document.getElementsByTagName('html')[0],
@ -107,7 +107,7 @@ export default defineComponent({
globalSearchShown.value = true globalSearchShown.value = true
}, },
lock: () => { lock: () => {
changeSwitcher(true, 'lockScreenSwitch') updateSettingState('lockScreenSwitch', true)
}, },
menu: () => { menu: () => {
setVariable('globalDrawerValue', !globalDrawerValue.value) setVariable('globalDrawerValue', !globalDrawerValue.value)

View File

@ -54,9 +54,9 @@ const avatarDropdownActionMap = {
* *
*/ */
lockScreen: () => { lockScreen: () => {
const { changeSwitcher } = useSettingActions() const { updateSettingState } = useSettingActions()
changeSwitcher(true, 'lockScreenSwitch') updateSettingState('lockScreenSwitch', true)
}, },
} }

View File

@ -23,7 +23,7 @@ import AppRequestCancelerProvider from '@/app-components/provider/AppRequestCanc
import { getVariableToRefs } from '@/global-variable' import { getVariableToRefs } from '@/global-variable'
import { useSettingGetters } from '@/store' import { useSettingGetters } from '@/store'
import { useMainPage } from '@/hooks/template' import { useMaximize } from '@/hooks/template'
import type { GlobalThemeOverrides } from 'naive-ui' import type { GlobalThemeOverrides } from 'naive-ui'
@ -32,7 +32,7 @@ export default defineComponent({
setup() { setup() {
const router = useRouter() const router = useRouter()
const { maximize } = useMainPage() const { maximize } = useMaximize()
const { getContentTransition } = useSettingGetters() const { getContentTransition } = useSettingGetters()
const spinning = ref(false) const spinning = ref(false)
const themeOverridesSpin: GlobalThemeOverrides['Spin'] = { const themeOverridesSpin: GlobalThemeOverrides['Spin'] = {

View File

@ -22,5 +22,6 @@
"QRCode": "QRCode", "QRCode": "QRCode",
"SvgIcon": "SVG Icon", "SvgIcon": "SVG Icon",
"TemplateHooks": "Template Api", "TemplateHooks": "Template Api",
"Modal": "Modal" "Modal": "Modal",
"ContextMenu": "Right Click Menu"
} }

View File

@ -22,5 +22,6 @@
"QRCode": "二维码", "QRCode": "二维码",
"SvgIcon": "SVG 图标", "SvgIcon": "SVG 图标",
"TemplateHooks": "模板内置 Api", "TemplateHooks": "模板内置 Api",
"Modal": "模态框" "Modal": "模态框",
"ContextMenu": "右键菜单"
} }

View File

@ -26,7 +26,7 @@ import { redirectRouterToDashboard } from '@/router/helper/routerCopilot'
import { WHITE_ROUTES } from '@/app-config/routerConfig' import { WHITE_ROUTES } from '@/app-config/routerConfig'
import { validRole } from '@/router/helper/routerCopilot' import { validRole } from '@/router/helper/routerCopilot'
import { isValueType } from '@/utils/basic' import { isValueType } from '@/utils/basic'
import { useRootRoute } from '@/hooks/template' import { useAppRoot } from '@/hooks/template'
import type { Router, RouteLocationNormalized } from 'vue-router' import type { Router, RouteLocationNormalized } from 'vue-router'
import type { AppRouteMeta } from '@/router/type' import type { AppRouteMeta } from '@/router/type'
@ -34,7 +34,7 @@ import type { AppRouteMeta } from '@/router/type'
/** 路由守卫 */ /** 路由守卫 */
export const permissionRouter = (router: Router) => { export const permissionRouter = (router: Router) => {
const { beforeEach } = router const { beforeEach } = router
const { getRootPath } = useRootRoute() const { getRootPath } = useAppRoot()
const isToLogin = ( const isToLogin = (
to: RouteLocationNormalized, to: RouteLocationNormalized,

View File

@ -15,7 +15,7 @@ import { useVueRouter } from '@/hooks/web'
import { setStorage } from '@/utils/cache' import { setStorage } from '@/utils/cache'
import { getAppEnvironment } from '@/utils/basic' import { getAppEnvironment } from '@/utils/basic'
import { useSigningGetters } from '@/store' import { useSigningGetters } from '@/store'
import { useRootRoute } from '@/hooks/template' import { useAppRoot } from '@/hooks/template'
import type { Router } from 'vue-router' import type { Router } from 'vue-router'
import type { AppRouteMeta } from '@/router/type' import type { AppRouteMeta } from '@/router/type'
@ -132,7 +132,7 @@ export const redirectRouterToDashboard = (isReplace = true) => {
const { router } = useVueRouter() const { router } = useVueRouter()
const { push, replace } = router const { push, replace } = router
const { getRootPath } = useRootRoute() const { getRootPath } = useAppRoot()
setStorage('menuKey', getRootPath.value) setStorage('menuKey', getRootPath.value)

View File

@ -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

View File

@ -1,9 +1,9 @@
import Layout from '@/layout' import Layout from '@/layout'
import { appExpandRoutes } from './appRouteModules' import { appExpandRoutes } from './appRouteModules'
import { useRootRoute } from '@/hooks/template' import { useAppRoot } from '@/hooks/template'
export default async () => { export default async () => {
const { getRootPath } = useRootRoute() const { getRootPath } = useAppRoot()
return [ return [
/** /**

View File

@ -10,7 +10,7 @@
*/ */
import { piniaMenuStore } from '../modules/menu' import { piniaMenuStore } from '../modules/menu'
import { useRootRoute } from '@/hooks/template' import { useAppRoot } from '@/hooks/template'
export const useMenuGetters = () => { export const useMenuGetters = () => {
const variable = piniaMenuStore() const variable = piniaMenuStore()
@ -35,7 +35,7 @@ export const useMenuGetters = () => {
* @remark * @remark
*/ */
const getMenuTagOptions = computed(() => { const getMenuTagOptions = computed(() => {
const { getRootPath } = useRootRoute() const { getRootPath } = useAppRoot()
return variable.menuTagOptions.map((curr, _idx, currentArray) => { return variable.menuTagOptions.map((curr, _idx, currentArray) => {
if (curr.key === getMenuKey.value && curr.key !== getRootPath.value) { if (curr.key === getMenuKey.value && curr.key !== getRootPath.value) {

View File

@ -65,6 +65,12 @@ export const useSettingGetters = () => {
*/ */
const getWatermarkSwitch = computed(() => variable.watermarkSwitch) const getWatermarkSwitch = computed(() => variable.watermarkSwitch)
/**
*
* @remark
*/
const getWatermarkConfig = computed(() => variable.watermarkConfig)
return { return {
getDrawerPlacement, getDrawerPlacement,
getPrimaryColorOverride, getPrimaryColorOverride,
@ -76,21 +82,17 @@ export const useSettingGetters = () => {
getCopyrightSwitch, getCopyrightSwitch,
getContentTransition, getContentTransition,
getWatermarkSwitch, getWatermarkSwitch,
getWatermarkConfig,
} }
} }
export const useSettingActions = () => { export const useSettingActions = () => {
const { const { updateLocale, changePrimaryColor, updateSettingState } =
updateLocale, piniaSettingStore()
changePrimaryColor,
changeSwitcher,
updateContentTransition,
} = piniaSettingStore()
return { return {
updateLocale, updateLocale,
changePrimaryColor, changePrimaryColor,
changeSwitcher, updateSettingState,
updateContentTransition,
} }
} }

View File

@ -15,7 +15,7 @@ import { APP_MENU_CONFIG } from '@/app-config/appConfig'
import { RIcon } from '@/components' import { RIcon } from '@/components'
import { isValueType } from '@/utils/basic' import { isValueType } from '@/utils/basic'
import { getStorage } from '@/utils/cache' import { getStorage } from '@/utils/cache'
import { useRootRoute } from '@/hooks/template' import { useAppRoot } from '@/hooks/template'
import type { import type {
AppMenuOption, AppMenuOption,
@ -174,7 +174,7 @@ export const hasMenuIcon = (option: AppMenuOption) => {
/** 获取缓存的 menu key, 如果未获取到则使用 getRootPath 当作默认激活路由菜单 */ /** 获取缓存的 menu key, 如果未获取到则使用 getRootPath 当作默认激活路由菜单 */
export const getCatchMenuKey = () => { export const getCatchMenuKey = () => {
const { getRootPath } = useRootRoute() const { getRootPath } = useAppRoot()
const cacheMenuKey = getStorage<AppMenuKey>( const cacheMenuKey = getStorage<AppMenuKey>(
'menuKey', 'menuKey',
'sessionStorage', 'sessionStorage',

View File

@ -5,10 +5,12 @@ import { colorToRgba } from '@/utils/element'
import { useI18n } from '@/hooks/web' import { useI18n } from '@/hooks/web'
import { APP_THEME } from '@/app-config/designConfig' import { APP_THEME } from '@/app-config/designConfig'
import { useDayjs } from '@/hooks/web' 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 { SettingState } from '@/store/modules/setting/type'
import type { LocalKey } from '@/hooks/web' import type { LocalKey } from '@/hooks/web'
import type { AnyFC } from '@/types/modules/utils'
export const piniaSettingStore = defineStore( export const piniaSettingStore = defineStore(
'setting', 'setting',
@ -36,13 +38,9 @@ export const piniaSettingStore = defineStore(
copyrightSwitch: true, // 底部区域开关 copyrightSwitch: true, // 底部区域开关
contentTransition: 'scale', // 切换过渡效果 contentTransition: 'scale', // 切换过渡效果
watermarkSwitch: false, // 水印开关, watermarkSwitch: false, // 水印开关,
watermarkConfig: cloneDeep(APP_WATERMARK_CONFIG),
}) })
/** 更新过渡效果 */
const updateContentTransition = (value: string) => {
settingState.contentTransition = value
}
/** 修改当前语言 */ /** 修改当前语言 */
const updateLocale = (key: string) => { const updateLocale = (key: string) => {
locale(key) locale(key)
@ -73,29 +71,38 @@ export const piniaSettingStore = defineStore(
/** /**
* *
* @param bool * @param key settingState key
* @param key `settingState` * @param value settingState value
* @param cb
* *
* @remark `boolean` * settingState key settingState
*
*
* @example
* updateSettingState('drawerPlacement', 'left')
* updateSettingState('appTheme', true)
*/ */
const changeSwitcher = ( const updateSettingState = <
bool: boolean, T extends keyof SettingState,
key: keyof ConditionalPick<SettingState, boolean>, V extends typeof settingState,
C extends AnyFC,
>(
key: T,
value: V[T],
cb?: C,
) => { ) => {
if ( if (Object.hasOwn(settingState, key)) {
Object.hasOwn(settingState, key) && settingState[key] = value
typeof settingState[key] === 'boolean'
) {
settingState[key] = bool
} }
cb?.()
} }
return { return {
...toRefs(settingState), ...toRefs(settingState),
updateLocale, updateLocale,
changePrimaryColor, changePrimaryColor,
changeSwitcher, updateSettingState,
updateContentTransition,
} }
}, },
{ {

View File

@ -1,5 +1,6 @@
import type { GlobalThemeOverrides } from 'naive-ui' import type { GlobalThemeOverrides } from 'naive-ui'
import type { Placement } from '@/types/modules/component' import type { Placement } from '@/types/modules/component'
import type { APP_WATERMARK_CONFIG } from '@/app-config/appConfig'
export interface SettingState { export interface SettingState {
drawerPlacement: Placement drawerPlacement: Placement
@ -12,4 +13,5 @@ export interface SettingState {
watermarkSwitch: boolean watermarkSwitch: boolean
copyrightSwitch: boolean copyrightSwitch: boolean
contentTransition: string contentTransition: string
watermarkConfig: typeof APP_WATERMARK_CONFIG
} }

View File

@ -1,23 +1,66 @@
/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-explicit-any */
/**
*
*
*
* @example
* ConditionalKeys<{ a: string, b: number }, string> // 'a'
*/
export type ConditionalKeys<Base, Condition> = NonNullable< export type ConditionalKeys<Base, Condition> = NonNullable<
{ {
[Key in keyof Base]: Base[Key] extends Condition ? Key : never [Key in keyof Base]: Base[Key] extends Condition ? Key : never
}[keyof Base] }[keyof Base]
> >
/**
*
*
*
* @example
* ConditionalPick<{ a: string, b: number }, string> // { a: string }
*/
export type ConditionalPick<Base, Condition> = Pick< export type ConditionalPick<Base, Condition> = Pick<
Base, Base,
ConditionalKeys<Base, Condition> ConditionalKeys<Base, Condition>
> >
/**
*
*
*
* @example
* const A: Recordable = { a: 1, b: [] }
*/
export type Recordable<T = any> = Record<string, T> export type Recordable<T = any> = Record<string, T>
/**
*
*
*
* @example
* Keys<{ a: string, b: number }> // 'a' | 'b'
*/
export type ValueOf<T extends object> = T[keyof T] export type ValueOf<T extends object> = T[keyof T]
/**
*
*
*
* @example
* Mutable<{ readonly a: string }> // { a: string }
*/
export type Mutable<T> = { export type Mutable<T> = {
-readonly [P in keyof T]: T[P] -readonly [P in keyof T]: T[P]
} }
/**
*
*
*
* @example
* DeepMutable<{ readonly a: { readonly b: { readonly c: string } } }> // { a: { b: { c: string } } }
*/
export type DeepMutable<T> = { export type DeepMutable<T> = {
-readonly [P in keyof T]: T[P] extends ReadonlyArray<infer U> -readonly [P in keyof T]: T[P] extends ReadonlyArray<infer U>
? Array<DeepMutable<U>> ? Array<DeepMutable<U>>

View File

@ -1,6 +1,5 @@
/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-explicit-any */
import type CryptoJS from 'crypto-js' import type CryptoJS from 'crypto-js'
import type { BasicTarget } from './vue'
export type StorageLike = 'sessionStorage' | 'localStorage' export type StorageLike = 'sessionStorage' | 'localStorage'
@ -10,10 +9,6 @@ export type RemoveStorageKey =
| 'all-sessionStorage' | 'all-sessionStorage'
| 'all-localStorage' | 'all-localStorage'
export type EventListenerOrEventListenerObject =
| EventListener
| EventListenerObject
export type ValidateValueType = export type ValidateValueType =
| 'BigUint64Array' | 'BigUint64Array'
| 'BigInt64Array' | 'BigInt64Array'
@ -93,7 +88,3 @@ export type ElementSelector = string | `attr:${string}`
export type MaybeArray<T> = T | T[] export type MaybeArray<T> = T | T[]
export type DownloadAnyFileDataType = Blob | File | string | ArrayBuffer export type DownloadAnyFileDataType = Blob | File | string | ArrayBuffer
export type EventListenerTarget = BasicTarget<
HTMLElement | Element | Window | Document
>

View File

@ -232,3 +232,27 @@ export function print<T extends BasicTarget<HTMLElement>>(
watchEffectWithTarget(watcher) 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 = <T extends Record<string, unknown>, K extends keyof T>(
targetObject: T,
targetKeys: K | K[],
): Omit<T, K> => {
const keys = Array.isArray(targetKeys) ? targetKeys : [targetKeys]
keys.forEach((key) => {
delete targetObject[key]
})
return targetObject
}

View File

@ -2,83 +2,14 @@ import { isValueType } from '@/utils/basic'
import { APP_REGEX } from '@/app-config/regexConfig' import { APP_REGEX } from '@/app-config/regexConfig'
import { unrefElement } from '@/utils/vue' import { unrefElement } from '@/utils/vue'
import { watchEffectWithTarget } from '@/utils/vue' import { watchEffectWithTarget } from '@/utils/vue'
import { useCurrentElement } from '@vueuse/core'
import type { import type {
EventListenerOrEventListenerObject,
PartialCSSStyleDeclaration, PartialCSSStyleDeclaration,
ElementSelector, ElementSelector,
} from '@/types/modules/utils' } from '@/types/modules/utils'
import type { EventListenerTarget } from '@/types/modules/utils'
import type { BasicTarget, TargetValue } from '@/types/modules/vue' 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<HTMLElement | Element | Window | Document>,
>(
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<HTMLElement | Element | Window | Document>,
>(
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 * @param target Target element dom

View File

@ -90,15 +90,7 @@ const Dashboard = defineComponent({
<NLayout class="dashboard-layout layout-full"> <NLayout class="dashboard-layout layout-full">
<NCard> <NCard>
{{ {{
header: () => header: () => <RIcon name="ray" size="64" />,
h(
RIcon,
{
name: 'ray',
size: '64',
},
{},
),
default: () => '当你看见这个页面后, 就说明项目已经启动成功了~', default: () => '当你看见这个页面后, 就说明项目已经启动成功了~',
}} }}
</NCard> </NCard>

View File

@ -0,0 +1,78 @@
/**
*
* @author Ray <https://github.com/XiaoDaiGua-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<HTMLElement | null>(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 (
<NSpace vertical wrapItem={false}>
<NCard title="useContextmenuCoordinate + NDropdown 实现右键菜单">
<NSpace vertical>
<h3></h3>
<div
ref="demoOneRef"
style="width: 100%; height: 200px; background-color: rgba(0, 128, 0, 0.5)"
>
</div>
</NSpace>
</NCard>
<NDropdown
show={show}
x={x}
y={y}
options={this.options}
trigger="manual"
placement="bottom-start"
/>
</NSpace>
)
},
})

View File

@ -55,18 +55,10 @@ const TableView = defineComponent({
key: 'tags', key: 'tags',
render: (row: RowData) => { render: (row: RowData) => {
const tags = row.tags.map((tagKey) => { const tags = row.tags.map((tagKey) => {
return h( return (
NTag, <NTag type="info" bordered={false} style="margin-right: 6px">
{ {tagKey}
style: { </NTag>
marginRight: '6px',
},
type: 'info',
bordered: false,
},
{
default: () => tagKey,
},
) )
}) })
@ -138,7 +130,7 @@ const TableView = defineComponent({
key: 'edit', key: 'edit',
}, },
{ {
label: () => h('span', { style: { color: 'red' } }, '删除'), label: () => <span style="color: red;"></span>,
key: 'delete', key: 'delete',
}, },
] ]

View File

@ -9,19 +9,36 @@
* @remark * @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 { getVariableToRefs } from '@/global-variable'
import { useSettingGetters } from '@/store'
export default defineComponent({ export default defineComponent({
name: 'TemplateHooks', name: 'TemplateHooks',
setup() { setup() {
const currentMenuOption = ref('') const currentMenuOption = ref('')
const maximizeRef = getVariableToRefs('layoutContentMaximize') const maximizeRef = getVariableToRefs('layoutContentMaximize')
const watermark = ref(useSettingGetters().getWatermarkConfig.value.content)
const { navigationTo } = useAppMenu() const { navigationTo } = useAppNavigation()
const { reload, maximize, openSpin, closeSpin } = useMainPage() const { maximize, isLayoutContentMaximized } = useMaximize()
const { reload, openSpin, closeSpin } = useSpinning()
const {
showWatermark,
hiddenWatermark,
setWatermarkContent,
toggleWatermark,
} = useWatermark()
const { changeDarkTheme, changeLightTheme, toggleTheme, getAppTheme } =
useTheme()
return { return {
navigationTo, navigationTo,
@ -31,10 +48,35 @@ export default defineComponent({
maximizeRef, maximizeRef,
openSpin, openSpin,
closeSpin, closeSpin,
showWatermark,
hiddenWatermark,
setWatermarkContent,
watermark,
toggleWatermark,
changeDarkTheme,
changeLightTheme,
toggleTheme,
getAppTheme,
isLayoutContentMaximized,
} }
}, },
render() { 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 ( return (
<NSpace wrapItem={false} vertical> <NSpace wrapItem={false} vertical>
@ -44,21 +86,38 @@ export default defineComponent({
</h3> </h3>
</NCard> </NCard>
<NCard title="useAppMenu 导航方法"> <NCard title="useTheme 主题">
<NSpace vertical>
<h3>getAppTheme : {getAppTheme().themeLabel}</h3>
<NSpace>
<NButton onClick={() => changeDarkTheme()}></NButton>
<NButton onClick={() => changeLightTheme()}></NButton>
<NButton onClick={() => toggleTheme()}></NButton>
</NSpace>
</NSpace>
</NCard>
<NCard title="useWatermark 水印">
<NSpace vertical>
<NInput
v-model:value={this.watermark}
onInput={(val) => {
setWatermarkContent(val)
}}
/>
<NSpace>
<NButton onClick={() => showWatermark()}></NButton>
<NButton onClick={() => hiddenWatermark()}></NButton>
<NButton onClick={() => toggleWatermark()}></NButton>
</NSpace>
</NSpace>
</NCard>
<NCard title="useSpinning">
<h3> <h3>
navigationTo 使
vue 800ms
</h3> </h3>
<br /> <br />
<NButton onClick={() => navigationTo(15)}></NButton> <NSpace>
</NCard>
<NCard title="useMainPage 主页面方法">
<NCard title="reload 加载函数">
<h3>
使
vue 800ms
</h3>
<br />
<NButton <NButton
onClick={() => { onClick={() => {
reload() reload()
@ -77,16 +136,28 @@ export default defineComponent({
> >
</NButton> </NButton>
</NCard> </NSpace>
<NCard title="maximize 内容区域最大化"> </NCard>
<NButton <NCard title="useMaximize 内容区域最大化">
onClick={() => { <h3>
maximize(!this.maximizeRef) isLayoutContentMaximized :
}} {isLayoutContentMaximized ? '最大化' : '正常尺寸'}
> </h3>
<NButton
</NButton> onClick={() => {
</NCard> maximize(!this.maximizeRef)
}}
>
</NButton>
</NCard>
<NCard title="useAppNavigation 导航方法">
<h3>
navigationTo
</h3>
<br />
<NButton onClick={() => navigationTo(16)}></NButton>
</NCard> </NCard>
</NSpace> </NSpace>
) )

View File

@ -5,7 +5,7 @@ import { useI18n } from '@/hooks/web'
import { APP_CATCH_KEY } from '@/app-config/appConfig' import { APP_CATCH_KEY } from '@/app-config/appConfig'
import { setVariable, getVariableToRefs } from '@/global-variable' import { setVariable, getVariableToRefs } from '@/global-variable'
import { useSigningActions } from '@/store' import { useSigningActions } from '@/store'
import { useRootRoute } from '@/hooks/template' import { useAppRoot } from '@/hooks/template'
import type { FormInst } from 'naive-ui' import type { FormInst } from 'naive-ui'
@ -16,7 +16,7 @@ export default defineComponent({
const { t } = useI18n() const { t } = useI18n()
const { signing } = useSigningActions() const { signing } = useSigningActions()
const { getRootPath } = useRootRoute() const { getRootPath } = useAppRoot()
const globalSpinning = getVariableToRefs('globalSpinning') const globalSpinning = getVariableToRefs('globalSpinning')
const useSigningForm = () => ({ const useSigningForm = () => ({

View File

@ -33,8 +33,8 @@ import config from './cfg'
import type { PluginOption } from 'vite' import type { PluginOption } from 'vite'
// 仅适用于构建模式任何构建模式preview、build、report... // 仅适用于报告模式
function onlyBuildOptions(mode: string) { function onlyReportOptions(mode: string) {
return [ return [
visualizer({ visualizer({
gzipSize: true, // 搜集 `gzip` 压缩包 gzipSize: true, // 搜集 `gzip` 压缩包
@ -43,6 +43,12 @@ function onlyBuildOptions(mode: string) {
filename: 'visualizer.html', filename: 'visualizer.html',
open: mode === 'report' ? true : false, // 以默认服务器代理打开文件 open: mode === 'report' ? true : false, // 以默认服务器代理打开文件
}), }),
]
}
// 仅适用于构建模式任何构建模式preview、build、report...
function onlyBuildOptions(mode: string) {
return [
viteCDNPlugin({ viteCDNPlugin({
// modules 顺序 vue, vue-demi 必须保持当前顺序加载,否则会出现加载错误问题 // modules 顺序 vue, vue-demi 必须保持当前顺序加载,否则会出现加载错误问题
modules: [ modules: [
@ -180,6 +186,7 @@ function baseOptions(mode: string) {
export default function (mode: string): PluginOption[] { export default function (mode: string): PluginOption[] {
const plugins = const plugins =
mode === 'development' ? onlyDevOptions(mode) : onlyBuildOptions(mode) mode === 'development' ? onlyDevOptions(mode) : onlyBuildOptions(mode)
const reportPlugins = mode === 'report' ? onlyReportOptions(mode) : []
return [...baseOptions(mode), ...plugins] return [...baseOptions(mode), ...plugins, ...reportPlugins]
} }