mirror of
https://github.com/XiaoDaiGua-Ray/ray-template.git
synced 2025-04-05 19:42:07 +08:00
v4.3.0
This commit is contained in:
parent
f7887989f9
commit
32183d0ab9
11
CHANGELOG.md
11
CHANGELOG.md
@ -1,5 +1,16 @@
|
||||
# CHANGE LOG
|
||||
|
||||
## 4.3.0
|
||||
|
||||
提供了专用于一些模板的 `hooks`,可以通过这些方法调用模板的特定功能。并且该功能后续是模板维护的重点。
|
||||
|
||||
改造 `pinia` 使用方式,统一使用 `xxxGetters`, `xxxActions` 去调用仓库数据与方法。
|
||||
|
||||
### Feats
|
||||
|
||||
- 新增 `store/hooks` 包
|
||||
- 新增 `hooks/template` 包
|
||||
|
||||
## 4.2.9
|
||||
|
||||
主要更新了命名问题。并且使用单词检查器,扫描整个项目替换了拼写错误的单词。
|
||||
|
@ -14,9 +14,9 @@
|
||||
import { NInput, NForm, NFormItem, NButton, NSpace } from 'naive-ui'
|
||||
import AppAvatar from '@/app-components/app/AppAvatar/index'
|
||||
|
||||
import { useSetting } from '@/store'
|
||||
import useAppLockScreen from '@/app-components/app/AppLockScreen/appLockVar'
|
||||
import { rules, useCondition } from '@/app-components/app/AppLockScreen/hook'
|
||||
import { useSettingGetters, useSettingActions } from '@/store'
|
||||
|
||||
import type { FormInst, InputInst } from 'naive-ui'
|
||||
|
||||
@ -27,7 +27,7 @@ const LockScreen = defineComponent({
|
||||
const inputInstRef = ref<InputInst | null>(null)
|
||||
|
||||
const { setLockAppScreen } = useAppLockScreen()
|
||||
const { changeSwitcher } = useSetting()
|
||||
const { changeSwitcher } = useSettingActions()
|
||||
|
||||
const state = reactive({
|
||||
lockCondition: useCondition(),
|
||||
|
@ -15,7 +15,7 @@ import { NInput, NForm, NFormItem, NButton, NSpace } from 'naive-ui'
|
||||
import AppAvatar from '@/app-components/app/AppAvatar/index'
|
||||
|
||||
import dayjs from 'dayjs'
|
||||
import { useSetting, useSigning } from '@/store'
|
||||
import { useSigningActions, useSettingActions } from '@/store'
|
||||
import { rules, useCondition } from '@/app-components/app/AppLockScreen/hook'
|
||||
import useAppLockScreen from '@/app-components/app/AppLockScreen/appLockVar'
|
||||
import { useDevice } from '@/hooks/web/index'
|
||||
@ -28,8 +28,8 @@ export default defineComponent({
|
||||
const formRef = ref<FormInst | null>(null)
|
||||
const inputInstRef = ref<InputInst | null>(null)
|
||||
|
||||
const { logout } = useSigning()
|
||||
const { changeSwitcher } = useSetting()
|
||||
const { logout } = useSigningActions()
|
||||
const { changeSwitcher } = useSettingActions()
|
||||
const { setLockAppScreen } = useAppLockScreen()
|
||||
const { isTabletOrSmaller } = useDevice()
|
||||
|
||||
|
@ -21,26 +21,25 @@ import { NModal } from 'naive-ui'
|
||||
import LockScreen from './components/LockScreen'
|
||||
import UnlockScreen from './components/UnlockScreen'
|
||||
|
||||
import { useSetting } from '@/store'
|
||||
import useAppLockScreen from '@/app-components/app/AppLockScreen/appLockVar'
|
||||
import { useSettingGetters } from '@/store'
|
||||
|
||||
const AppLockScreen = defineComponent({
|
||||
name: 'AppLockScreen',
|
||||
setup() {
|
||||
const settingStore = useSetting()
|
||||
const { lockScreenSwitch } = storeToRefs(settingStore)
|
||||
const { getLockScreenSwitch } = useSettingGetters()
|
||||
|
||||
const { getLockAppScreen } = useAppLockScreen()
|
||||
|
||||
return {
|
||||
lockScreenSwitch,
|
||||
getLockScreenSwitch,
|
||||
getLockAppScreen,
|
||||
}
|
||||
},
|
||||
render() {
|
||||
return (
|
||||
<NModal
|
||||
v-model:show={this.lockScreenSwitch}
|
||||
v-model:show={this.getLockScreenSwitch}
|
||||
transformOrigin="center"
|
||||
show
|
||||
autoFocus={false}
|
||||
|
@ -62,7 +62,7 @@ const RayLink = defineComponent({
|
||||
return (
|
||||
<NSpace>
|
||||
{this.avatarOptions.map((curr) => (
|
||||
<NTooltip>
|
||||
<NTooltip key={curr.key}>
|
||||
{{
|
||||
trigger: () => (
|
||||
<NAvatar
|
||||
|
@ -28,22 +28,17 @@ import {
|
||||
NGlobalStyle,
|
||||
} from 'naive-ui'
|
||||
|
||||
import { useSetting } from '@/store'
|
||||
import { naiveLocales } from '@/locales/helper'
|
||||
import { useSettingGetters } from '@/store'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'GlobalProvider',
|
||||
setup(_, { expose }) {
|
||||
const settingStore = useSetting()
|
||||
const { getPrimaryColorOverride, getAppTheme, getLocaleLanguage } =
|
||||
useSettingGetters()
|
||||
|
||||
const modelPrimaryColorOverride = computed(
|
||||
() => settingStore.primaryColorOverride,
|
||||
)
|
||||
const modelThemeValue = computed(() =>
|
||||
settingStore.themeValue ? darkTheme : null,
|
||||
)
|
||||
const localePackage = computed(() => {
|
||||
const key = settingStore.localeLanguage
|
||||
const key = getLocaleLanguage.value
|
||||
|
||||
return naiveLocales(key)
|
||||
})
|
||||
@ -52,7 +47,7 @@ export default defineComponent({
|
||||
['message', 'dialog', 'notification', 'loadingBar'],
|
||||
{
|
||||
configProviderProps: computed(() => ({
|
||||
theme: modelThemeValue.value,
|
||||
theme: getAppTheme.value ? darkTheme : null,
|
||||
})),
|
||||
notificationProviderProps: {},
|
||||
},
|
||||
@ -66,16 +61,16 @@ export default defineComponent({
|
||||
expose()
|
||||
|
||||
return {
|
||||
modelPrimaryColorOverride,
|
||||
modelThemeValue,
|
||||
getPrimaryColorOverride,
|
||||
localePackage,
|
||||
getAppTheme,
|
||||
}
|
||||
},
|
||||
render() {
|
||||
return (
|
||||
<NConfigProvider
|
||||
themeOverrides={this.modelPrimaryColorOverride}
|
||||
theme={this.modelThemeValue}
|
||||
themeOverrides={this.getPrimaryColorOverride}
|
||||
theme={this.getAppTheme ? darkTheme : null}
|
||||
locale={this.localePackage.locale}
|
||||
dateLocale={this.localePackage.dateLocal}
|
||||
>
|
||||
|
@ -13,17 +13,15 @@ import './index.scss'
|
||||
|
||||
import { getStorage } from '@/utils/cache'
|
||||
import { get } from 'lodash-es'
|
||||
import { useSetting } from '@/store'
|
||||
import { addClass, removeClass, addStyle, colorToRgba } from '@/utils/element'
|
||||
import { useSettingGetters } from '@/store'
|
||||
|
||||
import type { SettingState } from '@/store/modules/setting/type'
|
||||
|
||||
const AppStyleProvider = defineComponent({
|
||||
name: 'AppStyleProvider',
|
||||
setup(_, { expose }) {
|
||||
const settingStore = useSetting()
|
||||
|
||||
const { themeValue } = storeToRefs(settingStore)
|
||||
const { getAppTheme } = useSettingGetters()
|
||||
|
||||
/** 同步主题色变量至 body, 如果未获取到缓存值则已默认值填充 */
|
||||
const syncPrimaryColorToBody = () => {
|
||||
@ -72,7 +70,7 @@ const AppStyleProvider = defineComponent({
|
||||
*
|
||||
* 初始化时根据当前主题色进行初始化 body 的 class 属性
|
||||
*
|
||||
* 根据 themeValue 进行初始化
|
||||
* 根据 getAppTheme 进行初始化
|
||||
*/
|
||||
const body = document.body
|
||||
const darkClassName = 'ray-template--dark'
|
||||
@ -89,7 +87,7 @@ const AppStyleProvider = defineComponent({
|
||||
hiddenLoadingAnimation()
|
||||
|
||||
watch(
|
||||
() => themeValue.value,
|
||||
() => getAppTheme.value,
|
||||
(ndata) => {
|
||||
updateGlobalThemeClass(ndata)
|
||||
},
|
||||
|
@ -12,19 +12,19 @@
|
||||
import { NWatermark } from 'naive-ui'
|
||||
|
||||
import { APP_WATERMARK_CONFIG } from '@/app-config/appConfig'
|
||||
import { useSetting } from '@/store'
|
||||
import { useSettingGetters } from '@/store'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'AppWatermarkProvider',
|
||||
setup() {
|
||||
const { watermarkSwitch } = storeToRefs(useSetting())
|
||||
const { getWatermarkSwitch } = useSettingGetters()
|
||||
|
||||
return {
|
||||
watermarkSwitch,
|
||||
getWatermarkSwitch,
|
||||
}
|
||||
},
|
||||
render() {
|
||||
return this.watermarkSwitch ? (
|
||||
return this.getWatermarkSwitch ? (
|
||||
<NWatermark cross fullscreen {...APP_WATERMARK_CONFIG} />
|
||||
) : null
|
||||
},
|
||||
|
@ -40,7 +40,6 @@ import { CanvasRenderer } from 'echarts/renderers' // `echarts` 渲染器
|
||||
import { NCard } from 'naive-ui'
|
||||
|
||||
import props from './props'
|
||||
import { useSetting } from '@/store'
|
||||
import { throttle } from 'lodash-es'
|
||||
import { completeSize } from '@/utils/element'
|
||||
import { call } from '@/utils/vue/index'
|
||||
@ -50,6 +49,7 @@ import { useResizeObserver } from '@vueuse/core'
|
||||
import RMoreDropdown from '@/components/RMoreDropdown/index'
|
||||
import { renderNode } from '@use-utils/vue/index'
|
||||
import { downloadBase64File } from '@use-utils/basic'
|
||||
import { useSettingGetters } from '@/store'
|
||||
|
||||
import type { WatchStopHandle } from 'vue'
|
||||
import type { AnyFC } from '@/types/modules/utils'
|
||||
@ -70,8 +70,7 @@ export default defineComponent({
|
||||
name: 'RChart',
|
||||
props,
|
||||
setup(props, { expose }) {
|
||||
const settingStore = useSetting()
|
||||
const { themeValue: currentTheme } = storeToRefs(settingStore)
|
||||
const { getAppTheme } = useSettingGetters()
|
||||
const rayChartRef = ref<HTMLElement>() // echart 容器实例
|
||||
const rayChartWrapperRef = ref<HTMLElement>()
|
||||
const echartInstanceRef = ref<ECharts>() // echart 实例
|
||||
@ -153,7 +152,7 @@ export default defineComponent({
|
||||
|
||||
if (!props.theme) {
|
||||
const theme = props.autoChangeTheme
|
||||
? currentTheme.value
|
||||
? getAppTheme.value
|
||||
? `${echartTheme}-dark`
|
||||
: echartTheme
|
||||
: echartTheme
|
||||
@ -295,7 +294,7 @@ export default defineComponent({
|
||||
// 避免重复渲染
|
||||
if (echartInst?.getDom()) {
|
||||
console.warn(
|
||||
'RChart mount: There is a chart instance already initialized on the dom. Execution was interrupted',
|
||||
'RChart mount: There is a chart instance already initialized on the dom. Execution was interrupted.',
|
||||
)
|
||||
|
||||
return
|
||||
@ -326,7 +325,7 @@ export default defineComponent({
|
||||
|
||||
/** 监听全局主题变化, 然后重新渲染对应主题 echarts */
|
||||
watch(
|
||||
() => currentTheme.value,
|
||||
() => getAppTheme.value,
|
||||
() => {
|
||||
/**
|
||||
*
|
||||
|
@ -11,7 +11,7 @@
|
||||
<KeepAlive
|
||||
v-if="setupKeepAlive"
|
||||
:max="maxKeepAliveLength"
|
||||
:include="keepAliveInclude"
|
||||
:include="getKeepAliveInclude"
|
||||
:exclude="keepAliveExclude"
|
||||
>
|
||||
<Component :is="Component" :key="route.fullPath" />
|
||||
@ -23,7 +23,7 @@
|
||||
</RouterView>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { useKeepAlive } from '@/store'
|
||||
import { useKeepAliveGetters } from '@/store'
|
||||
import { APP_KEEP_ALIVE } from '@/app-config/appConfig'
|
||||
|
||||
import type { TransitionProps } from './type'
|
||||
@ -42,7 +42,6 @@ withDefaults(defineProps<TransitionProps>(), {
|
||||
transitionAppear: true,
|
||||
})
|
||||
|
||||
const keepAliveStore = useKeepAlive()
|
||||
const { keepAliveInclude } = storeToRefs(keepAliveStore)
|
||||
const { getKeepAliveInclude } = useKeepAliveGetters()
|
||||
const { setupKeepAlive, maxKeepAliveLength, keepAliveExclude } = APP_KEEP_ALIVE
|
||||
</script>
|
||||
|
@ -32,6 +32,8 @@ import type { AnyFC } from '@/types/modules/utils'
|
||||
const variableState = reactive({
|
||||
globalSpinning: false,
|
||||
globalDrawerValue: false,
|
||||
globalMainLayoutLoad: true,
|
||||
layoutContentMaximize: false,
|
||||
})
|
||||
|
||||
export type VariableState = typeof variableState
|
||||
|
5
src/hooks/template/index.ts
Normal file
5
src/hooks/template/index.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import { useAppMenu } from './useAppMenu'
|
||||
import { useMainPage } from './useMainPage'
|
||||
import { useMenuTag } from './useMenuTag'
|
||||
|
||||
export { useAppMenu, useMainPage, useMenuTag }
|
82
src/hooks/template/useAppMenu.ts
Normal file
82
src/hooks/template/useAppMenu.ts
Normal file
@ -0,0 +1,82 @@
|
||||
/**
|
||||
*
|
||||
* @author Ray <https://github.com/XiaoDaiGua-Ray>
|
||||
*
|
||||
* @date 2023-11-03
|
||||
*
|
||||
* @workspace ray-template
|
||||
*
|
||||
* @remark 今天也是元气满满撸代码的一天
|
||||
*/
|
||||
|
||||
import { useMenuGetters, useMenuActions } from '@/store'
|
||||
|
||||
import type { AppMenuOption } from '@/types/modules/app'
|
||||
|
||||
export type Target = number | AppMenuOption
|
||||
|
||||
/**
|
||||
*
|
||||
* 导航函数
|
||||
* 但是该方法仅限于在登入后调用
|
||||
*/
|
||||
export function useAppMenu() {
|
||||
const { changeMenuModelValue } = useMenuActions()
|
||||
|
||||
/**
|
||||
*
|
||||
* @param target 需要导航的目标地址
|
||||
*
|
||||
* 当传递参数为 number:
|
||||
* - 当传入参数值超过最大菜单长度,也会跳转失败并且抛出警告
|
||||
* - 如果传递参数需要导航的菜单项为非根菜单项,会自动的递归导航至第一个子菜单项
|
||||
*
|
||||
* 当传递参数为 AppMenuOption 类型,会直接导航至目标页面。该方法可以不区分菜单层级
|
||||
*/
|
||||
const navigationTo = (target: Target) => {
|
||||
if (typeof target === 'number') {
|
||||
// 校验是否为 NaN
|
||||
if (isNaN(target)) {
|
||||
console.warn(`navigationTo: The ${target} is NaN, expect number.`)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
const { getMenuOptions } = useMenuGetters()
|
||||
|
||||
// 校验是否超出最大菜单长度
|
||||
if (target > getMenuOptions.value.length) {
|
||||
console.warn(
|
||||
`navigationTo: The current ${target} exceeds the maximum number of menus.`,
|
||||
)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
const option = getMenuOptions.value[target]
|
||||
|
||||
// 递归获取第一级子菜单
|
||||
const deepNavigation = (routes: AppMenuOption) => {
|
||||
if (routes.children && routes.children.length > 0) {
|
||||
const {
|
||||
children: [firstChild],
|
||||
} = routes
|
||||
|
||||
deepNavigation(firstChild)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
changeMenuModelValue(routes.key, routes)
|
||||
}
|
||||
|
||||
deepNavigation(option)
|
||||
} else {
|
||||
changeMenuModelValue(target.key, target)
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
navigationTo,
|
||||
}
|
||||
}
|
35
src/hooks/template/useMainPage.ts
Normal file
35
src/hooks/template/useMainPage.ts
Normal file
@ -0,0 +1,35 @@
|
||||
/**
|
||||
*
|
||||
* @author Ray <https://github.com/XiaoDaiGua-Ray>
|
||||
*
|
||||
* @date 2023-11-03
|
||||
*
|
||||
* @workspace ray-template
|
||||
*
|
||||
* @remark 今天也是元气满满撸代码的一天
|
||||
*/
|
||||
|
||||
import { getVariableToRefs, setVariable } from '@/global-variable/index'
|
||||
import { LAYOUT_CONTENT_REF } from '@/app-config/routerConfig'
|
||||
import { useFullscreen } from 'vue-hooks-plus'
|
||||
import { useI18n } from '@/hooks/web/index'
|
||||
|
||||
import type { AppMenuOption } from '@/types/modules/app'
|
||||
import type { Ref } from 'vue'
|
||||
|
||||
export function useMainPage() {
|
||||
const reload = (wait = 800) => {
|
||||
setVariable('globalMainLayoutLoad', false)
|
||||
|
||||
setTimeout(() => setVariable('globalMainLayoutLoad', true), wait)
|
||||
}
|
||||
|
||||
const maximize = (full: boolean) => {
|
||||
// setVariable('layoutContentMaximize', full)
|
||||
}
|
||||
|
||||
return {
|
||||
reload,
|
||||
maximize,
|
||||
}
|
||||
}
|
165
src/hooks/template/useMenuTag.ts
Normal file
165
src/hooks/template/useMenuTag.ts
Normal file
@ -0,0 +1,165 @@
|
||||
/**
|
||||
*
|
||||
* @author Ray <https://github.com/XiaoDaiGua-Ray>
|
||||
*
|
||||
* @date 2023-11-03
|
||||
*
|
||||
* @workspace ray-template
|
||||
*
|
||||
* @remark 今天也是元气满满撸代码的一天
|
||||
*/
|
||||
|
||||
import { useMenuGetters, useMenuActions } from '@/store'
|
||||
import { ROOT_ROUTE } from '@/app-config/appConfig'
|
||||
import { redirectRouterToDashboard } from '@/router/helper/routerCopilot'
|
||||
|
||||
import type { MenuTagOptions, Key } from '@/types/modules/app'
|
||||
|
||||
export type CloseMenuTag = Key | MenuTagOptions
|
||||
|
||||
export function useMenuTag() {
|
||||
const { getMenuTagOptions, getMenuKey } = useMenuGetters()
|
||||
const {
|
||||
changeMenuModelValue,
|
||||
spliceMenTagOptions,
|
||||
emptyMenuTagOptions,
|
||||
setMenuTagOptions,
|
||||
} = useMenuActions()
|
||||
const { path } = ROOT_ROUTE
|
||||
|
||||
/**
|
||||
*
|
||||
* 如果为非 root path,直接导航至上一 menuTag
|
||||
*/
|
||||
const navigationPreTagOption = () => {
|
||||
const options = getMenuTagOptions.value
|
||||
const length = options.length
|
||||
const preOption = options[length - 1]
|
||||
|
||||
if (getMenuKey.value !== path) {
|
||||
changeMenuModelValue(preOption.key as string, preOption)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param target 当前关闭项
|
||||
*
|
||||
* 传递参数类型情况:
|
||||
* - number: 关闭当前项索引
|
||||
* - string: 关闭当前项 key
|
||||
* - AppMenuOption: 关闭当前项
|
||||
*/
|
||||
const close = (target: CloseMenuTag) => {
|
||||
if (typeof target === 'number') {
|
||||
if (isNaN(target)) {
|
||||
console.warn(`close: The ${target} is NaN, expect number.`)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if (target > getMenuTagOptions.value.length) {
|
||||
console.warn(
|
||||
`close: The ${target} is greater than menuTagOptions length.`,
|
||||
)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
spliceMenTagOptions(target)
|
||||
navigationPreTagOption()
|
||||
} else if (typeof target === 'string') {
|
||||
const findOptionIndex = getMenuTagOptions.value.findIndex(
|
||||
(curr) => curr.key === target,
|
||||
)
|
||||
|
||||
if (findOptionIndex !== -1) {
|
||||
spliceMenTagOptions(findOptionIndex)
|
||||
navigationPreTagOption()
|
||||
} else {
|
||||
console.warn(
|
||||
`close: The ${target} is not found in current menuTagOptions.`,
|
||||
)
|
||||
|
||||
return
|
||||
}
|
||||
} else {
|
||||
changeMenuModelValue(target.key as string, target)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* 关闭所有标签并且导航至 root path
|
||||
*/
|
||||
const closeAll = () => {
|
||||
emptyMenuTagOptions()
|
||||
redirectRouterToDashboard(true)
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param currentIndex 开始关闭索引
|
||||
*
|
||||
* 关闭以当前项为索引的右侧标签
|
||||
* 如果当前选择标签与 menuKey 不匹配,则会关闭当前标签右侧所有变迁并且跳转至该页面
|
||||
*/
|
||||
const closeRight = (currentIndex: number) => {
|
||||
const spliceLength = getMenuTagOptions.value.length - currentIndex
|
||||
const routeOption = getMenuTagOptions.value[currentIndex]
|
||||
|
||||
if (spliceLength > -1 && routeOption) {
|
||||
spliceMenTagOptions(currentIndex + 1, spliceLength)
|
||||
|
||||
if (getMenuKey.value !== routeOption.key) {
|
||||
changeMenuModelValue(routeOption.key as string, routeOption)
|
||||
}
|
||||
} else {
|
||||
console.warn(
|
||||
`closeRight: The ${currentIndex} is not found in current menuTagOptions.`,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param currentIndex 当前选择项的索引
|
||||
*
|
||||
* 关闭以当前项左侧所有标签
|
||||
* 如果当前选择标签与 menuKey 不匹配,则会关闭当前标签左侧所有变迁并且跳转至该页面
|
||||
*/
|
||||
const closeLeft = (currentIndex: number) => {
|
||||
spliceMenTagOptions(0, currentIndex)
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param currentIndex 当前项索引
|
||||
*
|
||||
* 会关闭除了当前索引的所有菜单项
|
||||
*/
|
||||
const closeOther = (currentIndex: number) => {
|
||||
const routeOption = getMenuTagOptions.value[currentIndex]
|
||||
|
||||
if (routeOption) {
|
||||
if (getMenuKey.value !== routeOption.key) {
|
||||
emptyMenuTagOptions()
|
||||
changeMenuModelValue(routeOption.key, routeOption)
|
||||
} else {
|
||||
setMenuTagOptions(routeOption, false)
|
||||
}
|
||||
} else {
|
||||
console.warn(
|
||||
`closeOther: The ${currentIndex} is not found in current menuTagOptions.`,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
close,
|
||||
closeAll,
|
||||
closeRight,
|
||||
closeLeft,
|
||||
closeOther,
|
||||
}
|
||||
}
|
@ -14,10 +14,10 @@ import './index.scss'
|
||||
import { NMenu, NLayoutSider, NDrawer } from 'naive-ui'
|
||||
import SiderBarLogo from './components/SiderBarLogo/index'
|
||||
|
||||
import { useMenu } from '@/store'
|
||||
import { APP_MENU_CONFIG } from '@/app-config/appConfig'
|
||||
import { useDevice } from '@/hooks/web/index'
|
||||
import { getVariableToRefs, setVariable } from '@/global-variable/index'
|
||||
import { useMenuGetters, useMenuActions } from '@/store'
|
||||
|
||||
import type { MenuInst } from 'naive-ui'
|
||||
import type { NaiveMenuOptions } from '@/types/modules/component'
|
||||
@ -28,16 +28,16 @@ export default defineComponent({
|
||||
setup() {
|
||||
const menuRef = ref<MenuInst | null>(null)
|
||||
|
||||
const menuStore = useMenu()
|
||||
const { changeMenuModelValue, collapsedMenu } = useMenuActions()
|
||||
const { getMenuOptions, getCollapsed, getMenuKey } = useMenuGetters()
|
||||
|
||||
const { changeMenuModelValue, collapsedMenu } = menuStore
|
||||
const modelMenuKey = computed({
|
||||
get: () => {
|
||||
nextTick().then(() => {
|
||||
showMenuOption()
|
||||
})
|
||||
|
||||
return menuStore.menuKey
|
||||
return getMenuKey.value
|
||||
},
|
||||
set: () => {
|
||||
if (isTabletOrSmaller.value) {
|
||||
@ -45,8 +45,6 @@ export default defineComponent({
|
||||
}
|
||||
},
|
||||
})
|
||||
const modelMenuOptions = computed(() => menuStore.options)
|
||||
const modelCollapsed = computed(() => menuStore.collapsed)
|
||||
const { isTabletOrSmaller } = useDevice()
|
||||
const modelGlobalDrawerValue = computed({
|
||||
get: () => getVariableToRefs('globalDrawerValue').value,
|
||||
@ -72,14 +70,14 @@ export default defineComponent({
|
||||
onUpdateCollapsed={collapsedMenu.bind(this)}
|
||||
nativeScrollbar={false}
|
||||
>
|
||||
<SiderBarLogo collapsed={modelCollapsed.value} />
|
||||
<SiderBarLogo collapsed={getCollapsed.value} />
|
||||
<NMenu
|
||||
ref="menuRef"
|
||||
ref={menuRef}
|
||||
class="r-menu--app"
|
||||
v-model:value={modelMenuKey.value}
|
||||
options={modelMenuOptions.value as NaiveMenuOptions[]}
|
||||
options={getMenuOptions.value as NaiveMenuOptions[]}
|
||||
indent={APP_MENU_CONFIG.menuCollapsedIndent}
|
||||
collapsed={modelCollapsed.value}
|
||||
collapsed={getCollapsed.value}
|
||||
collapsedIconSize={APP_MENU_CONFIG.menuCollapsedIconSize}
|
||||
collapsedWidth={APP_MENU_CONFIG.menuCollapsedWidth}
|
||||
onUpdateValue={(key, op) => {
|
||||
@ -91,7 +89,6 @@ export default defineComponent({
|
||||
)
|
||||
|
||||
return {
|
||||
menuRef,
|
||||
isTabletOrSmaller,
|
||||
BasicMenu: BasicMenu,
|
||||
modelGlobalDrawerValue,
|
||||
|
@ -29,15 +29,17 @@ import { NScrollbar, NTag, NSpace, NLayoutHeader, NDropdown } from 'naive-ui'
|
||||
import RIcon from '@/components/RIcon/index'
|
||||
import RMoreDropdown from '@/components/RMoreDropdown/index'
|
||||
|
||||
import { useMenu, useSetting } from '@/store'
|
||||
import { useMenuGetters, useMenuActions } from '@/store'
|
||||
import { uuid } from '@/utils/basic'
|
||||
import { hasClass } from '@/utils/element'
|
||||
import { redirectRouterToDashboard } from '@/router/helper/routerCopilot'
|
||||
import { ROOT_ROUTE } from '@/app-config/appConfig'
|
||||
import { queryElements } from '@use-utils/element'
|
||||
import { renderNode } from '@/utils/vue/index'
|
||||
import { useMainPage } from '@/hooks/template/index'
|
||||
import { useMenuTag } from '@/hooks/template/index'
|
||||
|
||||
import type { MenuOption, ScrollbarInst } from 'naive-ui'
|
||||
import type { ScrollbarInst } from 'naive-ui'
|
||||
import type { MenuTagOptions, AppMenuOption } from '@/types/modules/app'
|
||||
|
||||
export default defineComponent({
|
||||
@ -45,43 +47,28 @@ export default defineComponent({
|
||||
setup(_, { expose }) {
|
||||
const scrollRef = ref<ScrollbarInst | null>(null)
|
||||
|
||||
const menuStore = useMenu()
|
||||
const settingStore = useSetting()
|
||||
|
||||
const { menuKey, menuTagOptions } = storeToRefs(menuStore)
|
||||
const { getMenuKey, getMenuTagOptions } = useMenuGetters()
|
||||
const {
|
||||
changeMenuModelValue,
|
||||
spliceMenTagOptions,
|
||||
emptyMenuTagOptions,
|
||||
setMenuTagOptions,
|
||||
} = menuStore
|
||||
const { changeSwitcher } = settingStore
|
||||
} = useMenuActions()
|
||||
const { path } = ROOT_ROUTE
|
||||
const { reload } = useMainPage()
|
||||
const {
|
||||
close,
|
||||
closeAll: $closeAll,
|
||||
closeRight: $closeRight,
|
||||
closeLeft: $closeLeft,
|
||||
closeOther: $closeOther,
|
||||
} = useMenuTag()
|
||||
|
||||
const exclude = ['closeAll', 'closeRight', 'closeLeft', 'closeOther']
|
||||
let currentContextmenuIndex = -1 // 当前右键标签页索引位置
|
||||
const iconConfig = {
|
||||
size: 16,
|
||||
}
|
||||
const modelMenuTagOptions = computed(() =>
|
||||
menuTagOptions.value.map((curr, _idx, currentArray) => {
|
||||
if (curr.key === menuKey.value && curr.key !== path) {
|
||||
curr.closeable = true
|
||||
} else {
|
||||
curr.closeable = false
|
||||
}
|
||||
|
||||
if (curr.key === path) {
|
||||
curr.closeable = false
|
||||
}
|
||||
|
||||
if (currentArray.length <= 1) {
|
||||
curr.closeable = false
|
||||
}
|
||||
|
||||
return curr
|
||||
}),
|
||||
)
|
||||
const moreOptions = ref([
|
||||
{
|
||||
label: '重新加载',
|
||||
@ -154,58 +141,22 @@ export default defineComponent({
|
||||
disabled: false,
|
||||
},
|
||||
])
|
||||
const scrollBarUUID = uuid(16)
|
||||
const uuidScrollBar = uuid(16)
|
||||
const actionMap = {
|
||||
reloadCurrentPage: () => {
|
||||
changeSwitcher(false, 'reloadRouteSwitch')
|
||||
|
||||
setTimeout(() => changeSwitcher(true, 'reloadRouteSwitch'))
|
||||
reload()
|
||||
},
|
||||
closeAll: () => {
|
||||
/**
|
||||
*
|
||||
* 关闭全部标签页, 然后重定向至首页(dashboard)
|
||||
* 如果做了相关更改, 则需要手动更新
|
||||
*/
|
||||
if (moreOptions.value.length > 1) {
|
||||
emptyMenuTagOptions()
|
||||
redirectRouterToDashboard(true)
|
||||
}
|
||||
$closeAll()
|
||||
},
|
||||
closeRight: () => {
|
||||
/**
|
||||
*
|
||||
* 关闭右侧标签
|
||||
*
|
||||
* 如果当前选择标签与 menuKey 不匹配, 则会关闭当前标签右侧所有变迁并且跳转至该页面
|
||||
*/
|
||||
const length = moreOptions.value.length
|
||||
const routeItem = modelMenuTagOptions.value[currentContextmenuIndex]
|
||||
|
||||
spliceMenTagOptions(currentContextmenuIndex + 1, length - 1)
|
||||
|
||||
if (menuKey.value !== routeItem.key) {
|
||||
changeMenuModelValue(routeItem.key, routeItem)
|
||||
}
|
||||
$closeRight(currentContextmenuIndex)
|
||||
},
|
||||
closeLeft: () => {
|
||||
spliceMenTagOptions(0, currentContextmenuIndex)
|
||||
$closeLeft(currentContextmenuIndex)
|
||||
},
|
||||
closeOther: () => {
|
||||
/**
|
||||
*
|
||||
* 关闭其他标签
|
||||
*
|
||||
* 如果关闭标签与当前 menuKey 不匹配, 则会关闭当前选择标签页以外的所有标签页并且跳转至该页面
|
||||
*/
|
||||
const routeItem = modelMenuTagOptions.value[currentContextmenuIndex]
|
||||
|
||||
if (menuKey.value !== routeItem.key) {
|
||||
emptyMenuTagOptions()
|
||||
changeMenuModelValue(routeItem.key, routeItem)
|
||||
} else {
|
||||
setMenuTagOptions(routeItem, false)
|
||||
}
|
||||
$closeOther(currentContextmenuIndex)
|
||||
},
|
||||
}
|
||||
/** 右键菜单 */
|
||||
@ -223,16 +174,7 @@ export default defineComponent({
|
||||
* @remark 关闭 `tag` 菜单, 如果仅有一个则不能关闭
|
||||
*/
|
||||
const closeCurrentMenuTag = (idx: number) => {
|
||||
spliceMenTagOptions(idx)
|
||||
|
||||
if (menuKey.value !== path) {
|
||||
const options = modelMenuTagOptions.value
|
||||
const length = options.length
|
||||
|
||||
const tag = options[length - 1]
|
||||
|
||||
changeMenuModelValue(tag.key as string, tag)
|
||||
}
|
||||
close(idx)
|
||||
}
|
||||
|
||||
const setMoreOptionsDisabled = (
|
||||
@ -257,7 +199,7 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
const getScrollElement = () => {
|
||||
const scroll = document.getElementById(scrollBarUUID) // 获取滚动条容器
|
||||
const scroll = document.getElementById(uuidScrollBar) // 获取滚动条容器
|
||||
|
||||
if (scroll) {
|
||||
const scrollContentElement = Array.from(
|
||||
@ -320,7 +262,7 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
const setDisabledAccordionToIndex = () => {
|
||||
const length = modelMenuTagOptions.value.length - 1
|
||||
const length = getMenuTagOptions.value.length - 1
|
||||
|
||||
if (currentContextmenuIndex === length) {
|
||||
setMoreOptionsDisabled('closeRight', true)
|
||||
@ -342,8 +284,8 @@ export default defineComponent({
|
||||
* 并且动态设置是否可操作状态
|
||||
*/
|
||||
const setCurrentContextmenuIndex = () => {
|
||||
const index = modelMenuTagOptions.value.findIndex(
|
||||
(curr) => curr.key === menuKey.value,
|
||||
const index = getMenuTagOptions.value.findIndex(
|
||||
(curr) => curr.key === getMenuKey.value,
|
||||
)
|
||||
|
||||
currentContextmenuIndex = index
|
||||
@ -351,16 +293,16 @@ export default defineComponent({
|
||||
setDisabledAccordionToIndex()
|
||||
}
|
||||
|
||||
/** 仅有 modelMenuTagOptions 长度大于 1 并且非 root path 时, 才激活关闭按钮 */
|
||||
/** 仅有 getMenuTagOptions 长度大于 1 并且非 root path 时, 才激活关闭按钮 */
|
||||
const menuTagMouseenter = (option: MenuTagOptions) => {
|
||||
if (modelMenuTagOptions.value.length > 1 && option.key !== path) {
|
||||
if (getMenuTagOptions.value.length > 1 && option.key !== path) {
|
||||
option.closeable = true
|
||||
}
|
||||
}
|
||||
|
||||
/** 移出 MenuTag 时, 判断是否为当前已激活 key */
|
||||
const menuTagMouseleave = (option: MenuTagOptions) => {
|
||||
if (option.key !== menuKey.value) {
|
||||
if (option.key !== getMenuKey.value) {
|
||||
option.closeable = false
|
||||
}
|
||||
}
|
||||
@ -387,7 +329,7 @@ export default defineComponent({
|
||||
const positionMenuTag = () => {
|
||||
nextTick().then(() => {
|
||||
const tags = queryElements<HTMLElement>(
|
||||
`attr:${MENU_TAG_DATA}="${menuKey.value}"`,
|
||||
`attr:${MENU_TAG_DATA}="${getMenuKey.value}"`,
|
||||
)
|
||||
|
||||
if (tags?.length) {
|
||||
@ -402,7 +344,7 @@ export default defineComponent({
|
||||
|
||||
/** 如果有且只有一个标签页时, 禁止全部关闭操作 */
|
||||
watch(
|
||||
() => modelMenuTagOptions.value,
|
||||
() => getMenuTagOptions.value,
|
||||
(newData, oldData) => {
|
||||
moreOptions.value.forEach((curr) => {
|
||||
if (exclude.includes(curr.key)) {
|
||||
@ -436,15 +378,15 @@ export default defineComponent({
|
||||
expose({})
|
||||
|
||||
return {
|
||||
modelMenuTagOptions,
|
||||
getMenuTagOptions,
|
||||
changeMenuModelValue,
|
||||
closeCurrentMenuTag,
|
||||
menuKey,
|
||||
getMenuKey,
|
||||
handleTagClick,
|
||||
moreOptions,
|
||||
scrollX,
|
||||
scrollRef,
|
||||
scrollBarUUID,
|
||||
uuidScrollBar,
|
||||
actionDropdownSelect,
|
||||
rootPath: path,
|
||||
actionState,
|
||||
@ -495,7 +437,7 @@ export default defineComponent({
|
||||
xScrollable
|
||||
ref="scrollRef"
|
||||
{...{
|
||||
id: this.scrollBarUUID,
|
||||
id: this.uuidScrollBar,
|
||||
}}
|
||||
>
|
||||
<NSpace
|
||||
@ -504,13 +446,14 @@ export default defineComponent({
|
||||
align="center"
|
||||
justify="start"
|
||||
>
|
||||
{this.modelMenuTagOptions.map((curr, idx) => (
|
||||
{this.getMenuTagOptions.map((curr, idx) => (
|
||||
<NTag
|
||||
key={curr.key}
|
||||
size="large"
|
||||
strong
|
||||
closable={curr.closeable}
|
||||
onClose={this.closeCurrentMenuTag.bind(this, idx)}
|
||||
type={curr.key === this.menuKey ? 'primary' : 'default'}
|
||||
type={curr.key === this.getMenuKey ? 'primary' : 'default'}
|
||||
bordered={false}
|
||||
{...{
|
||||
onClick: this.handleTagClick.bind(this, curr),
|
||||
@ -520,7 +463,7 @@ export default defineComponent({
|
||||
[this.MENU_TAG_DATA]: curr.path,
|
||||
}}
|
||||
>
|
||||
{renderNode(curr.label)}
|
||||
{renderNode(curr.breadcrumbLabel)}
|
||||
</NTag>
|
||||
))}
|
||||
</NSpace>
|
||||
|
@ -20,24 +20,17 @@
|
||||
|
||||
import { NDropdown, NBreadcrumb, NBreadcrumbItem } from 'naive-ui'
|
||||
|
||||
import { useMenu } from '@/store'
|
||||
import { useMenuGetters, useMenuActions } from '@/store'
|
||||
import { useDevice } from '@/hooks/web/index'
|
||||
|
||||
import type { DropdownOption } from 'naive-ui'
|
||||
import type {
|
||||
AppMenuOption,
|
||||
MenuTagOptions,
|
||||
AppMenuKey,
|
||||
} from '@/types/modules/app'
|
||||
import type { AppMenuOption } from '@/types/modules/app'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'RBreadcrumb',
|
||||
setup() {
|
||||
const menuStore = useMenu()
|
||||
|
||||
const { changeMenuModelValue } = menuStore
|
||||
const { breadcrumbOptions } = storeToRefs(menuStore)
|
||||
const modelBreadcrumbOptions = computed(() => breadcrumbOptions.value)
|
||||
const { changeMenuModelValue } = useMenuActions()
|
||||
const { getBreadcrumbOptions } = useMenuGetters()
|
||||
const { isTabletOrSmaller } = useDevice()
|
||||
|
||||
const dropdownSelect = (key: string | number, option: DropdownOption) => {
|
||||
@ -55,7 +48,7 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
return {
|
||||
modelBreadcrumbOptions,
|
||||
getBreadcrumbOptions,
|
||||
dropdownSelect,
|
||||
breadcrumbItemClick,
|
||||
isTabletOrSmaller,
|
||||
@ -68,7 +61,7 @@ export default defineComponent({
|
||||
<div></div>
|
||||
) : (
|
||||
<NBreadcrumb>
|
||||
{this.modelBreadcrumbOptions.map((curr) => (
|
||||
{this.getBreadcrumbOptions.map((curr) => (
|
||||
<NBreadcrumbItem
|
||||
key={curr.key}
|
||||
onClick={this.breadcrumbItemClick.bind(this, curr)}
|
||||
|
@ -11,20 +11,12 @@
|
||||
|
||||
import './index.scss'
|
||||
|
||||
import {
|
||||
NInput,
|
||||
NModal,
|
||||
NResult,
|
||||
NScrollbar,
|
||||
NSpace,
|
||||
NDivider,
|
||||
NButton,
|
||||
} from 'naive-ui'
|
||||
import { NInput, NModal, NResult, NScrollbar, NSpace } from 'naive-ui'
|
||||
import RIcon from '@/components/RIcon/index'
|
||||
|
||||
import { on, off, queryElements, addClass, removeClass } from '@/utils/element'
|
||||
import { debounce } from 'lodash-es'
|
||||
import { useMenu } from '@/store'
|
||||
import { useMenuGetters, useMenuActions } from '@/store'
|
||||
import { validMenuItemShow } from '@/router/helper/routerCopilot'
|
||||
import { useDevice } from '@/hooks/web/index'
|
||||
|
||||
@ -41,9 +33,7 @@ export default defineComponent({
|
||||
},
|
||||
emits: ['update:show'],
|
||||
setup(props, { emit }) {
|
||||
const menuStore = useMenu()
|
||||
|
||||
const { changeMenuModelValue } = menuStore
|
||||
const { changeMenuModelValue } = useMenuActions()
|
||||
const modelShow = computed({
|
||||
get: () => props.show,
|
||||
set: (val) => {
|
||||
@ -54,7 +44,8 @@ export default defineComponent({
|
||||
}
|
||||
},
|
||||
})
|
||||
const modelMenuOptions = computed(() => menuStore.options)
|
||||
const { getMenuOptions } = useMenuGetters()
|
||||
|
||||
const state = reactive({
|
||||
searchValue: null,
|
||||
searchOptions: [] as AppMenuOption[],
|
||||
@ -131,7 +122,7 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
if (value) {
|
||||
filterArr(modelMenuOptions.value)
|
||||
filterArr(getMenuOptions.value)
|
||||
|
||||
state.searchOptions = arr
|
||||
} else {
|
||||
@ -326,7 +317,7 @@ export default defineComponent({
|
||||
{searchOptions.length ? (
|
||||
<NSpace vertical wrapItem={false} size={[8, 8]}>
|
||||
{searchOptions.map((curr) => (
|
||||
<SearchItem menuOption={curr} />
|
||||
<SearchItem menuOption={curr} key={curr.key} />
|
||||
))}
|
||||
</NSpace>
|
||||
) : (
|
||||
@ -355,7 +346,7 @@ export default defineComponent({
|
||||
size={[24, 8]}
|
||||
>
|
||||
{this.helperTipOptions.map((curr) => (
|
||||
<div class="tip-wrapper-item">
|
||||
<div class="tip-wrapper-item" key={curr.label}>
|
||||
<div class="item-icon">
|
||||
{curr.plain ? (
|
||||
<span>{curr.icon}</span>
|
||||
|
@ -12,14 +12,13 @@
|
||||
import { NSpace, NSwitch, NTooltip } from 'naive-ui'
|
||||
import RIcon from '@/components/RIcon'
|
||||
|
||||
import { useSetting } from '@/store'
|
||||
import { useSettingGetters, useSettingActions } from '@/store'
|
||||
|
||||
const ThemeSwitch = defineComponent({
|
||||
name: 'ThemeSwitch',
|
||||
setup() {
|
||||
const settingStore = useSetting()
|
||||
const { changeSwitcher } = settingStore
|
||||
const { themeValue } = storeToRefs(settingStore)
|
||||
const { changeSwitcher } = useSettingActions()
|
||||
const { getAppTheme } = useSettingGetters()
|
||||
|
||||
const handleRailStyle = ({ checked }: { checked: boolean }) => {
|
||||
return checked
|
||||
@ -33,7 +32,7 @@ const ThemeSwitch = defineComponent({
|
||||
|
||||
return {
|
||||
changeSwitcher,
|
||||
themeValue,
|
||||
getAppTheme,
|
||||
handleRailStyle,
|
||||
}
|
||||
},
|
||||
@ -46,10 +45,10 @@ const ThemeSwitch = defineComponent({
|
||||
{{
|
||||
trigger: () => (
|
||||
<NSwitch
|
||||
v-model:value={this.themeValue}
|
||||
v-model:value={this.getAppTheme}
|
||||
railStyle={this.handleRailStyle.bind(this)}
|
||||
onUpdateValue={(bool: boolean) =>
|
||||
this.changeSwitcher(bool, 'themeValue')
|
||||
this.changeSwitcher(bool, 'appTheme')
|
||||
}
|
||||
>
|
||||
{{
|
||||
@ -75,7 +74,7 @@ const ThemeSwitch = defineComponent({
|
||||
</NSwitch>
|
||||
),
|
||||
default: () =>
|
||||
this.themeValue
|
||||
this.getAppTheme
|
||||
? $t('headerSettingOptions.ThemeOptions.Dark')
|
||||
: $t('headerSettingOptions.ThemeOptions.Light'),
|
||||
}}
|
||||
|
@ -14,7 +14,7 @@ import {
|
||||
import ThemeSwitch from '@/layout/components/SiderBar/components/SettingDrawer/components/ThemeSwitch/index'
|
||||
|
||||
import { APP_THEME } from '@/app-config/designConfig'
|
||||
import { useSetting } from '@/store'
|
||||
import { useSettingGetters, useSettingActions } from '@/store'
|
||||
|
||||
import type { PropType } from 'vue'
|
||||
import type { Placement } from '@/types/modules/component'
|
||||
@ -37,19 +37,17 @@ const SettingDrawer = defineComponent({
|
||||
},
|
||||
emits: ['update:show'],
|
||||
setup(props, { emit }) {
|
||||
const settingStore = useSetting()
|
||||
|
||||
const { changePrimaryColor, changeSwitcher, updateContentTransition } =
|
||||
settingStore
|
||||
useSettingActions()
|
||||
const {
|
||||
themeValue,
|
||||
primaryColorOverride,
|
||||
menuTagSwitch,
|
||||
breadcrumbSwitch,
|
||||
footerSwitch,
|
||||
contentTransition,
|
||||
watermarkSwitch,
|
||||
} = storeToRefs(settingStore)
|
||||
getAppTheme,
|
||||
getPrimaryColorOverride,
|
||||
getMenuTagSwitch,
|
||||
getBreadcrumbSwitch,
|
||||
getCopyrightSwitch,
|
||||
getContentTransition,
|
||||
getWatermarkSwitch,
|
||||
} = useSettingGetters()
|
||||
|
||||
const modelShow = computed({
|
||||
get: () => props.show,
|
||||
@ -79,16 +77,16 @@ const SettingDrawer = defineComponent({
|
||||
return {
|
||||
modelShow,
|
||||
changePrimaryColor,
|
||||
themeValue,
|
||||
primaryColorOverride,
|
||||
menuTagSwitch,
|
||||
getAppTheme,
|
||||
getPrimaryColorOverride,
|
||||
getMenuTagSwitch,
|
||||
changeSwitcher,
|
||||
breadcrumbSwitch,
|
||||
footerSwitch,
|
||||
getBreadcrumbSwitch,
|
||||
getCopyrightSwitch,
|
||||
contentTransitionOptions,
|
||||
contentTransition,
|
||||
getContentTransition,
|
||||
updateContentTransition,
|
||||
watermarkSwitch,
|
||||
getWatermarkSwitch,
|
||||
}
|
||||
},
|
||||
render() {
|
||||
@ -111,14 +109,14 @@ const SettingDrawer = defineComponent({
|
||||
</NDivider>
|
||||
<NColorPicker
|
||||
swatches={APP_THEME.appThemeColors}
|
||||
v-model:value={this.primaryColorOverride.common!.primaryColor}
|
||||
v-model:value={this.getPrimaryColorOverride.common!.primaryColor}
|
||||
onUpdateValue={this.changePrimaryColor.bind(this)}
|
||||
/>
|
||||
<NDivider titlePlacement="center">
|
||||
{$t('headerSettingOptions.ContentTransition')}
|
||||
</NDivider>
|
||||
<NSelect
|
||||
v-model:value={this.contentTransition}
|
||||
v-model:value={this.getContentTransition}
|
||||
options={this.contentTransitionOptions}
|
||||
onUpdateValue={(value) => {
|
||||
this.updateContentTransition(value)
|
||||
@ -130,7 +128,7 @@ const SettingDrawer = defineComponent({
|
||||
<NDescriptions labelPlacement="left" column={1}>
|
||||
<NDescriptionsItem label="多标签">
|
||||
<NSwitch
|
||||
v-model:value={this.menuTagSwitch}
|
||||
v-model:value={this.getMenuTagSwitch}
|
||||
onUpdateValue={(bool: boolean) =>
|
||||
this.changeSwitcher(bool, 'menuTagSwitch')
|
||||
}
|
||||
@ -138,7 +136,7 @@ const SettingDrawer = defineComponent({
|
||||
</NDescriptionsItem>
|
||||
<NDescriptionsItem label="面包屑">
|
||||
<NSwitch
|
||||
v-model:value={this.breadcrumbSwitch}
|
||||
v-model:value={this.getBreadcrumbSwitch}
|
||||
onUpdateValue={(bool: boolean) =>
|
||||
this.changeSwitcher(bool, 'breadcrumbSwitch')
|
||||
}
|
||||
@ -146,7 +144,7 @@ const SettingDrawer = defineComponent({
|
||||
</NDescriptionsItem>
|
||||
<NDescriptionsItem label="水印">
|
||||
<NSwitch
|
||||
v-model:value={this.watermarkSwitch}
|
||||
v-model:value={this.getWatermarkSwitch}
|
||||
onUpdateValue={(bool: boolean) =>
|
||||
this.changeSwitcher(bool, 'watermarkSwitch')
|
||||
}
|
||||
@ -154,9 +152,9 @@ const SettingDrawer = defineComponent({
|
||||
</NDescriptionsItem>
|
||||
<NDescriptionsItem label="版权信息">
|
||||
<NSwitch
|
||||
v-model:value={this.footerSwitch}
|
||||
v-model:value={this.getCopyrightSwitch}
|
||||
onUpdateValue={(bool: boolean) =>
|
||||
this.changeSwitcher(bool, 'footerSwitch')
|
||||
this.changeSwitcher(bool, 'copyrightSwitch')
|
||||
}
|
||||
/>
|
||||
</NDescriptionsItem>
|
||||
|
@ -1,5 +1,9 @@
|
||||
import { useSetting, useSigning } from '@/store'
|
||||
import { useI18n } from '@/hooks/web/index'
|
||||
import {
|
||||
useSigningActions,
|
||||
useSigningGetters,
|
||||
useSettingActions,
|
||||
} from '@/store'
|
||||
|
||||
import type { IconOptionsFC, IconOptions } from './type'
|
||||
|
||||
@ -24,8 +28,7 @@ export const createAvatarOptions = () => [
|
||||
|
||||
const avatarDropdownActionMap = {
|
||||
logout: () => {
|
||||
const signingStore = useSigning()
|
||||
const { logout } = signingStore
|
||||
const { logout } = useSigningActions()
|
||||
|
||||
window.$dialog.warning({
|
||||
title: '提示',
|
||||
@ -38,8 +41,7 @@ const avatarDropdownActionMap = {
|
||||
})
|
||||
},
|
||||
lockScreen: () => {
|
||||
const settingStore = useSetting()
|
||||
const { changeSwitcher } = settingStore
|
||||
const { changeSwitcher } = useSettingActions()
|
||||
|
||||
changeSwitcher(true, 'lockScreenSwitch')
|
||||
},
|
||||
@ -52,7 +54,7 @@ export const avatarDropdownClick = (key: string | number) => {
|
||||
}
|
||||
|
||||
export const createLeftIconOptions = (opts: IconOptionsFC) => {
|
||||
const { isTabletOrSmaller, reloadRouteSwitch } = opts
|
||||
const { isTabletOrSmaller, globalMainLayoutLoad } = opts
|
||||
const { t } = useI18n()
|
||||
|
||||
const notTableOrSmallerOptions: IconOptions[] = [
|
||||
@ -60,7 +62,7 @@ export const createLeftIconOptions = (opts: IconOptionsFC) => {
|
||||
name: 'reload',
|
||||
size: 18,
|
||||
tooltip: t('headerTooltip.Reload'),
|
||||
iconClass: !reloadRouteSwitch.value ? 'ray-icon__reload--loading' : '',
|
||||
iconClass: !globalMainLayoutLoad.value ? 'ray-icon__reload--loading' : '',
|
||||
eventKey: 'reload',
|
||||
},
|
||||
]
|
||||
|
@ -27,7 +27,6 @@ import Breadcrumb from './components/Breadcrumb/index'
|
||||
import GlobalSearch from './components/GlobalSearch/index'
|
||||
import AppAvatar from '@/app-components/app/AppAvatar/index'
|
||||
|
||||
import { useSetting } from '@/store'
|
||||
import { LOCAL_OPTIONS } from '@/app-config/localConfig'
|
||||
import {
|
||||
createAvatarOptions,
|
||||
@ -39,22 +38,22 @@ import { useDevice } from '@/hooks/web/index'
|
||||
import { getVariableToRefs, setVariable } from '@/global-variable/index'
|
||||
import { useFullscreen } from 'vue-hooks-plus'
|
||||
import { useI18n } from '@/hooks/web/index'
|
||||
import { useMainPage } from '@/hooks/template/index'
|
||||
import { useSettingGetters, useSettingActions } from '@/store'
|
||||
|
||||
import type { IconEventMapOptions, IconEventMap } from './type'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'AppSiderBar',
|
||||
setup() {
|
||||
const settingStore = useSetting()
|
||||
|
||||
const { updateLocale, changeSwitcher } = settingStore
|
||||
const { updateLocale, changeSwitcher } = useSettingActions()
|
||||
const { t } = useI18n()
|
||||
const { reload } = useMainPage()
|
||||
|
||||
const [isFullscreen, { toggleFullscreen, isEnabled }] = useFullscreen(
|
||||
document.getElementsByTagName('html')[0],
|
||||
)
|
||||
const { drawerPlacement, breadcrumbSwitch, reloadRouteSwitch } =
|
||||
storeToRefs(settingStore)
|
||||
const { getDrawerPlacement, getBreadcrumbSwitch } = useSettingGetters()
|
||||
const showSettings = ref(false)
|
||||
const spaceItemStyle = {
|
||||
display: 'flex',
|
||||
@ -62,6 +61,7 @@ export default defineComponent({
|
||||
const globalSearchShown = ref(false)
|
||||
const { isTabletOrSmaller } = useDevice()
|
||||
const globalDrawerValue = getVariableToRefs('globalDrawerValue')
|
||||
const globalMainLayoutLoad = getVariableToRefs('globalMainLayoutLoad')
|
||||
|
||||
/**
|
||||
*
|
||||
@ -71,7 +71,7 @@ export default defineComponent({
|
||||
createLeftIconOptions({
|
||||
isFullscreen,
|
||||
isTabletOrSmaller,
|
||||
reloadRouteSwitch,
|
||||
globalMainLayoutLoad,
|
||||
}),
|
||||
)
|
||||
/**
|
||||
@ -82,15 +82,13 @@ export default defineComponent({
|
||||
createRightIconOptions({
|
||||
isFullscreen,
|
||||
isTabletOrSmaller,
|
||||
reloadRouteSwitch,
|
||||
globalMainLayoutLoad,
|
||||
}),
|
||||
)
|
||||
const iconEventMap: IconEventMapOptions = {
|
||||
// 刷新组件重新加载,手动设置 800ms loading 时长
|
||||
reload: () => {
|
||||
changeSwitcher(false, 'reloadRouteSwitch')
|
||||
|
||||
setTimeout(() => changeSwitcher(true, 'reloadRouteSwitch'), 800)
|
||||
reload()
|
||||
},
|
||||
setting: () => {
|
||||
showSettings.value = true
|
||||
@ -127,8 +125,8 @@ export default defineComponent({
|
||||
showSettings,
|
||||
updateLocale,
|
||||
spaceItemStyle,
|
||||
drawerPlacement,
|
||||
breadcrumbSwitch,
|
||||
getDrawerPlacement,
|
||||
getBreadcrumbSwitch,
|
||||
globalSearchShown,
|
||||
}
|
||||
},
|
||||
@ -148,6 +146,7 @@ export default defineComponent({
|
||||
>
|
||||
{this.leftIconOptions.map((curr) => (
|
||||
<TooltipIcon
|
||||
key={curr.name}
|
||||
iconName={curr.name}
|
||||
tooltipText={
|
||||
isRef(curr.tooltip) ? curr.tooltip.value : curr.tooltip
|
||||
@ -156,7 +155,7 @@ export default defineComponent({
|
||||
onClick={this.toolIconClick.bind(this, curr.name)}
|
||||
/>
|
||||
))}
|
||||
{this.breadcrumbSwitch ? <Breadcrumb /> : null}
|
||||
{this.getBreadcrumbSwitch ? <Breadcrumb /> : null}
|
||||
</NSpace>
|
||||
<NSpace
|
||||
align="center"
|
||||
@ -165,6 +164,7 @@ export default defineComponent({
|
||||
>
|
||||
{this.rightTooltipIconOptions.map((curr) => (
|
||||
<TooltipIcon
|
||||
key={curr.name}
|
||||
iconName={curr.name}
|
||||
tooltipText={
|
||||
isRef(curr.tooltip) ? curr.tooltip.value : curr.tooltip
|
||||
@ -198,7 +198,7 @@ export default defineComponent({
|
||||
</NSpace>
|
||||
<SettingDrawer
|
||||
v-model:show={this.showSettings}
|
||||
placement={this.drawerPlacement}
|
||||
placement={this.getDrawerPlacement}
|
||||
/>
|
||||
</NLayoutHeader>
|
||||
)
|
||||
|
@ -26,5 +26,5 @@ export interface IconOptions {
|
||||
export interface IconOptionsFC {
|
||||
isTabletOrSmaller: Ref<boolean>
|
||||
isFullscreen: Ref<boolean>
|
||||
reloadRouteSwitch: Ref<boolean>
|
||||
globalMainLayoutLoad: Ref<boolean>
|
||||
}
|
||||
|
@ -21,21 +21,22 @@ import { NSpin } from 'naive-ui'
|
||||
import RTransitionComponent from '@/components/RTransitionComponent/index.vue'
|
||||
import AppRequestCancelerProvider from '@/app-components/provider/AppRequestCancelerProvider/index'
|
||||
|
||||
import { useSetting } from '@/store'
|
||||
import { getVariableToRefs } from '@/global-variable/index'
|
||||
import { useSettingGetters } from '@/store'
|
||||
|
||||
import type { GlobalThemeOverrides } from 'naive-ui'
|
||||
|
||||
const ContentWrapper = defineComponent({
|
||||
name: 'LayoutContentWrapper',
|
||||
setup() {
|
||||
const settingStore = useSetting()
|
||||
const router = useRouter()
|
||||
|
||||
const { reloadRouteSwitch, contentTransition } = storeToRefs(settingStore)
|
||||
const { getContentTransition } = useSettingGetters()
|
||||
const spinning = ref(false)
|
||||
const themeOverridesSpin: GlobalThemeOverrides['Spin'] = {
|
||||
opacitySpinning: '0',
|
||||
}
|
||||
const globalMainLayoutLoad = getVariableToRefs('globalMainLayoutLoad')
|
||||
|
||||
const setupLayoutContentSpin = () => {
|
||||
router.beforeEach(() => {
|
||||
@ -50,25 +51,27 @@ const ContentWrapper = defineComponent({
|
||||
setupLayoutContentSpin()
|
||||
|
||||
return {
|
||||
reloadRouteSwitch,
|
||||
globalMainLayoutLoad,
|
||||
spinning,
|
||||
themeOverridesSpin,
|
||||
contentTransition,
|
||||
getContentTransition,
|
||||
}
|
||||
},
|
||||
render() {
|
||||
const { globalMainLayoutLoad } = this
|
||||
|
||||
return (
|
||||
<NSpin
|
||||
show={this.spinning || !this.reloadRouteSwitch}
|
||||
show={this.spinning || !globalMainLayoutLoad}
|
||||
description="loading..."
|
||||
size="large"
|
||||
themeOverrides={this.themeOverridesSpin}
|
||||
>
|
||||
<AppRequestCancelerProvider />
|
||||
{this.reloadRouteSwitch ? (
|
||||
{globalMainLayoutLoad ? (
|
||||
<RTransitionComponent
|
||||
class="content-wrapper"
|
||||
transitionPropName={this.contentTransition + '-transform'}
|
||||
transitionPropName={this.getContentTransition + '-transform'}
|
||||
/>
|
||||
) : null}
|
||||
</NSpin>
|
||||
|
@ -9,6 +9,17 @@
|
||||
& .r-layout-full__viewer-content {
|
||||
height: var(--layout-content-height);
|
||||
padding: 16px;
|
||||
transition: all 0.3s;
|
||||
|
||||
&.r-layout-full__viewer-content--maximize {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
transform-origin: center;
|
||||
z-index: 99;
|
||||
}
|
||||
|
||||
& .n-scrollbar-container {
|
||||
height: 100%;
|
||||
|
@ -18,10 +18,11 @@ import FooterWrapper from '@/layout/default/FooterWrapper'
|
||||
import HeaderWrapper from './default/HeaderWrapper'
|
||||
import FeatureWrapper from './default/FeatureWrapper'
|
||||
|
||||
import { useSetting } from '@/store'
|
||||
import { LAYOUT_CONTENT_REF } from '@/app-config/routerConfig'
|
||||
import { layoutHeaderCssVars } from '@/layout/layoutResize'
|
||||
import useAppLockScreen from '@/app-components/app/AppLockScreen/appLockVar'
|
||||
import { getVariableToRefs } from '@/global-variable/index'
|
||||
import { useSettingGetters } from '@/store'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'RLayout',
|
||||
@ -30,45 +31,54 @@ export default defineComponent({
|
||||
const layoutMenuTagRef = ref<HTMLElement>()
|
||||
const layoutFooterRef = ref<HTMLElement>()
|
||||
|
||||
const settingStore = useSetting()
|
||||
|
||||
const { menuTagSwitch: modelMenuTagSwitch, footerSwitch } =
|
||||
storeToRefs(settingStore)
|
||||
const { getMenuTagSwitch, getCopyrightSwitch } = useSettingGetters()
|
||||
const { getLockAppScreen } = useAppLockScreen()
|
||||
const cssVarsRef = layoutHeaderCssVars([
|
||||
layoutSiderBarRef,
|
||||
layoutMenuTagRef,
|
||||
layoutFooterRef,
|
||||
])
|
||||
const layoutContentMaximize = getVariableToRefs('layoutContentMaximize')
|
||||
|
||||
return {
|
||||
modelMenuTagSwitch,
|
||||
getMenuTagSwitch,
|
||||
cssVarsRef,
|
||||
getLockAppScreen,
|
||||
LAYOUT_CONTENT_REF,
|
||||
layoutSiderBarRef,
|
||||
layoutMenuTagRef,
|
||||
layoutFooterRef,
|
||||
footerSwitch,
|
||||
getCopyrightSwitch,
|
||||
layoutContentMaximize,
|
||||
}
|
||||
},
|
||||
render() {
|
||||
return !this.getLockAppScreen() ? (
|
||||
<NLayout class="r-layout-full" style={[this.cssVarsRef]} hasSider>
|
||||
const {
|
||||
layoutContentMaximize,
|
||||
getMenuTagSwitch,
|
||||
cssVarsRef,
|
||||
getCopyrightSwitch,
|
||||
} = this
|
||||
const { getLockAppScreen } = this
|
||||
|
||||
return !getLockAppScreen() ? (
|
||||
<NLayout class="r-layout-full" style={[cssVarsRef]} hasSider>
|
||||
<Menu />
|
||||
<NLayoutContent class="r-layout-full__viewer">
|
||||
<HeaderWrapper ref="layoutSiderBarRef" />
|
||||
{this.modelMenuTagSwitch ? (
|
||||
<FeatureWrapper ref="layoutMenuTagRef" />
|
||||
) : null}
|
||||
{getMenuTagSwitch ? <FeatureWrapper ref="layoutMenuTagRef" /> : null}
|
||||
<NLayoutContent
|
||||
ref="LAYOUT_CONTENT_REF"
|
||||
class="r-layout-full__viewer-content"
|
||||
ref={LAYOUT_CONTENT_REF}
|
||||
class={[
|
||||
'r-layout-full__viewer-content',
|
||||
layoutContentMaximize
|
||||
? 'r-layout-full__viewer-content--maximize'
|
||||
: null,
|
||||
]}
|
||||
nativeScrollbar={false}
|
||||
>
|
||||
<ContentWrapper />
|
||||
</NLayoutContent>
|
||||
{this.footerSwitch ? <FooterWrapper ref="layoutFooterRef" /> : null}
|
||||
{getCopyrightSwitch ? <FooterWrapper ref="layoutFooterRef" /> : null}
|
||||
</NLayoutContent>
|
||||
</NLayout>
|
||||
) : null
|
||||
|
@ -20,5 +20,6 @@
|
||||
"RouterDemo": "Same Level Router Demo",
|
||||
"Mock": "Mock",
|
||||
"QRCode": "QRCode",
|
||||
"SvgIcon": "SVG Icon"
|
||||
"SvgIcon": "SVG Icon",
|
||||
"TemplateHooks": "Template Api"
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"Register": "Register",
|
||||
"Signin": "Signin",
|
||||
"QRCodeSignin": "QRCode Signin",
|
||||
"Signing": "Signing",
|
||||
"QRCodeSigning": "QRCode Signing",
|
||||
"NamePlaceholder": "please enter user name",
|
||||
"PasswordPlaceholder": "please enter password",
|
||||
"Login": "Login",
|
||||
|
@ -18,7 +18,8 @@
|
||||
"CalculatePrecision": "数字精度",
|
||||
"Directive": "指令",
|
||||
"RouterDemo": "页面详情模式",
|
||||
"Mock": "mock 数据",
|
||||
"Mock": "Mock 数据",
|
||||
"QRCode": "二维码",
|
||||
"SvgIcon": "SVG图标"
|
||||
"SvgIcon": "SVG 图标",
|
||||
"TemplateHooks": "模板内置 Api"
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"Register": "注册",
|
||||
"Signin": "登录",
|
||||
"QRCodeSignin": "扫码登陆",
|
||||
"Signing": "登录",
|
||||
"QRCodeSigning": "扫码登陆",
|
||||
"NamePlaceholder": "请输入用户名",
|
||||
"PasswordPlaceholder": "请输入密码",
|
||||
"Login": "登 陆",
|
||||
|
@ -11,11 +11,11 @@
|
||||
|
||||
import { permissionRouter } from './permission'
|
||||
import { SETUP_ROUTER_ACTION, SUPER_ADMIN } from '@/app-config/routerConfig'
|
||||
import { useSigning } from '@/store'
|
||||
import { useVueRouter } from '@/hooks/web/index'
|
||||
import { ROOT_ROUTE } from '@/app-config/appConfig'
|
||||
import { setStorage } from '@/utils/cache'
|
||||
import { getAppEnvironment } from '@/utils/basic'
|
||||
import { useSigningGetters } from '@/store'
|
||||
|
||||
import type { Router } from 'vue-router'
|
||||
import type { AppRouteMeta } from '@/router/type'
|
||||
@ -29,11 +29,13 @@ import type { AppMenuOption } from '@/types/modules/app'
|
||||
* 如果为超级管理员, 则会默认获取所有权限
|
||||
*/
|
||||
export const validRole = (meta: AppRouteMeta) => {
|
||||
const { signingCallback } = storeToRefs(useSigning())
|
||||
const modelRole = computed(() => signingCallback.value.role)
|
||||
const { getSigningCallback } = useSigningGetters()
|
||||
const { role: metaRole } = meta
|
||||
|
||||
if (SUPER_ADMIN?.length && SUPER_ADMIN.includes(modelRole.value)) {
|
||||
if (
|
||||
SUPER_ADMIN?.length &&
|
||||
SUPER_ADMIN.includes(getSigningCallback.value.role)
|
||||
) {
|
||||
return true
|
||||
} else {
|
||||
// 如果 role 为 undefined 或者空数组, 则认为该路由不做权限过滤
|
||||
@ -43,7 +45,7 @@ export const validRole = (meta: AppRouteMeta) => {
|
||||
|
||||
// 判断是否含有该权限
|
||||
if (metaRole) {
|
||||
return metaRole.includes(modelRole.value)
|
||||
return metaRole.includes(getSigningCallback.value.role)
|
||||
}
|
||||
|
||||
return true
|
||||
|
17
src/router/modules/demo/template-hooks.ts
Normal file
17
src/router/modules/demo/template-hooks.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import { t } from '@/hooks/web/index'
|
||||
import { LAYOUT } from '@/router/constant/index'
|
||||
|
||||
import type { AppRouteRecordRaw } from '@/router/type'
|
||||
|
||||
const axios: AppRouteRecordRaw = {
|
||||
path: '/template-hooks',
|
||||
name: 'TemplateHooks',
|
||||
component: () => import('@/views/demo/template-hooks/index'),
|
||||
meta: {
|
||||
i18nKey: t('menu.TemplateHooks'),
|
||||
icon: 'other',
|
||||
order: 1,
|
||||
},
|
||||
}
|
||||
|
||||
export default axios
|
35
src/store/hooks/useKeepAliveStore.ts
Normal file
35
src/store/hooks/useKeepAliveStore.ts
Normal file
@ -0,0 +1,35 @@
|
||||
/**
|
||||
*
|
||||
* @author Ray <https://github.com/XiaoDaiGua-Ray>
|
||||
*
|
||||
* @date 2023-11-06
|
||||
*
|
||||
* @workspace ray-template
|
||||
*
|
||||
* @remark 今天也是元气满满撸代码的一天
|
||||
*/
|
||||
|
||||
import { piniaKeepAliveStore } from '../index'
|
||||
|
||||
export const useKeepAliveGetters = () => {
|
||||
const variable = piniaKeepAliveStore()
|
||||
|
||||
/**
|
||||
*
|
||||
* @remark 获取当前可缓存项 name
|
||||
*/
|
||||
const getKeepAliveInclude = computed(() => variable.keepAliveInclude)
|
||||
|
||||
return {
|
||||
getKeepAliveInclude,
|
||||
}
|
||||
}
|
||||
|
||||
export const useKeepAliveActions = () => {
|
||||
const { setKeepAliveInclude, getKeepAliveInclude } = piniaKeepAliveStore()
|
||||
|
||||
return {
|
||||
setKeepAliveInclude,
|
||||
getKeepAliveInclude,
|
||||
}
|
||||
}
|
97
src/store/hooks/useMenuStore.ts
Normal file
97
src/store/hooks/useMenuStore.ts
Normal file
@ -0,0 +1,97 @@
|
||||
/**
|
||||
*
|
||||
* @author Ray <https://github.com/XiaoDaiGua-Ray>
|
||||
*
|
||||
* @date 2023-11-05
|
||||
*
|
||||
* @workspace ray-template
|
||||
*
|
||||
* @remark 今天也是元气满满撸代码的一天
|
||||
*/
|
||||
|
||||
import { piniaMenuStore } from '../modules/menu'
|
||||
import { ROOT_ROUTE } from '@/app-config/appConfig'
|
||||
|
||||
export const useMenuGetters = () => {
|
||||
const variable = piniaMenuStore()
|
||||
|
||||
/**
|
||||
*
|
||||
* @remark 获取菜单列表
|
||||
*/
|
||||
const getMenuOptions = computed(() => variable.options)
|
||||
/**
|
||||
*
|
||||
* @remark 获取面包屑列表
|
||||
*/
|
||||
const getBreadcrumbOptions = computed(() => variable.breadcrumbOptions)
|
||||
/**
|
||||
*
|
||||
* @remark 获取菜单当前 key
|
||||
*/
|
||||
const getMenuKey = computed(() => variable.menuKey)
|
||||
/**
|
||||
*
|
||||
* @remark 获取菜单标签列表
|
||||
*/
|
||||
const getMenuTagOptions = computed(() => {
|
||||
const { path } = ROOT_ROUTE
|
||||
|
||||
return variable.menuTagOptions.map((curr, _idx, currentArray) => {
|
||||
if (curr.key === getMenuKey.value && curr.key !== path) {
|
||||
curr.closeable = true
|
||||
} else {
|
||||
curr.closeable = false
|
||||
}
|
||||
|
||||
if (curr.key === path) {
|
||||
curr.closeable = false
|
||||
}
|
||||
|
||||
if (currentArray.length <= 1) {
|
||||
curr.closeable = false
|
||||
}
|
||||
|
||||
return curr
|
||||
})
|
||||
})
|
||||
/**
|
||||
*
|
||||
* @remark 获取当前菜单项
|
||||
*/
|
||||
const getCurrentMenuOption = computed(() => variable.currentMenuOption)
|
||||
/**
|
||||
*
|
||||
* @remark 获取是否折叠
|
||||
*/
|
||||
const getCollapsed = computed(() => variable.collapsed)
|
||||
|
||||
return {
|
||||
getMenuOptions,
|
||||
getBreadcrumbOptions,
|
||||
getMenuKey,
|
||||
getMenuTagOptions,
|
||||
getCurrentMenuOption,
|
||||
getCollapsed,
|
||||
}
|
||||
}
|
||||
|
||||
export const useMenuActions = () => {
|
||||
const {
|
||||
changeMenuModelValue,
|
||||
setupAppMenu,
|
||||
collapsedMenu,
|
||||
spliceMenTagOptions,
|
||||
emptyMenuTagOptions,
|
||||
setMenuTagOptions,
|
||||
} = piniaMenuStore()
|
||||
|
||||
return {
|
||||
changeMenuModelValue,
|
||||
setupAppMenu,
|
||||
collapsedMenu,
|
||||
spliceMenTagOptions,
|
||||
emptyMenuTagOptions,
|
||||
setMenuTagOptions,
|
||||
}
|
||||
}
|
96
src/store/hooks/useSettingStore.ts
Normal file
96
src/store/hooks/useSettingStore.ts
Normal file
@ -0,0 +1,96 @@
|
||||
/**
|
||||
*
|
||||
* @author Ray <https://github.com/XiaoDaiGua-Ray>
|
||||
*
|
||||
* @date 2023-11-06
|
||||
*
|
||||
* @workspace ray-template
|
||||
*
|
||||
* @remark 今天也是元气满满撸代码的一天
|
||||
*/
|
||||
|
||||
import { piniaSettingStore } from '../modules/setting'
|
||||
|
||||
export const useSettingGetters = () => {
|
||||
const variable = piniaSettingStore()
|
||||
|
||||
/**
|
||||
*
|
||||
* @remark 获取设置抽屉方向
|
||||
*/
|
||||
const getDrawerPlacement = computed(() => variable.drawerPlacement)
|
||||
/**
|
||||
*
|
||||
* @remark 获取 Naive UI 覆盖配置
|
||||
*/
|
||||
const getPrimaryColorOverride = computed(() => variable.primaryColorOverride)
|
||||
/**
|
||||
*
|
||||
* @remark 获取 app 主题
|
||||
*/
|
||||
const getAppTheme = computed(() => variable.appTheme)
|
||||
/**
|
||||
*
|
||||
* @remark 获取菜单标签开关
|
||||
*/
|
||||
const getMenuTagSwitch = computed(() => variable.menuTagSwitch)
|
||||
/**
|
||||
*
|
||||
* @remark 获取面包屑开关
|
||||
*/
|
||||
const getBreadcrumbSwitch = computed(() => variable.breadcrumbSwitch)
|
||||
/**
|
||||
*
|
||||
* @remark 获取 app 当前语言
|
||||
*/
|
||||
const getLocaleLanguage = computed(() => variable.localeLanguage)
|
||||
/**
|
||||
*
|
||||
* @remark 获取锁屏开关
|
||||
*/
|
||||
const getLockScreenSwitch = computed(() => variable.lockScreenSwitch)
|
||||
/**
|
||||
*
|
||||
* @remark 获取版权信息开关
|
||||
*/
|
||||
const getCopyrightSwitch = computed(() => variable.copyrightSwitch)
|
||||
/**
|
||||
*
|
||||
* @remark 获取 app 内容区域过渡动画效果
|
||||
*/
|
||||
const getContentTransition = computed(() => variable.contentTransition)
|
||||
/**
|
||||
*
|
||||
* @remark 获取水印开关
|
||||
*/
|
||||
const getWatermarkSwitch = computed(() => variable.watermarkSwitch)
|
||||
|
||||
return {
|
||||
getDrawerPlacement,
|
||||
getPrimaryColorOverride,
|
||||
getAppTheme,
|
||||
getMenuTagSwitch,
|
||||
getBreadcrumbSwitch,
|
||||
getLocaleLanguage,
|
||||
getLockScreenSwitch,
|
||||
getCopyrightSwitch,
|
||||
getContentTransition,
|
||||
getWatermarkSwitch,
|
||||
}
|
||||
}
|
||||
|
||||
export const useSettingActions = () => {
|
||||
const {
|
||||
updateLocale,
|
||||
changePrimaryColor,
|
||||
changeSwitcher,
|
||||
updateContentTransition,
|
||||
} = piniaSettingStore()
|
||||
|
||||
return {
|
||||
updateLocale,
|
||||
changePrimaryColor,
|
||||
changeSwitcher,
|
||||
updateContentTransition,
|
||||
}
|
||||
}
|
35
src/store/hooks/useSigningStore.ts
Normal file
35
src/store/hooks/useSigningStore.ts
Normal file
@ -0,0 +1,35 @@
|
||||
/**
|
||||
*
|
||||
* @author Ray <https://github.com/XiaoDaiGua-Ray>
|
||||
*
|
||||
* @date 2023-11-06
|
||||
*
|
||||
* @workspace ray-template
|
||||
*
|
||||
* @remark 今天也是元气满满撸代码的一天
|
||||
*/
|
||||
|
||||
import { piniaSigningStore } from '../index'
|
||||
|
||||
export const useSigningGetters = () => {
|
||||
const variable = piniaSigningStore()
|
||||
|
||||
/**
|
||||
*
|
||||
* @remark 获取登陆返回信息
|
||||
*/
|
||||
const getSigningCallback = computed(() => variable.signingCallback)
|
||||
|
||||
return {
|
||||
getSigningCallback,
|
||||
}
|
||||
}
|
||||
|
||||
export const useSigningActions = () => {
|
||||
const { signing, logout } = piniaSigningStore()
|
||||
|
||||
return {
|
||||
signing,
|
||||
logout,
|
||||
}
|
||||
}
|
@ -18,10 +18,20 @@
|
||||
*/
|
||||
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
|
||||
|
||||
export { useSetting } from './modules/setting/index' // import { useSetting } from '@/store' 即可使用
|
||||
export { useMenu } from './modules/menu/index'
|
||||
export { useSigning } from './modules/signing/index'
|
||||
export { useKeepAlive } from './modules/keep-alive/index'
|
||||
// 导出仓库实例
|
||||
export { piniaSettingStore } from './modules/setting/index' // import { piniaSettingStore } from '@/store' 即可使用
|
||||
export { piniaMenuStore } from './modules/menu/index'
|
||||
export { piniaSigningStore } from './modules/signing/index'
|
||||
export { piniaKeepAliveStore } from './modules/keep-alive/index'
|
||||
|
||||
// 导出 getters, actions
|
||||
export { useMenuGetters, useMenuActions } from './hooks/useMenuStore'
|
||||
export { useSettingGetters, useSettingActions } from './hooks/useSettingStore'
|
||||
export { useSigningGetters, useSigningActions } from './hooks/useSigningStore'
|
||||
export {
|
||||
useKeepAliveGetters,
|
||||
useKeepAliveActions,
|
||||
} from './hooks/useKeepAliveStore'
|
||||
|
||||
import type { App } from 'vue'
|
||||
|
||||
|
@ -23,7 +23,7 @@ import { APP_KEEP_ALIVE } from '@/app-config/appConfig'
|
||||
import type { KeepAliveStoreState } from './type'
|
||||
import type { AppMenuOption } from '@/types/modules/app'
|
||||
|
||||
export const useKeepAlive = defineStore(
|
||||
export const piniaKeepAliveStore = defineStore(
|
||||
'keepAlive',
|
||||
() => {
|
||||
const { maxKeepAliveLength } = APP_KEEP_ALIVE
|
||||
|
@ -20,6 +20,7 @@
|
||||
* 缓存(sessionStorage):
|
||||
* - breadcrumbOptions
|
||||
* - menuKey
|
||||
* - menuTagOptions
|
||||
*/
|
||||
|
||||
import { NEllipsis } from 'naive-ui'
|
||||
@ -34,9 +35,9 @@ import {
|
||||
} from './helper'
|
||||
import { useI18n } from '@/hooks/web/index'
|
||||
import { getAppRawRoutes } from '@/router/appRouteModules'
|
||||
import { useKeepAlive } from '@/store'
|
||||
import { useVueRouter } from '@/hooks/web/index'
|
||||
import { throttle } from 'lodash-es'
|
||||
import { useKeepAliveActions } from '@/store'
|
||||
|
||||
import type { AppRouteMeta, AppRouteRecordRaw } from '@/router/type'
|
||||
import type {
|
||||
@ -46,13 +47,13 @@ import type {
|
||||
} from '@/types/modules/app'
|
||||
import type { MenuState } from '@/store/modules/menu/type'
|
||||
|
||||
export const useMenu = defineStore(
|
||||
export const piniaMenuStore = defineStore(
|
||||
'menu',
|
||||
() => {
|
||||
const { router } = useVueRouter()
|
||||
const route = useRoute()
|
||||
const { t } = useI18n()
|
||||
const { setKeepAliveInclude } = useKeepAlive()
|
||||
const { setKeepAliveInclude } = useKeepAliveActions()
|
||||
|
||||
const menuState = reactive<MenuState>({
|
||||
menuKey: getCatchMenuKey(), // 当前菜单 `key`
|
||||
@ -60,6 +61,7 @@ export const useMenu = defineStore(
|
||||
collapsed: false, // 是否折叠菜单
|
||||
menuTagOptions: [], // tag 标签菜单
|
||||
breadcrumbOptions: [], // 面包屑菜单
|
||||
currentMenuOption: null, // 当前激活菜单项
|
||||
})
|
||||
const isSetupAppMenuLock = ref(true)
|
||||
|
||||
@ -191,6 +193,8 @@ export const useMenu = defineStore(
|
||||
} else {
|
||||
setBreadcrumbOptions(menuState.menuKey || '', option)
|
||||
}
|
||||
|
||||
menuState.currentMenuOption = option
|
||||
}
|
||||
}
|
||||
|
||||
@ -367,7 +371,7 @@ export const useMenu = defineStore(
|
||||
persist: {
|
||||
key: 'piniaMenuStore',
|
||||
storage: window.sessionStorage,
|
||||
paths: ['breadcrumbOptions', 'menuKey'],
|
||||
paths: ['breadcrumbOptions', 'menuKey', 'menuTagOptions'],
|
||||
},
|
||||
},
|
||||
)
|
||||
|
@ -10,4 +10,5 @@ export interface MenuState {
|
||||
collapsed: boolean
|
||||
menuTagOptions: MenuTagOptions[]
|
||||
breadcrumbOptions: AppMenuOption[]
|
||||
currentMenuOption: AppMenuOption | null
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ import type { ConditionalPick } from '@/types/modules/helper'
|
||||
import type { SettingState } from '@/store/modules/setting/type'
|
||||
import type { DayjsLocal } from '@/dayjs/type'
|
||||
|
||||
export const useSetting = defineStore(
|
||||
export const piniaSettingStore = defineStore(
|
||||
'setting',
|
||||
() => {
|
||||
const {
|
||||
@ -28,15 +28,12 @@ export const useSetting = defineStore(
|
||||
primaryColorHover: primaryColor,
|
||||
},
|
||||
},
|
||||
themeValue: false, // `true` 为黑夜主题, `false` 为白色主题
|
||||
reloadRouteSwitch: true, // 刷新路由开关
|
||||
appTheme: false, // `true` 为黑夜主题, `false` 为白色主题
|
||||
menuTagSwitch: true, // 多标签页开关
|
||||
spinSwitch: false, // 全屏加载
|
||||
breadcrumbSwitch: true, // 面包屑开关
|
||||
localeLanguage: getAppDefaultLanguage(),
|
||||
lockScreenSwitch: false, // 锁屏开关
|
||||
lockScreenInputSwitch: false, // 锁屏输入状态开关(预留该字段是为了方便拓展用, 但是舍弃了该字段, 改为使用 useAppLockScreen 方法)
|
||||
footerSwitch: true, // 底部区域开关
|
||||
copyrightSwitch: true, // 底部区域开关
|
||||
contentTransition: 'scale', // 切换过渡效果
|
||||
watermarkSwitch: false, // 水印开关,
|
||||
})
|
||||
|
@ -4,15 +4,12 @@ import type { Placement } from '@/types/modules/component'
|
||||
export interface SettingState {
|
||||
drawerPlacement: Placement
|
||||
primaryColorOverride: GlobalThemeOverrides
|
||||
themeValue: boolean
|
||||
reloadRouteSwitch: boolean
|
||||
appTheme: boolean
|
||||
menuTagSwitch: boolean
|
||||
spinSwitch: boolean
|
||||
breadcrumbSwitch: boolean
|
||||
localeLanguage: string
|
||||
lockScreenSwitch: boolean
|
||||
lockScreenInputSwitch: boolean
|
||||
watermarkSwitch: boolean
|
||||
footerSwitch: boolean
|
||||
copyrightSwitch: boolean
|
||||
contentTransition: string
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ import type {
|
||||
SigningResponse,
|
||||
} from '@/store/modules/signing/type'
|
||||
|
||||
export const useSigning = defineStore(
|
||||
export const piniaSigningStore = defineStore(
|
||||
'signing',
|
||||
() => {
|
||||
const state = reactive({
|
||||
|
@ -43,6 +43,7 @@ const PreviewSVGIcons = defineComponent({
|
||||
<div
|
||||
class="pre-view-icons__card"
|
||||
v-copy={`<RIcon name="${curr}" size="56" />`}
|
||||
key={curr}
|
||||
>
|
||||
<NPopover>
|
||||
{{
|
||||
|
76
src/views/demo/template-hooks/index.tsx
Normal file
76
src/views/demo/template-hooks/index.tsx
Normal file
@ -0,0 +1,76 @@
|
||||
/**
|
||||
*
|
||||
* @author Ray <https://github.com/XiaoDaiGua-Ray>
|
||||
*
|
||||
* @date 2023-11-03
|
||||
*
|
||||
* @workspace ray-template
|
||||
*
|
||||
* @remark 今天也是元气满满撸代码的一天
|
||||
*/
|
||||
|
||||
import { NSpace, NCard, NButton } from 'naive-ui'
|
||||
|
||||
import { useAppMenu, useMainPage } from '@/hooks/template/index'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'TemplateHooks',
|
||||
setup() {
|
||||
const currentMenuOption = ref('')
|
||||
const maximizeRef = ref(false)
|
||||
|
||||
const { navigationTo } = useAppMenu()
|
||||
const { reload, maximize } = useMainPage()
|
||||
|
||||
return {
|
||||
navigationTo,
|
||||
reload,
|
||||
currentMenuOption,
|
||||
maximize,
|
||||
maximizeRef,
|
||||
}
|
||||
},
|
||||
render() {
|
||||
const { navigationTo, reload, maximize } = this
|
||||
|
||||
return (
|
||||
<NSpace wrapItem={false} vertical>
|
||||
<NCard title="useAppMenu 导航方法">
|
||||
<h3>
|
||||
navigationTo
|
||||
参数为正整数时,会更具当前的菜单顺序进行自动导航匹配。但是此方法仅能导航一级菜单。并且如果导航菜单非根菜单项,会自动递归导航至一子菜单。
|
||||
</h3>
|
||||
<br />
|
||||
<NButton onClick={() => navigationTo(14)}>跳转至多级菜单</NButton>
|
||||
</NCard>
|
||||
<NCard title="useMainPage 主页面方法">
|
||||
<NCard title="reload 加载函数">
|
||||
<h3>
|
||||
手动刷新内容区域,会使得当前路由页面内容强制重新加载(会执行完整的
|
||||
vue 生命周期)。默认 800ms 延迟。
|
||||
</h3>
|
||||
<br />
|
||||
<NButton
|
||||
onClick={() => {
|
||||
reload()
|
||||
}}
|
||||
>
|
||||
刷新
|
||||
</NButton>
|
||||
</NCard>
|
||||
<NCard title="maximize 内容区域最大化">
|
||||
<NButton
|
||||
onClick={() => {
|
||||
this.maximizeRef = !this.maximizeRef
|
||||
|
||||
maximize(this.maximizeRef)
|
||||
}}
|
||||
>
|
||||
最大化内容区域
|
||||
</NButton>
|
||||
</NCard>
|
||||
</NCard>
|
||||
</NSpace>
|
||||
)
|
||||
},
|
||||
})
|
@ -1,4 +1,4 @@
|
||||
.qrcode-signin {
|
||||
.qrcode-signing {
|
||||
width: 100%;
|
||||
height: 220px;
|
||||
@include flexCenter;
|
@ -22,8 +22,8 @@ import LOGO from '@/assets/images/ray.svg'
|
||||
* 可以根据业务需求自行更改
|
||||
*/
|
||||
|
||||
const QRCodeSignin = defineComponent({
|
||||
name: 'QRCodeSignin',
|
||||
const QRCodeSigning = defineComponent({
|
||||
name: 'QRCodeSigning',
|
||||
setup() {
|
||||
const qrcodeState = reactive({
|
||||
qrcodeValue: 'https://github.com/XiaoDaiGua-Ray/xiaodaigua-ray.github.io',
|
||||
@ -35,11 +35,11 @@ const QRCodeSignin = defineComponent({
|
||||
},
|
||||
render() {
|
||||
return (
|
||||
<div class="qrcode-signin">
|
||||
<div class="qrcode-signing">
|
||||
<RayQRcode text="ray template yes" size={200} logoImage={LOGO} />
|
||||
</div>
|
||||
)
|
||||
},
|
||||
})
|
||||
|
||||
export default QRCodeSignin
|
||||
export default QRCodeSigning
|
@ -1,5 +1,5 @@
|
||||
.ray-template--light {
|
||||
& .sso-signin {
|
||||
& .sso-signing {
|
||||
color: #878787;
|
||||
}
|
||||
}
|
@ -21,16 +21,16 @@ import './index.scss'
|
||||
import { NSpace, NPopover } from 'naive-ui'
|
||||
import RIcon from '@/components/RIcon/index'
|
||||
|
||||
interface SSOSigninOptions {
|
||||
interface SSOSigningOptions {
|
||||
icon: string
|
||||
key: string
|
||||
tooltipLabel: string
|
||||
}
|
||||
|
||||
const SSOSignin = defineComponent({
|
||||
name: 'SSOSignin',
|
||||
const SSOSigning = defineComponent({
|
||||
name: 'SSOSigning',
|
||||
setup() {
|
||||
const ssoSigninOptions = [
|
||||
const ssoSigningOptions = [
|
||||
{
|
||||
icon: 'github',
|
||||
key: 'github',
|
||||
@ -48,27 +48,31 @@ const SSOSignin = defineComponent({
|
||||
},
|
||||
]
|
||||
|
||||
const handleSSOSigninClick = (option: SSOSigninOptions) => {
|
||||
const handleSSOSigningClick = (option: SSOSigningOptions) => {
|
||||
window.$message.info(`调用${option.tooltipLabel}`)
|
||||
}
|
||||
|
||||
return {
|
||||
ssoSigninOptions,
|
||||
handleSSOSigninClick,
|
||||
ssoSigningOptions,
|
||||
handleSSOSigningClick,
|
||||
}
|
||||
},
|
||||
render() {
|
||||
return (
|
||||
<NSpace class="sso-signin" align="center" itemStyle={{ display: 'flex' }}>
|
||||
{this.ssoSigninOptions.map((curr) => (
|
||||
<NPopover>
|
||||
<NSpace
|
||||
class="sso-signing"
|
||||
align="center"
|
||||
itemStyle={{ display: 'flex' }}
|
||||
>
|
||||
{this.ssoSigningOptions.map((curr) => (
|
||||
<NPopover key={curr.key}>
|
||||
{{
|
||||
trigger: () => (
|
||||
<RIcon
|
||||
name={curr.icon}
|
||||
size="24"
|
||||
cursor="pointer"
|
||||
onClick={this.handleSSOSigninClick.bind(this, curr)}
|
||||
onClick={this.handleSSOSigningClick.bind(this, curr)}
|
||||
/>
|
||||
),
|
||||
default: () => curr.tooltipLabel,
|
||||
@ -80,4 +84,4 @@ const SSOSignin = defineComponent({
|
||||
},
|
||||
})
|
||||
|
||||
export default SSOSignin
|
||||
export default SSOSigning
|
@ -1,23 +1,21 @@
|
||||
import { NForm, NFormItem, NInput, NButton } from 'naive-ui'
|
||||
|
||||
import { setStorage } from '@/utils/cache'
|
||||
import { useSigning } from '@/store'
|
||||
import { useI18n } from '@/hooks/web/index'
|
||||
import { APP_CATCH_KEY, ROOT_ROUTE } from '@/app-config/appConfig'
|
||||
import { useVueRouter } from '@/hooks/web/index'
|
||||
import { setVariable, getVariableToRefs } from '@/global-variable/index'
|
||||
import { useSigningActions, useSigningGetters } from '@/store'
|
||||
|
||||
import type { FormInst } from 'naive-ui'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'RSignin',
|
||||
name: 'RSigning',
|
||||
setup() {
|
||||
const loginFormRef = ref<FormInst>()
|
||||
|
||||
const { t } = useI18n()
|
||||
const signingStore = useSigning()
|
||||
|
||||
const { signing } = signingStore
|
||||
const { signing } = useSigningActions()
|
||||
const { path } = ROOT_ROUTE
|
||||
const globalSpinning = getVariableToRefs('globalSpinning')
|
||||
|
@ -11,17 +11,17 @@ import {
|
||||
NGrid,
|
||||
NGridItem,
|
||||
} from 'naive-ui'
|
||||
import Signin from './components/Signin/index'
|
||||
import Signing from './components/Signing/index'
|
||||
import Register from './components/Register/index'
|
||||
import QRCodeSignin from './components/QRCodeSignin/index'
|
||||
import SSOSignin from './components/SSOSignin/index'
|
||||
import QRCodeSigning from './components/QRCodeSigning/index'
|
||||
import SSOSigning from './components/SSOSigning/index'
|
||||
import RIcon from '@/components/RIcon'
|
||||
import RayLink from '@/app-components/app/RayLink/index'
|
||||
import ThemeSwitch from '@/layout/components/SiderBar/components/SettingDrawer/components/ThemeSwitch/index'
|
||||
|
||||
import { useSetting } from '@/store'
|
||||
import { LOCAL_OPTIONS } from '@/app-config/localConfig'
|
||||
import { useWindowSize } from '@vueuse/core'
|
||||
import { useSettingActions } from '@/store'
|
||||
|
||||
const Login = defineComponent({
|
||||
name: 'RLogin',
|
||||
@ -31,12 +31,11 @@ const Login = defineComponent({
|
||||
} = __APP_CFG__
|
||||
|
||||
const state = reactive({
|
||||
tabsValue: 'signin',
|
||||
tabsValue: 'signing',
|
||||
})
|
||||
|
||||
const { height: windowHeight, width: windowWidth } = useWindowSize()
|
||||
const settingStore = useSetting()
|
||||
const { updateLocale } = settingStore
|
||||
const { updateLocale } = useSettingActions()
|
||||
|
||||
return {
|
||||
...toRefs(state),
|
||||
@ -122,10 +121,10 @@ const Login = defineComponent({
|
||||
default: () => (
|
||||
<>
|
||||
<NTabPane
|
||||
tab={$t('views.login.index.Signin')}
|
||||
name="signin"
|
||||
tab={$t('views.login.index.Signing')}
|
||||
name="signing"
|
||||
>
|
||||
<Signin />
|
||||
<Signing />
|
||||
</NTabPane>
|
||||
<NTabPane
|
||||
tab={$t('views.login.index.Register')}
|
||||
@ -134,17 +133,17 @@ const Login = defineComponent({
|
||||
<Register />
|
||||
</NTabPane>
|
||||
<NTabPane
|
||||
tab={$t('views.login.index.QRCodeSignin')}
|
||||
name="qrcodeSignin"
|
||||
tab={$t('views.login.index.QRCodeSigning')}
|
||||
name="qrcodeSigning"
|
||||
>
|
||||
<QRCodeSignin />
|
||||
<QRCodeSigning />
|
||||
</NTabPane>
|
||||
</>
|
||||
),
|
||||
}}
|
||||
</NTabs>
|
||||
<NDivider>其他登陆方式</NDivider>
|
||||
<SSOSignin />
|
||||
<SSOSigning />
|
||||
<NDivider>友情链接</NDivider>
|
||||
<RayLink />
|
||||
</NCard>
|
||||
|
Loading…
x
Reference in New Issue
Block a user