diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 422cc9b4..de3966c9 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -30,7 +30,12 @@ module.exports = { withDefaults: 'readonly', }, rules: { - '@typescript-eslint/no-explicit-any': 'error', + '@typescript-eslint/no-explicit-any': [ + 'error', + { + ignoreRestArgs: true, + }, + ], 'prettier/prettier': 'error', 'no-unused-vars': 'off', '@typescript-eslint/no-unused-vars': 'off', diff --git a/CHANGELOG.md b/CHANGELOG.md index 82e8951c..9dfaa33f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # CHANGE LOG +## 3.1.8 + +### Fixes + +- 修复路由切换不能复位容器位置问题(让可视区域置顶) + +### Feats + +- 新增 useI18n hook 方法 +- 手动补充 AppRouteRecordRaw、AppRouteMeta 类型 +- 重新拆分 Layout 入口文件 +- 重新指定组件暴露方法、属性 + ## 3.1.7 ### Fixes diff --git a/auto-imports.d.ts b/auto-imports.d.ts index 58e06e8c..a0e4a019 100644 --- a/auto-imports.d.ts +++ b/auto-imports.d.ts @@ -50,6 +50,8 @@ declare global { const nextTick: typeof import('vue')['nextTick'] const onActivated: typeof import('vue')['onActivated'] const onBeforeMount: typeof import('vue')['onBeforeMount'] + const onBeforeRouteLeave: typeof import('vue-router')['onBeforeRouteLeave'] + const onBeforeRouteUpdate: typeof import('vue-router')['onBeforeRouteUpdate'] const onBeforeUnmount: typeof import('vue')['onBeforeUnmount'] const onBeforeUpdate: typeof import('vue')['onBeforeUpdate'] const onClickOutside: typeof import('@vueuse/core')['onClickOutside'] @@ -81,6 +83,7 @@ declare global { const refThrottled: typeof import('@vueuse/core')['refThrottled'] const refWithControl: typeof import('@vueuse/core')['refWithControl'] const resolveComponent: typeof import('vue')['resolveComponent'] + const resolveDirective: typeof import('vue')['resolveDirective'] const resolveRef: typeof import('@vueuse/core')['resolveRef'] const resolveUnref: typeof import('@vueuse/core')['resolveUnref'] const setActivePinia: typeof import('pinia')['setActivePinia'] @@ -112,10 +115,12 @@ declare global { const useArrayFilter: typeof import('@vueuse/core')['useArrayFilter'] const useArrayFind: typeof import('@vueuse/core')['useArrayFind'] const useArrayFindIndex: typeof import('@vueuse/core')['useArrayFindIndex'] + const useArrayFindLast: typeof import('@vueuse/core')['useArrayFindLast'] const useArrayJoin: typeof import('@vueuse/core')['useArrayJoin'] const useArrayMap: typeof import('@vueuse/core')['useArrayMap'] const useArrayReduce: typeof import('@vueuse/core')['useArrayReduce'] const useArraySome: typeof import('@vueuse/core')['useArraySome'] + const useArrayUnique: typeof import('@vueuse/core')['useArrayUnique'] const useAsyncQueue: typeof import('@vueuse/core')['useAsyncQueue'] const useAsyncState: typeof import('@vueuse/core')['useAsyncState'] const useAttrs: typeof import('vue')['useAttrs'] @@ -127,6 +132,7 @@ declare global { const useBrowserLocation: typeof import('@vueuse/core')['useBrowserLocation'] const useCached: typeof import('@vueuse/core')['useCached'] const useClipboard: typeof import('@vueuse/core')['useClipboard'] + const useCloned: typeof import('@vueuse/core')['useCloned'] const useColorMode: typeof import('@vueuse/core')['useColorMode'] const useConfirmDialog: typeof import('@vueuse/core')['useConfirmDialog'] const useCounter: typeof import('@vueuse/core')['useCounter'] @@ -177,6 +183,7 @@ declare global { const useIntervalFn: typeof import('@vueuse/core')['useIntervalFn'] const useKeyModifier: typeof import('@vueuse/core')['useKeyModifier'] const useLastChanged: typeof import('@vueuse/core')['useLastChanged'] + const useLink: typeof import('vue-router')['useLink'] const useLoadingBar: typeof import('naive-ui')['useLoadingBar'] const useLocalStorage: typeof import('@vueuse/core')['useLocalStorage'] const useMagicKeys: typeof import('@vueuse/core')['useMagicKeys'] @@ -202,11 +209,14 @@ declare global { const useParallax: typeof import('@vueuse/core')['useParallax'] const usePermission: typeof import('@vueuse/core')['usePermission'] const usePointer: typeof import('@vueuse/core')['usePointer'] + const usePointerLock: typeof import('@vueuse/core')['usePointerLock'] const usePointerSwipe: typeof import('@vueuse/core')['usePointerSwipe'] const usePreferredColorScheme: typeof import('@vueuse/core')['usePreferredColorScheme'] + const usePreferredContrast: typeof import('@vueuse/core')['usePreferredContrast'] const usePreferredDark: typeof import('@vueuse/core')['usePreferredDark'] const usePreferredLanguages: typeof import('@vueuse/core')['usePreferredLanguages'] const usePreferredReducedMotion: typeof import('@vueuse/core')['usePreferredReducedMotion'] + const usePrevious: typeof import('@vueuse/core')['usePrevious'] const useRafFn: typeof import('@vueuse/core')['useRafFn'] const useRefHistory: typeof import('@vueuse/core')['useRefHistory'] const useResizeObserver: typeof import('@vueuse/core')['useResizeObserver'] @@ -220,6 +230,7 @@ declare global { const useSessionStorage: typeof import('@vueuse/core')['useSessionStorage'] const useShare: typeof import('@vueuse/core')['useShare'] const useSlots: typeof import('vue')['useSlots'] + const useSorted: typeof import('@vueuse/core')['useSorted'] const useSpeechRecognition: typeof import('@vueuse/core')['useSpeechRecognition'] const useSpeechSynthesis: typeof import('@vueuse/core')['useSpeechSynthesis'] const useStepper: typeof import('@vueuse/core')['useStepper'] diff --git a/src/components/RayChart/index.tsx b/src/components/RayChart/index.tsx index 622577ea..8ed46a55 100644 --- a/src/components/RayChart/index.tsx +++ b/src/components/RayChart/index.tsx @@ -199,7 +199,7 @@ const RayChart = defineComponent({ default: () => loadingOptions(), }, }, - setup(props) { + setup(props, { expose }) { const settingStore = useSetting() const { themeValue } = storeToRefs(settingStore) const rayChartRef = ref() // `echart` 容器实例 @@ -465,6 +465,10 @@ const RayChart = defineComponent({ off(window, 'resize', resizeDebounce) }) + expose({ + echart: echartInstanceRef, + }) + return { rayChartRef, cssVarsRef, diff --git a/src/components/RayTable/src/index.tsx b/src/components/RayTable/src/index.tsx index 6a662cd2..64caf7cb 100644 --- a/src/components/RayTable/src/index.tsx +++ b/src/components/RayTable/src/index.tsx @@ -57,7 +57,7 @@ const RayTable = defineComponent({ name: 'RayTable', props: props, emits: ['update:columns', 'menuSelect', 'exportSuccess', 'exportError'], - setup(props, { emit }) { + setup(props, { emit, expose }) { const rayTableInstance = ref() const tableUUID = uuid() // 表格 id, 用于打印表格 @@ -83,6 +83,7 @@ const RayTable = defineComponent({ return cssVar }) const tableSize = ref(props.size) + const tableMethods = ref>() /** 注入相关属性 */ provide('tableSettingProvider', { @@ -196,6 +197,40 @@ const RayTable = defineComponent({ tableSize.value = size } + const registerRayTableMethods = (ins: DataTableInst) => { + const { + clearFilters, + clearSorter, + filters, + page, + scrollTo, + sort, + filter, + } = ins + + tableMethods.value = { + clearFilters, + clearSorter, + filters, + page, + scrollTo, + sort, + filter, + } + } + + expose({ + tableMethods: computed(() => tableMethods.value), + }) + + onMounted(() => { + registerRayTableMethods(rayTableInstance.value as DataTableInst) + }) + + // expose({ + // tableMethods: tableMethods.value, + // }) + return { tableUUID, rayTableUUID, @@ -217,14 +252,14 @@ const RayTable = defineComponent({ class="ray-table" bordered={this.bordered} style={[this.cssVars]} - id={this.rayTableUUID} + {...{ id: this.rayTableUUID }} > {{ default: () => ( <> VNodeChild) -export declare interface RayTableInst { - rayTableInstance: DataTableInst +export declare type RayTableInst = { + tableMethods: Omit } diff --git a/src/language/index.ts b/src/language/index.ts index 8cf6ddb0..a2b1a30c 100644 --- a/src/language/index.ts +++ b/src/language/index.ts @@ -33,6 +33,9 @@ import { forIn, merge } from 'lodash-es' export { naiveLocales, localOptions } from './language' import type { App } from 'vue' +import type { I18n } from 'vue-i18n' + +export let i18n: I18n /** * @@ -88,13 +91,17 @@ export const getDefaultLocal = () => { export const setupI18n = (app: App) => { const locale = getDefaultLocal() - const i18n = createI18n({ + const i18nInstance = createI18n({ locale, allowComposition: true, messages: getMatchLanguageModule(), + legacy: false, + sync: true, }) - app.use(i18n) + i18n = i18nInstance + + app.use(i18nInstance) } /** diff --git a/src/language/useI18n.ts b/src/language/useI18n.ts new file mode 100644 index 00000000..bdd8ace9 --- /dev/null +++ b/src/language/useI18n.ts @@ -0,0 +1,48 @@ +import { i18n } from './index' + +const getI18nKey = (namespace: string | undefined, key: string) => { + if (!namespace) { + return key + } + + if (key.startsWith(namespace)) { + return key + } + + return `${namespace}.${key}` +} + +export const useI18n = (namespace?: string) => { + const normalFunc = { + t: (key: string) => { + return getI18nKey(namespace, key) + }, + } + + if (!i18n) { + return normalFunc + } + + const { t, ...methods } = i18n.global + + const overridesTFunc = (key: string, ...args: any[]) => { + if (!key) { + return '' + } + + if (!key.includes('.') && !namespace) { + return key + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + return (t as any)(getI18nKey(namespace, key), ...args) + } + + return { + ...methods, + t: overridesTFunc, + } +} + +/** 配合 i18n ally 插件提示使用 */ +export const t = (key: string) => key diff --git a/src/layout/components/SiderBar/Components/SettingDrawer/index.tsx b/src/layout/components/SiderBar/Components/SettingDrawer/index.tsx index 5186b7af..8e5fb03e 100644 --- a/src/layout/components/SiderBar/Components/SettingDrawer/index.tsx +++ b/src/layout/components/SiderBar/Components/SettingDrawer/index.tsx @@ -13,6 +13,7 @@ import ThemeSwitch from '@/layout/components/SiderBar/components/SettingDrawer/c import { useSwatchesColorOptions } from './hook' import { useSetting } from '@/store' +import { useI18n } from '@/language/useI18n' import type { PropType } from 'vue' diff --git a/src/layout/components/SiderBar/index.tsx b/src/layout/components/SiderBar/index.tsx index 34d952d6..f5f8ce17 100644 --- a/src/layout/components/SiderBar/index.tsx +++ b/src/layout/components/SiderBar/index.tsx @@ -24,6 +24,7 @@ import { localOptions } from '@/language/index' import { useAvatarOptions } from './hook' import { getCache } from '@/utils/cache' import screenfull from 'screenfull' +import { useI18n } from '@/language/useI18n' import type { IconEventMapOptions, IconEventMap } from './type' diff --git a/src/layout/default/ContentWrapper/index.scss b/src/layout/default/ContentWrapper/index.scss new file mode 100644 index 00000000..a2c09a37 --- /dev/null +++ b/src/layout/default/ContentWrapper/index.scss @@ -0,0 +1,3 @@ +.content-wrapper { + box-sizing: border-box; +} diff --git a/src/layout/default/ContentWrapper/index.tsx b/src/layout/default/ContentWrapper/index.tsx new file mode 100644 index 00000000..93aefa8f --- /dev/null +++ b/src/layout/default/ContentWrapper/index.tsx @@ -0,0 +1,38 @@ +/** + * + * @author Ray + * + * @date 2023-04-21 + * + * @workspace ray-template-mine + * + * @remark 今天也是元气满满撸代码的一天 + */ + +import './index.scss' + +import RayTransitionComponent from '@/components/RayTransitionComponent/index.vue' + +import { useSetting } from '@/store' + +const ContentWrapper = defineComponent({ + name: 'ContentWrapper', + setup() { + const settingStore = useSetting() + + const { reloadRouteSwitch } = storeToRefs(settingStore) + + return { + reloadRouteSwitch, + } + }, + render() { + return this.reloadRouteSwitch ? ( + + ) : ( + <> + ) + }, +}) + +export default ContentWrapper diff --git a/src/layout/default/FooterWrapper/index.scss b/src/layout/default/FooterWrapper/index.scss new file mode 100644 index 00000000..bd7d04cc --- /dev/null +++ b/src/layout/default/FooterWrapper/index.scss @@ -0,0 +1,4 @@ +.layout-footer-wrapper { + padding: 24px; + text-align: center; +} diff --git a/src/layout/default/FooterWrapper/index.tsx b/src/layout/default/FooterWrapper/index.tsx new file mode 100644 index 00000000..ecdd85fc --- /dev/null +++ b/src/layout/default/FooterWrapper/index.tsx @@ -0,0 +1,34 @@ +/** + * + * @author Ray + * + * @date 2023-04-21 + * + * @workspace ray-template-mine + * + * @remark 今天也是元气满满撸代码的一天 + */ + +import './index.scss' + +const FooterWrapper = defineComponent({ + name: 'FooterWrapper', + setup() { + const { + layout: { copyright }, + } = __APP_CFG__ + + return { + copyright, + } + }, + render() { + return this.copyright ? ( + + ) : ( + <> + ) + }, +}) + +export default FooterWrapper diff --git a/src/layout/index.scss b/src/layout/index.scss index 379b61b5..a21db6d5 100644 --- a/src/layout/index.scss +++ b/src/layout/index.scss @@ -17,9 +17,4 @@ } } } - - & .layout-footer { - padding: 24px; - text-align: center; - } } diff --git a/src/layout/index.tsx b/src/layout/index.tsx index 042dcba5..950155f5 100644 --- a/src/layout/index.tsx +++ b/src/layout/index.tsx @@ -1,10 +1,19 @@ +/** + * + * 页面布局入口文件 + * + * 说明: + * - rayLayoutContentWrapperScopeSelector: 页面切换时重置滚动条注入 id + */ + import './index.scss' import { NLayout, NLayoutContent } from 'naive-ui' -import RayTransitionComponent from '@/components/RayTransitionComponent/index.vue' -import LayoutMenu from './components/Menu/index' +import Menu from './components/Menu/index' import SiderBar from './components/SiderBar/index' import MenuTag from './components/MenuTag/index' +import ContentWrapper from '@/layout/default/ContentWrapper' +import FooterWrapper from '@/layout/default/FooterWrapper' import { useSetting } from '@/store' @@ -14,10 +23,7 @@ const Layout = defineComponent({ const settingStore = useSetting() const { height: windowHeight } = useWindowSize() - const { - reloadRouteSwitch: modelReloadRoute, - menuTagSwitch: modelMenuTagSwitch, - } = storeToRefs(settingStore) + const { menuTagSwitch: modelMenuTagSwitch } = storeToRefs(settingStore) const cssVarsRef = computed(() => { let cssVar = {} @@ -33,16 +39,11 @@ const Layout = defineComponent({ return cssVar }) - const { - layout: { copyright }, - } = __APP_CFG__ return { windowHeight, - modelReloadRoute, modelMenuTagSwitch, cssVarsRef, - copyright, } }, render() { @@ -52,20 +53,17 @@ const Layout = defineComponent({ style={[`height: ${this.windowHeight}px`, this.cssVarsRef]} > - + {this.modelMenuTagSwitch ? : ''} - {this.modelReloadRoute ? : ''} - {this.copyright ? ( - - ) : ( - '' - )} + + diff --git a/src/router/index.ts b/src/router/index.ts index ad174956..30887524 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -2,14 +2,54 @@ import { createRouter, createWebHashHistory } from 'vue-router' import { constantRoutes } from './routes' import { permissionRouter as _permissionRouter } from './permission' +import { getElement } from '@/utils/element' import type { App } from 'vue' -import type { RouteRecordRaw } from 'vue-router' +import type { RouteRecordRaw, RouteLocationNormalized } from 'vue-router' + +/** + * + * 切换路由时, 手动将容器区域回归默认值 + * + * 由于官方不支持这个方法了, 所以自己手写了一个 + * 如果需要忽略恢复默认位置, 仅需要在 meta 中配置 ignoreResetScroll 属性即可 + * + * 找到滚动元素容器的写法有点丑陋, 暂时也想不到啥好方法解决, 就凑合一下吧 + */ +const scrollViewToTop = (route: RouteLocationNormalized) => { + const { meta } = route + + /** 这个 id 是注入在 layout 中 */ + if (!meta?.ignoreResetScroll) { + const scrollViewRoot = getElement( + '#rayLayoutContentWrapperScopeSelector', + )?.[0] + + if (scrollViewRoot && typeof scrollViewRoot.scroll) { + /** 找到 NLayoutContent 组件滚动元素 */ + const scrollView = scrollViewRoot?.firstElementChild + ?.firstChild as HTMLElement + + scrollView?.scroll({ + top: 0, + left: 0, + behavior: 'smooth', + }) + } + } +} export const router = createRouter({ history: createWebHashHistory(), routes: constantRoutes as unknown as RouteRecordRaw[], - scrollBehavior: () => ({ left: 0, top: 0 }), + scrollBehavior: (to) => { + scrollViewToTop(to) + + return { + top: 0, + left: 0, + } + }, }) export const permissionRouter = () => _permissionRouter(router) diff --git a/src/router/modules/axios.ts b/src/router/modules/axios.ts index 3822cca5..8170be45 100644 --- a/src/router/modules/axios.ts +++ b/src/router/modules/axios.ts @@ -1,9 +1,13 @@ -export default { +import type { AppRouteRecordRaw } from '@/router/type' + +const axios: AppRouteRecordRaw = { path: '/axios', - name: 'axios', + name: 'Axios', component: () => import('@/views/axios/index'), meta: { i18nKey: 'Axios', icon: 'axios', }, } + +export default axios diff --git a/src/router/modules/dashboard.ts b/src/router/modules/dashboard.ts index 3394a8f2..ccabbc0b 100644 --- a/src/router/modules/dashboard.ts +++ b/src/router/modules/dashboard.ts @@ -1,9 +1,13 @@ -export default { +import type { AppRouteRecordRaw } from '@/router/type' + +const dashboard: AppRouteRecordRaw = { path: '/dashboard', - name: 'dashboard', + name: 'Dashboard', component: () => import('@/views/dashboard/index'), meta: { i18nKey: 'Dashboard', icon: 'dashboard', }, } + +export default dashboard diff --git a/src/router/modules/doc-local.ts b/src/router/modules/doc-local.ts index 31a2f7ed..3fff123a 100644 --- a/src/router/modules/doc-local.ts +++ b/src/router/modules/doc-local.ts @@ -1,6 +1,8 @@ -export default { +import type { AppRouteRecordRaw } from '@/router/type' + +const docLocal: AppRouteRecordRaw = { path: '/doc', - name: 'doc', + name: 'DocLocal', component: () => import('@/views/doc/index'), meta: { i18nKey: 'DocLocal', @@ -8,3 +10,5 @@ export default { windowOpen: 'https://ray-template.yunkuangao.com/ray-template-doc/', }, } + +export default docLocal diff --git a/src/router/modules/doc.ts b/src/router/modules/doc.ts index b073d811..78cc8fd2 100644 --- a/src/router/modules/doc.ts +++ b/src/router/modules/doc.ts @@ -1,6 +1,8 @@ -export default { +import type { AppRouteRecordRaw } from '@/router/type' + +const doc: AppRouteRecordRaw = { path: '/doc', - name: 'doc', + name: 'Doc', component: () => import('@/views/doc/index'), meta: { i18nKey: 'Doc', @@ -8,3 +10,5 @@ export default { windowOpen: 'https://xiaodaigua-ray.github.io/ray-template-doc/', }, } + +export default doc diff --git a/src/router/modules/echart.ts b/src/router/modules/echart.ts index 3cb487dc..dafc7959 100644 --- a/src/router/modules/echart.ts +++ b/src/router/modules/echart.ts @@ -1,9 +1,13 @@ -export default { +import type { AppRouteRecordRaw } from '@/router/type' + +const echart: AppRouteRecordRaw = { path: '/echart', - name: 'echart', + name: 'Echart', component: () => import('@/views/echart/index'), meta: { i18nKey: 'Echart', icon: 'echart', }, } + +export default echart diff --git a/src/router/modules/error.ts b/src/router/modules/error.ts index 6d683a67..5c66114f 100644 --- a/src/router/modules/error.ts +++ b/src/router/modules/error.ts @@ -1,9 +1,14 @@ -export default { +import type { AppRouteRecordRaw } from '@/router/type' + +const error: AppRouteRecordRaw = { path: '/error', - name: 'error', + name: 'ErrorPage', component: () => import('@/views/error/index'), meta: { i18nKey: 'Error', icon: 'error', + hidden: true, }, } + +export default error diff --git a/src/router/modules/index.ts b/src/router/modules/index.ts deleted file mode 100644 index 15d551b2..00000000 --- a/src/router/modules/index.ts +++ /dev/null @@ -1,36 +0,0 @@ -import dashboard from './dashboard' -import reyl from './rely' -import error from './error' -import echart from './echart' -import scrollReveal from './scroll-reveal' -import axios from './axios' -import table from './table' -import doc from './doc' -import multiMenu from './multi-menu' -import docLocal from './doc-local' -import office from './office' - -const routes = [ - dashboard, - office, - echart, - table, - axios, - scrollReveal, - error, - multiMenu, - doc, - docLocal, - reyl, -] - -export default routes - -/** - * - * 弃用自动导入路由模块方式 - * - * 采用手动引入子路由模块方式 - * - * 因为自动导入路由方式在实际体验后还是有一些小问题, 综合考虑后, 还是自己手动挡吧 - */ diff --git a/src/router/modules/multi-menu.ts b/src/router/modules/multi-menu.ts index 84ffcce9..13c6aae6 100644 --- a/src/router/modules/multi-menu.ts +++ b/src/router/modules/multi-menu.ts @@ -1,6 +1,8 @@ -export default { +import type { AppRouteRecordRaw } from '@/router/type' + +const multiMenu: AppRouteRecordRaw = { path: '/multi-menu', - name: 'multi-menu', + name: 'MultiMenu', component: () => import('@/views/multi-menu/index'), meta: { i18nKey: 'MultiMenu', @@ -9,7 +11,7 @@ export default { children: [ { path: 'multi-menu-one', - name: 'multi-menu-one', + name: 'MultiMenuOne', component: () => import('@/views/multi-menu/views/multi-menu-one/index'), meta: { noLocalTitle: '多级菜单-1', @@ -17,7 +19,7 @@ export default { }, { path: 'multi-menu-two', - name: 'multi-menu-two', + name: 'MultiMenuTwo', component: () => import('@/views/multi-menu/views/multi-menu-two/index'), meta: { noLocalTitle: '多级菜单-2', @@ -25,7 +27,7 @@ export default { children: [ { path: 'sub-menu', - name: 'sub-menu', + name: 'SubMenu', component: () => import( '@/views/multi-menu/views/multi-menu-two/views/sub-menu/index' @@ -38,3 +40,5 @@ export default { }, ], } + +export default multiMenu diff --git a/src/router/modules/office.ts b/src/router/modules/office.ts index dbfa6a86..af9165d0 100644 --- a/src/router/modules/office.ts +++ b/src/router/modules/office.ts @@ -1,6 +1,8 @@ -export default { +import type { AppRouteRecordRaw } from '@/router/type' + +const office: AppRouteRecordRaw = { path: '/office', - name: 'office', + name: 'Office', component: () => import('@/views/office/index'), meta: { i18nKey: 'Office', @@ -10,7 +12,7 @@ export default { children: [ { path: '/document', - name: 'document', + name: 'Document', component: () => import('@/views/office/views/document/index'), meta: { i18nKey: 'Office_Document', @@ -18,7 +20,7 @@ export default { }, { path: '/presentation', - name: 'presentation', + name: 'Presentation', component: () => import('@/views/office/views/presentation/index'), meta: { i18nKey: 'Office_Presentation', @@ -26,7 +28,7 @@ export default { }, { path: '/spreadsheet', - name: 'spreadsheet', + name: 'Spreadsheet', component: () => import('@/views/office/views/spreadsheet/index'), meta: { i18nKey: 'Office_Spreadsheet', @@ -34,3 +36,5 @@ export default { }, ], } + +export default office diff --git a/src/router/modules/rely.ts b/src/router/modules/rely.ts index bba82e47..00397adb 100644 --- a/src/router/modules/rely.ts +++ b/src/router/modules/rely.ts @@ -1,6 +1,8 @@ -export default { +import type { AppRouteRecordRaw } from '@/router/type' + +const rely: AppRouteRecordRaw = { path: '/rely', - name: 'rely', + name: 'Rely', component: () => import('@/views/rely/index'), meta: { i18nKey: 'Rely', @@ -9,7 +11,7 @@ export default { children: [ { path: '/rely-about', - name: 'rely-about', + name: 'RelyAbout', component: () => import('@/views/rely/views/rely-about/index'), meta: { i18nKey: 'RelyAbout', @@ -17,3 +19,5 @@ export default { }, ], } + +export default rely diff --git a/src/router/modules/scroll-reveal.ts b/src/router/modules/scroll-reveal.ts index 850fa791..0b3eca84 100644 --- a/src/router/modules/scroll-reveal.ts +++ b/src/router/modules/scroll-reveal.ts @@ -1,6 +1,8 @@ -export default { +import type { AppRouteRecordRaw } from '@/router/type' + +const scrollReveal: AppRouteRecordRaw = { path: '/scroll-reveal', - name: 'scroll-reveal', + name: 'ScrollReveal', component: () => import('@/views/scroll-reveal/index'), meta: { i18nKey: 'scrollReveal', @@ -8,3 +10,5 @@ export default { hidden: true, }, } + +export default scrollReveal diff --git a/src/router/modules/table.ts b/src/router/modules/table.ts index fc9cfb77..066a691d 100644 --- a/src/router/modules/table.ts +++ b/src/router/modules/table.ts @@ -1,9 +1,13 @@ -export default { +import type { AppRouteRecordRaw } from '@/router/type' + +const table: AppRouteRecordRaw = { path: '/table', - name: 'table', + name: 'TableView', component: () => import('@/views/table/index'), meta: { i18nKey: 'Table', icon: 'table', }, } + +export default table diff --git a/src/router/route-module.ts b/src/router/route-module.ts new file mode 100644 index 00000000..8d8c5433 --- /dev/null +++ b/src/router/route-module.ts @@ -0,0 +1,38 @@ +import type { AppRouteRecordRaw } from '@/router/type' + +import dashboard from './modules/dashboard' +import reyl from './modules/rely' +import error from './modules/error' +import echart from './modules/echart' +import scrollReveal from './modules/scroll-reveal' +import axios from './modules/axios' +import table from './modules/table' +import doc from './modules/doc' +import multiMenu from './modules/multi-menu' +import docLocal from './modules/doc-local' +import office from './modules/office' + +const routes: AppRouteRecordRaw[] = [ + dashboard, + office, + echart, + table, + axios, + scrollReveal, + error, + multiMenu, + doc, + docLocal, + reyl, +] + +export default routes + +/** + * + * 弃用自动导入路由模块方式 + * + * 采用手动引入子路由模块方式 + * + * 因为自动导入路由方式在实际体验后还是有一些小问题, 综合考虑后, 还是自己手动挡吧 + */ diff --git a/src/router/routes.ts b/src/router/routes.ts index c4f73464..afec43cc 100644 --- a/src/router/routes.ts +++ b/src/router/routes.ts @@ -1,5 +1,5 @@ import Layout from '@/layout/index' -import childrenRoutes from './modules/index' +import childrenRoutes from './route-module' const { rootRoute: { path }, @@ -22,7 +22,8 @@ export const constantRoutes = [ /** 错误页面(404) */ path: '/:catchAll(.*)', name: 'error-page', - component: () => import('@/views/error/index'), + component: Layout, + redirect: '/error', }, ] diff --git a/src/router/type.ts b/src/router/type.ts new file mode 100644 index 00000000..1a557b5b --- /dev/null +++ b/src/router/type.ts @@ -0,0 +1,30 @@ +/* eslint-disable @typescript-eslint/ban-ts-comment */ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import type { RouteRecordRaw } from 'vue-router' +import type { Recordable } from '@/types/type-utils' + +export type Component = + | ReturnType + | (() => Promise) + | (() => Promise) + +export interface AppRouteMeta extends IUnknownObjectKey { + i18nKey?: string + icon?: string + windowOpen?: string + role?: string[] + hidden?: boolean + noLocalTitle?: string | number + ignoreResetScroll?: boolean +} + +// @ts-ignore +export interface AppRouteRecordRaw extends Omit { + name: string + meta: AppRouteMeta + component?: Component | string + components?: Component + children?: AppRouteRecordRaw[] + props?: Recordable + fullPath?: string +} diff --git a/src/store/modules/menu/index.ts b/src/store/modules/menu/index.ts index 99bc4785..0a3a5b24 100644 --- a/src/store/modules/menu/index.ts +++ b/src/store/modules/menu/index.ts @@ -28,6 +28,7 @@ import RayIcon from '@/components/RayIcon/index' import { getCache, setCache } from '@/utils/cache' import { validRole } from '@/router/basic' import { parse, matchMenuOption, updateDocumentTitle } from './helper' +import { useI18n } from '@/language/useI18n' import type { MenuOption } from 'naive-ui' import type { RouteMeta } from 'vue-router' diff --git a/src/types/module.d.ts b/src/types/module.d.ts new file mode 100644 index 00000000..e7997d1c --- /dev/null +++ b/src/types/module.d.ts @@ -0,0 +1,12 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +declare module '*.vue' { + import type { DefineComponent } from 'vue' + + const Component: DefineComponent<{}, {}, any> + export default Component +} + +declare module 'virtual:*' { + const result: any + export default result +} diff --git a/src/types/store.d.ts b/src/types/store.d.ts index 52aeea47..8580f81e 100644 --- a/src/types/store.d.ts +++ b/src/types/store.d.ts @@ -3,9 +3,10 @@ export {} import type { RouteRecordRaw, RouteMeta } from 'vue-router' import type { MenuOption } from 'naive-ui' import type { VNode } from 'vue' +import type { AppRouteRecordRaw } from '@/router/type' declare global { - declare interface IMenuOptions extends RouteRecordRaw, MenuOption { + declare interface IMenuOptions extends AppRouteRecordRaw, MenuOption { name: string key: string | number path: string diff --git a/src/types/type-utils.ts b/src/types/type-utils.ts index 59111b0f..144bd302 100644 --- a/src/types/type-utils.ts +++ b/src/types/type-utils.ts @@ -1,14 +1,7 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ export type ConditionalKeys = NonNullable< - // Wrap in `NonNullable` to strip away the `undefined` type from the produced union. { - // Map through all the keys of the given base type. - [Key in keyof Base]: Base[Key] extends Condition // Pick only keys with types extending the given `Condition` type. - ? // Retain this key since the condition passes. - Key - : // Discard this key since the condition fails. - never - - // Convert the produced object into a union type of the keys which passed the conditional test. + [Key in keyof Base]: Base[Key] extends Condition ? Key : never }[keyof Base] > @@ -16,3 +9,5 @@ export type ConditionalPick = Pick< Base, ConditionalKeys > + +export type Recordable = Record diff --git a/src/utils/element.ts b/src/utils/element.ts index 768dc490..c489e6e4 100644 --- a/src/utils/element.ts +++ b/src/utils/element.ts @@ -1,33 +1,4 @@ import { validteValueType } from '@use-utils/hook' - -/** - * - * @param el 父节点对象 - * @param target 是否需要过滤,可按照数组或单个字符过滤 - * - * @returns 目标节点下所有子节点 - */ -export const getElementChildNodes = ( - el: HTMLElement, - target?: string[] | string, -) => { - if (el) { - let nodes = Array.from(el.childNodes) - - if (Array.isArray(target)) { - nodes = nodes.filter((el) => target.includes(el.nodeName)) - } else { - if (target) { - nodes = nodes.filter((el) => el.nodeName === target) - } - } - - return nodes - } else { - return [] - } -} - /** * * @param element Target element dom @@ -214,3 +185,44 @@ export const colorToRgba = (color: string, alpha = 1) => { return result } + +/** + * + * @param element 需要匹配元素参数名称 + * @returns 匹配元素列表 + * + * @remark 使用 querySelectorAll 作为检索方法 + * @remark 如果希望按照 attribute 匹配, 仅需要 'attr:xxx'传递参数即可 + * + * 示例: + * + * class: + * const el = getElement('.demo') + * id: + * const el = getElement('#demo') + * attribute: + * const el = getElement('attr:type=button') + * 或者可以这样写 + * const el = getElement('attr:type') + */ +export const getElement = (element: string) => { + if (!element) { + return + } + + let queryParam: string + + if (element.startsWith('attr:')) { + queryParam = '[' + element.replace('attr:', '') + ']' + } else { + queryParam = element + } + + try { + const el = Array.from(document.querySelectorAll(queryParam)) + + return el + } catch (e) { + return [] + } +} diff --git a/src/views/echart/index.tsx b/src/views/echart/index.tsx index a65795e0..0967faf8 100644 --- a/src/views/echart/index.tsx +++ b/src/views/echart/index.tsx @@ -182,7 +182,7 @@ const Echart = defineComponent({ duration: 5 * 1000, }) - console.log(chart) + console.log(baseChartRef.value, chart) } return { @@ -214,6 +214,7 @@ const Echart = defineComponent({ 渲染成功后运行回调函数
diff --git a/src/views/login/components/Signin/index.tsx b/src/views/login/components/Signin/index.tsx index fe558380..9e8d5ef6 100644 --- a/src/views/login/components/Signin/index.tsx +++ b/src/views/login/components/Signin/index.tsx @@ -3,6 +3,7 @@ import { NForm, NFormItem, NInput, NButton, NSpace, NDivider } from 'naive-ui' import { setCache } from '@/utils/cache' import { useSpin } from '@/spin' import { useSignin } from '@/store' +import { useI18n } from '@/language/useI18n' import type { FormInst } from 'naive-ui' diff --git a/src/views/login/index.tsx b/src/views/login/index.tsx index 1c0cd5fb..06ed4013 100644 --- a/src/views/login/index.tsx +++ b/src/views/login/index.tsx @@ -21,6 +21,7 @@ import ThemeSwitch from '@/layout/components/SiderBar/components/SettingDrawer/c import { useSetting } from '@/store' import { localOptions } from '@/language/index' +import { useI18n } from '@/language/useI18n' const Login = defineComponent({ name: 'Login', diff --git a/src/views/table/index.tsx b/src/views/table/index.tsx index 7431a494..31a9bf20 100644 --- a/src/views/table/index.tsx +++ b/src/views/table/index.tsx @@ -148,7 +148,7 @@ const TableView = defineComponent({ } onMounted(() => { - console.log(tableRef.value?.rayTableInstance) + console.log(tableRef.value?.tableMethods) }) return { diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts index 144b9cf5..f93e319c 100644 --- a/src/vite-env.d.ts +++ b/src/vite-env.d.ts @@ -3,6 +3,7 @@ /// import 'vue-router' +import { AppRouteMeta } from '@/router/type' declare module '*.vue' { import type { DefineComponent } from 'vue' @@ -11,14 +12,7 @@ declare module '*.vue' { } declare module 'vue-router' { - interface RouteMeta { - i18nKey: string - icon?: string - windowOpen?: string - role?: string[] - hidden?: boolean - noLocalTitle?: string | number - } + interface RouteMeta extends AppRouteMeta {} } declare module 'virtual:*' {