mirror of
https://github.com/XiaoDaiGua-Ray/ray-template.git
synced 2025-06-10 09:49:17 +08:00
version: v4.4.2
This commit is contained in:
parent
9e3cbac091
commit
f272832a8c
@ -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'],
|
||||||
|
33
CHANGELOG.md
33
CHANGELOG.md
@ -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` 后再重新安装依赖,这是缓存导致的问题。
|
||||||
|
|
||||||
|
@ -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 的语言
|
||||||
- **主题**:可配置的主题
|
- **主题**:可配置的主题
|
||||||
- **国际化**:内置完善的国际化方案
|
- **国际化**:内置完善的国际化方案
|
||||||
|
@ -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.
|
||||||
|
@ -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",
|
||||||
|
@ -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()
|
||||||
}
|
}
|
||||||
|
@ -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()
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -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
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
@ -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',
|
||||||
|
@ -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,
|
||||||
|
@ -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,
|
||||||
|
@ -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>
|
||||||
),
|
),
|
||||||
|
@ -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,
|
||||||
|
@ -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'
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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 }
|
|
||||||
|
@ -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))
|
||||||
}
|
}
|
||||||
|
1
src/hooks/components/index.ts
Normal file
1
src/hooks/components/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from './useContextmenuCoordinate'
|
86
src/hooks/components/useContextmenuCoordinate.ts
Normal file
86
src/hooks/components/useContextmenuCoordinate.ts
Normal 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
|
||||||
|
>
|
@ -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 }
|
|
||||||
|
@ -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>
|
@ -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>
|
@ -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,
|
|
||||||
}
|
|
||||||
}
|
|
@ -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>
|
@ -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>
|
59
src/hooks/template/useSpinning.ts
Normal file
59
src/hooks/template/useSpinning.ts
Normal 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>
|
90
src/hooks/template/useTheme.ts
Normal file
90
src/hooks/template/useTheme.ts
Normal 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>
|
92
src/hooks/template/useWatermark.ts
Normal file
92
src/hooks/template/useWatermark.ts
Normal 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>
|
@ -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 }
|
|
||||||
|
@ -155,3 +155,5 @@ export const useDayjs = () => {
|
|||||||
isDateInRange,
|
isDateInRange,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type UseDayjsReturnType = ReturnType<typeof useDayjs>
|
||||||
|
@ -33,3 +33,5 @@ export function useDevice() {
|
|||||||
isTabletOrSmaller,
|
isTabletOrSmaller,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type UseDeviceReturnType = ReturnType<typeof useDevice>
|
||||||
|
@ -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>
|
||||||
|
@ -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>
|
||||||
|
@ -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',
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -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>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
@ -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 {
|
||||||
|
@ -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: () => '暗',
|
||||||
}}
|
}}
|
||||||
|
@ -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>
|
||||||
|
@ -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)
|
||||||
|
@ -54,9 +54,9 @@ const avatarDropdownActionMap = {
|
|||||||
* 锁定屏幕
|
* 锁定屏幕
|
||||||
*/
|
*/
|
||||||
lockScreen: () => {
|
lockScreen: () => {
|
||||||
const { changeSwitcher } = useSettingActions()
|
const { updateSettingState } = useSettingActions()
|
||||||
|
|
||||||
changeSwitcher(true, 'lockScreenSwitch')
|
updateSettingState('lockScreenSwitch', true)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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'] = {
|
||||||
|
@ -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"
|
||||||
}
|
}
|
||||||
|
@ -22,5 +22,6 @@
|
|||||||
"QRCode": "二维码",
|
"QRCode": "二维码",
|
||||||
"SvgIcon": "SVG 图标",
|
"SvgIcon": "SVG 图标",
|
||||||
"TemplateHooks": "模板内置 Api",
|
"TemplateHooks": "模板内置 Api",
|
||||||
"Modal": "模态框"
|
"Modal": "模态框",
|
||||||
|
"ContextMenu": "右键菜单"
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
16
src/router/modules/demo/context-menu.ts
Normal file
16
src/router/modules/demo/context-menu.ts
Normal 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
|
@ -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 [
|
||||||
/**
|
/**
|
||||||
|
@ -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) {
|
||||||
|
@ -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,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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',
|
||||||
|
@ -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,
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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>>
|
||||||
|
@ -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
|
|
||||||
>
|
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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>
|
||||||
|
78
src/views/demo/context-menu/index.tsx
Normal file
78
src/views/demo/context-menu/index.tsx
Normal 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>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
})
|
@ -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',
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
@ -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>
|
||||||
)
|
)
|
||||||
|
@ -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 = () => ({
|
||||||
|
@ -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]
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user