This commit is contained in:
ray_wuhao 2023-06-29 17:51:47 +08:00
parent 5d4e23149d
commit 0411ec7277
41 changed files with 366 additions and 268 deletions

View File

@ -1,5 +1,19 @@
# CHANGE LOG # CHANGE LOG
## 4.0.1
### Feats
- 更改自定义路由暴露形式(由变量暴露改为方法获取)
- 模板所有方法进行检查,重命名部分方法(使其更加贴切其逻辑)
- 部分逻辑进行重写,使代码更容易阅读与维护
- 模板类型进一步完善
### Fixes
- 修复了内存高占用问题(路由模块)
- 修复类型导入错误问题
## 4.0.0 ## 4.0.0
### Feats ### Feats

6
cfg.ts
View File

@ -18,7 +18,7 @@
* - 系统: 根路由 * - 系统: 根路由
* - 请求: 代理配置 * - 请求: 代理配置
* *
* , src/types/cfg.ts * , src/types/modules/cfg.ts
* ``` * ```
* interface Config // config 内容类型配置 * interface Config // config 内容类型配置
* *
@ -43,7 +43,7 @@ import {
buildOptions, buildOptions,
mixinCSSPlugin, mixinCSSPlugin,
} from './vite-plugin/index' } from './vite-plugin/index'
import { APP_PRIMARY_COLOR } from './src/appConfig/designConfig' import { APP_THEME } from './src/appConfig/designConfig'
import { import {
PRE_LOADING_CONFIG, PRE_LOADING_CONFIG,
ROOT_ROUTE, ROOT_ROUTE,
@ -58,7 +58,7 @@ const config: AppConfigExport = {
/** 配置首屏加载信息 */ /** 配置首屏加载信息 */
preloadingConfig: PRE_LOADING_CONFIG, preloadingConfig: PRE_LOADING_CONFIG,
/** 默认主题色(不可省略, 必填), 也用于 ejs 注入 */ /** 默认主题色(不可省略, 必填), 也用于 ejs 注入 */
appPrimaryColor: APP_PRIMARY_COLOR, appPrimaryColor: APP_THEME.APP_PRIMARY_COLOR,
sideBarLogo: SIDE_BAR_LOGO, sideBarLogo: SIDE_BAR_LOGO,
/** /**
* *

View File

@ -16,10 +16,7 @@ import type {
PreloadingConfig, PreloadingConfig,
RootRoute, RootRoute,
} from '@/types/modules/cfg' } from '@/types/modules/cfg'
import type { import type { AppMenuConfig, AppKeepAlive } from '@/types/modules/appConfig'
MenuCollapsedConfig,
AppKeepAlive,
} from '@/types/modules/appConfig'
/** /**
* *
@ -80,21 +77,18 @@ export const SIDE_BAR_LOGO: LayoutSideBarLogo = {
* MENU_COLLAPSED_MODE: * MENU_COLLAPSED_MODE:
* - transform: 边栏将只会移动它的位置而不会改变宽度 * - transform: 边栏将只会移动它的位置而不会改变宽度
* - width: Sider * - width: Sider
*
* MENU_COLLAPSED_ICON_SIZE * MENU_COLLAPSED_ICON_SIZE
*
* MENU_COLLAPSED_INDENT * MENU_COLLAPSED_INDENT
* MENU_ACCORDION
*/ */
export const MENU_COLLAPSED_CONFIG: Readonly<MenuCollapsedConfig> = { export const APP_MENU_CONFIG: Readonly<AppMenuConfig> = {
MENU_COLLAPSED_WIDTH: 64, MENU_COLLAPSED_WIDTH: 64,
MENU_COLLAPSED_MODE: 'width', MENU_COLLAPSED_MODE: 'width',
MENU_COLLAPSED_ICON_SIZE: 22, MENU_COLLAPSED_ICON_SIZE: 22,
MENU_COLLAPSED_INDENT: 24, MENU_COLLAPSED_INDENT: 24,
MENU_ACCORDION: false,
} }
/** 是否启用菜单手风琴模式 */
export const MENU_ACCORDION = false
/** /**
* *
* key * key

View File

@ -11,15 +11,15 @@
/** 系统颜色风格配置入口 */ /** 系统颜色风格配置入口 */
import type { AppPrimaryColor } from '@/types/modules/cfg' import type { AppTheme } from '@/types/modules/cfg'
import type { GlobalThemeOverrides } from 'naive-ui'
/** export const APP_THEME: AppTheme = {
/**
* *
* *
* RGBARGB * RGBARGB
*/ */
export const APP_THEME_COLOR = [ APP_THEME_COLOR: [
'#2d8cf0', '#2d8cf0',
'#0960bd', '#0960bd',
'#536dfe', '#536dfe',
@ -28,17 +28,15 @@ export const APP_THEME_COLOR = [
'#9c27b0', '#9c27b0',
'#ff9800', '#ff9800',
'#18A058', '#18A058',
] ],
/** 系统主题色 */
/** 系统主题色 */ APP_PRIMARY_COLOR: {
export const APP_PRIMARY_COLOR: AppPrimaryColor = {
/** 主题色 */ /** 主题色 */
primaryColor: '#2d8cf0', primaryColor: '#2d8cf0',
/** 主题辅助色(用于整体 hover、active 等之类颜色) */ /** 主题辅助色(用于整体 hover、active 等之类颜色) */
primaryFadeColor: 'rgba(45, 140, 240, 0.3)', primaryFadeColor: 'rgba(45, 140, 240, 0.3)',
} },
/**
/**
* *
* naive-ui * naive-ui
* : <https://www.naiveui.com/zh-CN/dark/docs/customize-theme> * : <https://www.naiveui.com/zh-CN/dark/docs/customize-theme>
@ -61,4 +59,5 @@ export const APP_PRIMARY_COLOR: AppPrimaryColor = {
* , peers * , peers
* : <https://www.naiveui.com/zh-CN/dark/docs/customize-theme#%E4%BD%BF%E7%94%A8-peers-%E4%B8%BB%E9%A2%98%E5%8F%98%E9%87%8F> * : <https://www.naiveui.com/zh-CN/dark/docs/customize-theme#%E4%BD%BF%E7%94%A8-peers-%E4%B8%BB%E9%A2%98%E5%8F%98%E9%87%8F>
*/ */
export const APP_NAIVE_UI_THEME_OVERRIDES: GlobalThemeOverrides = {} APP_NAIVE_UI_THEME_OVERRIDES: {},
}

View File

@ -15,6 +15,8 @@
* , * ,
*/ */
/** css 尺寸单位匹配 */ export const APP_REGEX: Record<string, RegExp> = {
export const ELEMENT_UNIT = /** css 尺寸单位匹配 */
/^\d+(\.\d+)?(px|em|rem|%|vw|vh|vmin|vmax|cm|mm|in|pt|pc|ch|ex|q|s|ms|deg|rad|turn|grad|hz|khz|dpi|dpcm|dppx|fr|auto)$/ validerCSSUnit:
/^\d+(\.\d+)?(px|em|rem|%|vw|vh|vmin|vmax|cm|mm|in|pt|pc|ch|ex|q|s|ms|deg|rad|turn|grad|hz|khz|dpi|dpcm|dppx|fr|auto)$/,
}

View File

@ -13,9 +13,6 @@
import type { LayoutInst } from 'naive-ui' import type { LayoutInst } from 'naive-ui'
/** 路由切换容器区域 id 配置 */
export const viewScrollContainerId = 'rayLayoutContentWrapperScopeSelector'
/** /**
* *
* ref * ref

View File

@ -21,8 +21,8 @@ import type { RequestHeaderOptions } from '../type'
* *
* @remark `axios` * @remark `axios`
*/ */
export const appendRequestHeaders = ( export const appendRequestHeaders = <T = unknown>(
instance: AxiosRequestConfig<unknown>, instance: AxiosRequestConfig<T>,
options: RequestHeaderOptions[], options: RequestHeaderOptions[],
) => { ) => {
if (instance) { if (instance) {

View File

@ -56,12 +56,11 @@ export default class RequestCanceler {
const controller = new AbortController() const controller = new AbortController()
config.signal = controller.signal config.signal = controller.signal
this.pendingRequest.set(requestKey, controller) this.pendingRequest.set(requestKey, controller)
} else { } else {
// 如果已经有该 key 则重新挂载 signal // 如果已经有该 key 则重新挂载 signal
config.signal = ( config.signal = this.pendingRequest.get(requestKey)?.signal
this.pendingRequest.get(requestKey) as AbortController
).signal
} }
} }

View File

@ -29,17 +29,20 @@ import type {
ImplementQueue, ImplementQueue,
ErrorImplementQueue, ErrorImplementQueue,
FetchType, FetchType,
AxiosFetchInstance,
AxiosFetchError,
} from '@/axios/type' } from '@/axios/type'
import type { AnyFunc } from '@/types/modules/utils' import type { AnyFunc } from '@/types/modules/utils'
/** 当前请求的实例 */ /** 当前请求的实例 */
const axiosFetchInstance = { const axiosFetchInstance: AxiosFetchInstance = {
requestInstance: null as RequestInterceptorConfig | null, requestInstance: null,
responseInstance: null as ResponseInterceptorConfig | null, responseInstance: null,
} }
const axiosFetchError = { /** 请求失败返回值 */
requestError: null as null | unknown, const axiosFetchError: AxiosFetchError = {
responseError: null as null | unknown, requestError: null,
responseError: null,
} }
/** 请求队列(区分 reslove 与 reject 状态) */ /** 请求队列(区分 reslove 与 reject 状态) */
const implement: ImplementQueue = { const implement: ImplementQueue = {
@ -57,7 +60,7 @@ export const useAxiosInterceptor = () => {
/** 创建拦截器实例 */ /** 创建拦截器实例 */
const createAxiosInstance = ( const createAxiosInstance = (
instance: RequestInterceptorConfig | ResponseInterceptorConfig, instance: RequestInterceptorConfig | ResponseInterceptorConfig,
instanceKey: keyof typeof axiosFetchInstance, instanceKey: keyof AxiosFetchInstance,
) => { ) => {
instanceKey === 'requestInstance' instanceKey === 'requestInstance'
? (axiosFetchInstance['requestInstance'] = ? (axiosFetchInstance['requestInstance'] =
@ -67,7 +70,7 @@ export const useAxiosInterceptor = () => {
} }
/** 获取当前实例 */ /** 获取当前实例 */
const getAxiosInstance = (instanceKey: keyof typeof axiosFetchInstance) => { const getAxiosInstance = (instanceKey: keyof AxiosFetchInstance) => {
return axiosFetchInstance[instanceKey] return axiosFetchInstance[instanceKey]
} }
@ -101,7 +104,7 @@ export const useAxiosInterceptor = () => {
/** 请求、响应前执行拦截器队列中的所有方法 */ /** 请求、响应前执行拦截器队列中的所有方法 */
const beforeFetch = ( const beforeFetch = (
key: keyof typeof axiosFetchInstance, key: keyof AxiosFetchInstance,
implementKey: keyof ImplementQueue | keyof ErrorImplementQueue, implementKey: keyof ImplementQueue | keyof ErrorImplementQueue,
fetchType: FetchType, fetchType: FetchType,
) => { ) => {
@ -119,7 +122,7 @@ export const useAxiosInterceptor = () => {
/** 请求、响应错误时执行队列中所有方法 */ /** 请求、响应错误时执行队列中所有方法 */
const fetchError = ( const fetchError = (
key: keyof typeof axiosFetchError, key: keyof AxiosFetchError,
error: unknown, error: unknown,
errorImplementKey: keyof ErrorImplementQueue, errorImplementKey: keyof ErrorImplementQueue,
) => { ) => {

View File

@ -28,6 +28,7 @@ import type {
BeforeFetchFunction, BeforeFetchFunction,
FetchErrorFunction, FetchErrorFunction,
} from '@/axios/type' } from '@/axios/type'
const { setImplement } = useAxiosInterceptor() const { setImplement } = useAxiosInterceptor()
/** /**
@ -74,6 +75,7 @@ const injectCanceler: BeforeFetchFunction<RequestInterceptorConfig> = (
axiosCanceler.addPendingRequest(ins) // 把当前的请求信息添加到 pendingRequest 表中 axiosCanceler.addPendingRequest(ins) // 把当前的请求信息添加到 pendingRequest 表中
} }
/** 请求发生错误示例 */
const requestError: FetchErrorFunction<unknown> = (error, mode) => { const requestError: FetchErrorFunction<unknown> = (error, mode) => {
console.log(error, mode) console.log(error, mode)
} }

View File

@ -33,22 +33,29 @@ import type { AxiosInstanceExpand } from './type'
const server: AxiosInstanceExpand = axios.create(AXIOS_CONFIG) const server: AxiosInstanceExpand = axios.create(AXIOS_CONFIG)
const { createAxiosInstance, beforeFetch, fetchError } = useAxiosInterceptor() const { createAxiosInstance, beforeFetch, fetchError } = useAxiosInterceptor()
// 请求拦截器
server.interceptors.request.use( server.interceptors.request.use(
(request) => { (request) => {
// 生成 request instance
createAxiosInstance(request, 'requestInstance') createAxiosInstance(request, 'requestInstance')
// 初始化拦截器所有已注入方法
setupRequestInterceptor() setupRequestInterceptor()
// 执行拦截器所有已注入方法
beforeFetch('requestInstance', 'implementRequestInterceptorArray', 'ok') beforeFetch('requestInstance', 'implementRequestInterceptorArray', 'ok')
return request return request
}, },
(error) => { (error) => {
// 初始化拦截器所有已注入方法(错误状态)
setupRequestErrorInterceptor() setupRequestErrorInterceptor()
// 执行所有已注入方法
fetchError('requestError', error, 'implementRequestInterceptorErrorArray') fetchError('requestError', error, 'implementRequestInterceptorErrorArray')
return Promise.reject(error) return Promise.reject(error)
}, },
) )
// 响应拦截器
server.interceptors.response.use( server.interceptors.response.use(
(response) => { (response) => {
createAxiosInstance(response, 'responseInstance') createAxiosInstance(response, 'responseInstance')
@ -63,6 +70,7 @@ server.interceptors.response.use(
setupResponseErrorInterceptor() setupResponseErrorInterceptor()
fetchError('responseError', error, 'implementResponseInterceptorErrorArray') fetchError('responseError', error, 'implementResponseInterceptorErrorArray')
// 注销该失败请求的取消器
axiosCanceler.removePendingRequest(error.config || {}) axiosCanceler.removePendingRequest(error.config || {})
return Promise.reject(error) return Promise.reject(error)

View File

@ -104,3 +104,13 @@ export type FetchErrorFunction<T = any> = <K extends T>(
error: K, error: K,
mode: string, mode: string,
) => void ) => void
export interface AxiosFetchInstance {
requestInstance: RequestInterceptorConfig | null
responseInstance: ResponseInterceptorConfig | null
}
export interface AxiosFetchError<T = unknown> {
requestError: T | null
responseError: T | null
}

View File

@ -11,7 +11,7 @@
import type { DirectiveModules } from '@/directives/type' import type { DirectiveModules } from '@/directives/type'
export const mergerDirective = < export const combineDirective = <
T extends Record<string, DirectiveModules>, T extends Record<string, DirectiveModules>,
K extends keyof T, K extends keyof T,
>( >(

View File

@ -9,23 +9,41 @@
* @remark * @remark
*/ */
import { mergerDirective } from './helper/merger' import { combineDirective } from './helper/combine'
import { forIn } from 'lodash-es' import { forIn } from 'lodash-es'
import { isValueType } from '@/utils/hook'
import type { App } from 'vue' import type { App } from 'vue'
import type { DirectiveModules } from '@/directives/type' import type { DirectiveModules } from '@/directives/type'
/** 初始化全局自定义指令 */ /**
*
*
*
* modules
*
* index.ts (, Directive )
*/
export const setupDirective = (app: App<Element>) => { export const setupDirective = (app: App<Element>) => {
const modules = import.meta.glob('./modules/**/index.ts', { // 获取 modules 包下所有的 index.ts 文件
const directiveRawModules: Record<string, DirectiveModules> =
import.meta.glob('./modules/**/index.ts', {
eager: true, eager: true,
}) as Record<string, DirectiveModules> })
const directives = mergerDirective(modules) // 将所有的包提取出来(./modules/[file-name]/index.ts)
const directivesModules = combineDirective(directiveRawModules)
// 提取文件名(./modules/copy/index.ts => copy)
const reg = /(?<=modules\/).*(?=\/index\.ts)/ const reg = /(?<=modules\/).*(?=\/index\.ts)/
forIn(directives, (value, key) => { forIn(directivesModules, (value, key) => {
const directiveName = key.match(reg)?.[0] as string const dname = key.match(reg)?.[0]
app.directive(directiveName, value) if (isValueType<string>(dname, 'String')) {
app.directive(dname, value)
} else {
throw new Error(
'directiveName is not string, please check your directive file name',
)
}
}) })
} }

View File

@ -4,7 +4,7 @@ import { NMenu, NLayoutSider, NEllipsis } from 'naive-ui'
import RayIcon from '@/components/RayIcon/index' import RayIcon from '@/components/RayIcon/index'
import { useMenu } from '@/store' import { useMenu } from '@/store'
import { MENU_COLLAPSED_CONFIG, MENU_ACCORDION } from '@/appConfig/appConfig' import { APP_MENU_CONFIG } from '@/appConfig/appConfig'
import { useVueRouter } from '@/router/helper/useVueRouter' import { useVueRouter } from '@/router/helper/useVueRouter'
import type { MenuInst } from 'naive-ui' import type { MenuInst } from 'naive-ui'
@ -18,7 +18,7 @@ const LayoutMenu = defineComponent({
const menuStore = useMenu() const menuStore = useMenu()
const { router } = useVueRouter() const { router } = useVueRouter()
const { menuModelValueChange, collapsedMenu } = menuStore const { changeMenuModelValue, collapsedMenu } = menuStore
const modelMenuKey = computed({ const modelMenuKey = computed({
get: () => { get: () => {
nextTick().then(() => { nextTick().then(() => {
@ -46,7 +46,7 @@ const LayoutMenu = defineComponent({
return { return {
modelMenuKey, modelMenuKey,
menuModelValueChange, changeMenuModelValue,
modelMenuOptions, modelMenuOptions,
modelCollapsed, modelCollapsed,
collapsedMenu, collapsedMenu,
@ -60,8 +60,8 @@ const LayoutMenu = defineComponent({
<NLayoutSider <NLayoutSider
bordered bordered
showTrigger showTrigger
collapseMode={MENU_COLLAPSED_CONFIG.MENU_COLLAPSED_MODE} collapseMode={APP_MENU_CONFIG.MENU_COLLAPSED_MODE}
collapsedWidth={MENU_COLLAPSED_CONFIG.MENU_COLLAPSED_WIDTH} collapsedWidth={APP_MENU_CONFIG.MENU_COLLAPSED_WIDTH}
onUpdateCollapsed={this.collapsedMenu.bind(this)} onUpdateCollapsed={this.collapsedMenu.bind(this)}
nativeScrollbar={false} nativeScrollbar={false}
> >
@ -94,12 +94,12 @@ const LayoutMenu = defineComponent({
ref="menuRef" ref="menuRef"
v-model:value={this.modelMenuKey} v-model:value={this.modelMenuKey}
options={this.modelMenuOptions as NaiveMenuOptions[]} options={this.modelMenuOptions as NaiveMenuOptions[]}
indent={MENU_COLLAPSED_CONFIG.MENU_COLLAPSED_INDENT} indent={APP_MENU_CONFIG.MENU_COLLAPSED_INDENT}
collapsed={this.modelCollapsed} collapsed={this.modelCollapsed}
collapsedIconSize={MENU_COLLAPSED_CONFIG.MENU_COLLAPSED_ICON_SIZE} collapsedIconSize={APP_MENU_CONFIG.MENU_COLLAPSED_ICON_SIZE}
collapsedWidth={MENU_COLLAPSED_CONFIG.MENU_COLLAPSED_WIDTH} collapsedWidth={APP_MENU_CONFIG.MENU_COLLAPSED_WIDTH}
onUpdateValue={this.menuModelValueChange.bind(this)} onUpdateValue={this.changeMenuModelValue.bind(this)}
accordion={MENU_ACCORDION} accordion={APP_MENU_CONFIG.MENU_ACCORDION}
/> />
</NLayoutSider> </NLayoutSider>
) )

View File

@ -48,7 +48,7 @@ const MenuTag = defineComponent({
const { menuKey, menuTagOptions } = storeToRefs(menuStore) const { menuKey, menuTagOptions } = storeToRefs(menuStore)
const { const {
menuModelValueChange, changeMenuModelValue,
spliceMenTagOptions, spliceMenTagOptions,
emptyMenuTagOptions, emptyMenuTagOptions,
setMenuTagOptions, setMenuTagOptions,
@ -180,7 +180,7 @@ const MenuTag = defineComponent({
spliceMenTagOptions(currentContentmenuIndex + 1, length - 1) spliceMenTagOptions(currentContentmenuIndex + 1, length - 1)
if (menuKey.value !== routeItem.key) { if (menuKey.value !== routeItem.key) {
menuModelValueChange(routeItem.key, routeItem) changeMenuModelValue(routeItem.key, routeItem)
} }
}, },
closeLeft: () => { closeLeft: () => {
@ -197,7 +197,7 @@ const MenuTag = defineComponent({
if (menuKey.value !== routeItem.key) { if (menuKey.value !== routeItem.key) {
emptyMenuTagOptions() emptyMenuTagOptions()
menuModelValueChange(routeItem.key, routeItem) changeMenuModelValue(routeItem.key, routeItem)
} else { } else {
setMenuTagOptions(routeItem, false) setMenuTagOptions(routeItem, false)
} }
@ -226,7 +226,7 @@ const MenuTag = defineComponent({
const tag = options[length - 1] const tag = options[length - 1]
menuModelValueChange(tag.key as string, tag) changeMenuModelValue(tag.key as string, tag)
} }
} }
@ -248,7 +248,7 @@ const MenuTag = defineComponent({
* @param item * @param item
*/ */
const handleTagClick = (item: MenuOption) => { const handleTagClick = (item: MenuOption) => {
menuModelValueChange(item.key as string, item) changeMenuModelValue(item.key as string, item)
} }
const getScrollElement = () => { const getScrollElement = () => {
@ -430,7 +430,7 @@ const MenuTag = defineComponent({
return { return {
modelMenuTagOptions, modelMenuTagOptions,
menuModelValueChange, changeMenuModelValue,
closeCurrentMenuTag, closeCurrentMenuTag,
menuKey, menuKey,
handleTagClick, handleTagClick,

View File

@ -29,14 +29,14 @@ const Breadcrumb = defineComponent({
setup() { setup() {
const menuStore = useMenu() const menuStore = useMenu()
const { menuModelValueChange } = menuStore const { changeMenuModelValue } = menuStore
const modelBreadcrumbOptions = computed(() => menuStore.breadcrumbOptions) const modelBreadcrumbOptions = computed(() => menuStore.breadcrumbOptions)
const handleDropdownSelect = ( const handleDropdownSelect = (
key: string | number, key: string | number,
option: DropdownOption, option: DropdownOption,
) => { ) => {
menuModelValueChange(key, option) changeMenuModelValue(key, option)
} }
return { return {

View File

@ -35,7 +35,7 @@ const GlobalSeach = defineComponent({
setup(props, { emit }) { setup(props, { emit }) {
const menuStore = useMenu() const menuStore = useMenu()
const { menuModelValueChange } = menuStore const { changeMenuModelValue } = menuStore
const modelShow = computed({ const modelShow = computed({
get: () => props.show, get: () => props.show,
set: (val) => { set: (val) => {
@ -117,7 +117,7 @@ const GlobalSeach = defineComponent({
} else { } else {
modelShow.value = false modelShow.value = false
menuModelValueChange(option.key as string, option) changeMenuModelValue(option.key as string, option)
} }
} }

View File

@ -11,7 +11,7 @@ import {
} from 'naive-ui' } from 'naive-ui'
import ThemeSwitch from '@/layout/components/SiderBar/components/SettingDrawer/components/ThemeSwitch/index' import ThemeSwitch from '@/layout/components/SiderBar/components/SettingDrawer/components/ThemeSwitch/index'
import { APP_THEME_COLOR } from '@/appConfig/designConfig' import { APP_THEME } from '@/appConfig/designConfig'
import { useSetting } from '@/store' import { useSetting } from '@/store'
import { useI18n } from '@/locales/useI18n' import { useI18n } from '@/locales/useI18n'
@ -86,7 +86,7 @@ const SettingDrawer = defineComponent({
{t('headerSettingOptions.ThemeOptions.PrimaryColorConfig')} {t('headerSettingOptions.ThemeOptions.PrimaryColorConfig')}
</NDivider> </NDivider>
<NColorPicker <NColorPicker
swatches={APP_THEME_COLOR} swatches={APP_THEME.APP_THEME_COLOR}
v-model:value={this.primaryColorOverride.common!.primaryColor} v-model:value={this.primaryColorOverride.common!.primaryColor}
onUpdateValue={this.changePrimaryColor.bind(this)} onUpdateValue={this.changePrimaryColor.bind(this)}
/> />

View File

@ -26,7 +26,7 @@ const FooterWrapper = defineComponent({
return this.copyright ? ( return this.copyright ? (
<div class="layout-footer-wrapper">{this.copyright}</div> <div class="layout-footer-wrapper">{this.copyright}</div>
) : ( ) : (
<></> ''
) )
}, },
}) })

View File

@ -19,10 +19,7 @@ import ContentWrapper from '@/layout/default/ContentWrapper'
import FooterWrapper from '@/layout/default/FooterWrapper' import FooterWrapper from '@/layout/default/FooterWrapper'
import { useSetting, useMenu } from '@/store' import { useSetting, useMenu } from '@/store'
import { import { LAYOUT_CONTENT_REF } from '@/appConfig/routerConfig'
viewScrollContainerId,
LAYOUT_CONTENT_REF,
} from '@/appConfig/routerConfig'
import { layoutHeaderCssVars } from '@/layout/layoutResize' import { layoutHeaderCssVars } from '@/layout/layoutResize'
import useAppLockScreen from '@/components/AppComponents/AppLockScreen/appLockVar' import useAppLockScreen from '@/components/AppComponents/AppLockScreen/appLockVar'
@ -33,19 +30,18 @@ const Layout = defineComponent({
const layoutMenuTagRef = ref<HTMLElement>() const layoutMenuTagRef = ref<HTMLElement>()
const settingStore = useSetting() const settingStore = useSetting()
const menuStore = useMenu()
const { height: windowHeight } = useWindowSize() const { height: windowHeight } = useWindowSize()
const { menuTagSwitch: modelMenuTagSwitch } = storeToRefs(settingStore) const { menuTagSwitch: modelMenuTagSwitch } = storeToRefs(settingStore)
const { setupAppRoutes } = menuStore
const { getLockAppScreen } = useAppLockScreen() const { getLockAppScreen } = useAppLockScreen()
const cssVarsRef = layoutHeaderCssVars([ const cssVarsRef = layoutHeaderCssVars([
layoutSiderBarRef, layoutSiderBarRef,
layoutMenuTagRef, layoutMenuTagRef,
]) ])
const { setupAppMenu } = useMenu()
nextTick().then(() => { nextTick().then(() => {
setupAppRoutes() setupAppMenu()
}) })
return { return {
@ -78,7 +74,6 @@ const Layout = defineComponent({
ref="LAYOUT_CONTENT_REF" ref="LAYOUT_CONTENT_REF"
class="layout-content__router-view" class="layout-content__router-view"
nativeScrollbar={false} nativeScrollbar={false}
{...{ id: viewScrollContainerId }}
> >
<ContentWrapper /> <ContentWrapper />
<FooterWrapper /> <FooterWrapper />

View File

@ -17,14 +17,14 @@
#### 拓展方法 #### 拓展方法
- 配置语言包文件(文件名为语言包名称) - 配置语言包文件(文件名为语言包名称)
- 配置文件入口(使用 `mergeMessage` 方法进行自动合并处理) - 配置文件入口(使用 `combineI18nMessages` 方法进行自动合并处理)
- 语言包名称应该全局唯一 - 语言包名称应该全局唯一
### helper 文件说明 ### helper 文件说明
- `getAppLocales` 获取系统所有语言包(该方法强制依赖 LOCAL_OPTIONS key 配置意味着你在配置语言包的时候key 必须与 `src/locales/lang/xxx.ts` 一一对应匹配) - `getAppLocalMessages` 获取系统所有语言包(该方法强制依赖 LOCAL_OPTIONS key 配置意味着你在配置语言包的时候key 必须与 `src/locales/lang/xxx.ts` 一一对应匹配)
- `naiveLocales` 获取 `naive-ui` 组件库对应语言包文件 - `naiveLocales` 获取 `naive-ui` 组件库对应语言包文件
- `getDefaultLocal` 获取系统当前默认语言包 - `getAppDefaultLanguage` 获取系统当前默认语言包
### useI18n 文件说明 ### useI18n 文件说明

View File

@ -12,8 +12,8 @@
/** /**
* *
* : * :
* - mergeMessage: 合并对应文件下语言包 * - combineI18nMessages: 合并对应文件下语言包
* - getAppLocales: 获取所有语言 * - getAppLocalMessages: 获取所有语言
*/ */
import { set } from 'lodash-es' import { set } from 'lodash-es'
@ -27,6 +27,7 @@ import type {
AppLocalesModules, AppLocalesModules,
AppLocalesDropdownMixedOption, AppLocalesDropdownMixedOption,
CurrentAppMessages, CurrentAppMessages,
I18nModules,
} from '@/locales/type' } from '@/locales/type'
/** /**
@ -36,13 +37,12 @@ import type {
* *
* @remark , prefix * @remark , prefix
*/ */
// eslint-disable-next-line @typescript-eslint/no-explicit-any export const combineI18nMessages = (langs: I18nModules, prefix: string) => {
export const mergeMessage = (langs: Record<string, any>, prefix: string) => { if (typeof prefix !== 'string' || !prefix.trim()) {
if (!prefix) { throw new Error('Expected prefix to be a non-empty string')
throw new Error('Expected prefix to be string, got undefined instead')
} }
const langsGather: Recordable = {} const langsGather: Record<string, Recordable> = {}
Object.keys(langs).forEach((key) => { Object.keys(langs).forEach((key) => {
const langFileModule = langs[key].default const langFileModule = langs[key].default
@ -70,13 +70,13 @@ export const mergeMessage = (langs: Record<string, any>, prefix: string) => {
} }
/** 获取所有语言 */ /** 获取所有语言 */
export const getAppLocales = async ( export const getAppLocalMessages = async (
LOCAL_OPTIONS: AppLocalesDropdownMixedOption[], LOCAL_OPTIONS: AppLocalesDropdownMixedOption[],
) => { ) => {
const message = {} as CurrentAppMessages const message = {} as CurrentAppMessages
for (const curr of LOCAL_OPTIONS) { for (const curr of LOCAL_OPTIONS) {
const msg = (await import(`./lang/${curr.key}.ts`)) as AppLocalesModules const msg: AppLocalesModules = await import(`./lang/${curr.key}.ts`)
const key = curr.key const key = curr.key
if (key) { if (key) {
@ -125,13 +125,11 @@ export const naiveLocales = (key: string) => {
* *
* @remak , `main.ts` , `i18n` * @remak , `main.ts` , `i18n`
*/ */
export const getDefaultLocal = () => { export const getAppDefaultLanguage = () => {
const catchLanguage = getCache<string>( const language = getCache<string>(
APP_CATCH_KEY.localeLanguage, APP_CATCH_KEY.localeLanguage,
'localStorage', 'localStorage',
) )
const locale = catchLanguage ? catchLanguage : SYSTEM_DEFAULT_LOCAL return language ? language : SYSTEM_DEFAULT_LOCAL
return locale
} }

View File

@ -25,9 +25,9 @@
import { createI18n } from 'vue-i18n' import { createI18n } from 'vue-i18n'
import { LOCAL_OPTIONS } from '@/appConfig/localConfig' import { LOCAL_OPTIONS } from '@/appConfig/localConfig'
import { getDefaultLocal } from '@/locales/helper' import { getAppDefaultLanguage } from '@/locales/helper'
import { getAppLocales } from '@/locales/helper' import { getAppLocalMessages } from '@/locales/helper'
import type { App } from 'vue' import type { App } from 'vue'
import type { I18n, I18nOptions } from 'vue-i18n' import type { I18n, I18nOptions } from 'vue-i18n'
@ -37,8 +37,8 @@ export let i18n: I18n
/** 创建 i18n 实例 */ /** 创建 i18n 实例 */
const createI18nOptions = async () => { const createI18nOptions = async () => {
const locale = getDefaultLocal() const locale = getAppDefaultLanguage()
const messages = await getAppLocales(LOCAL_OPTIONS) const messages = await getAppLocalMessages(LOCAL_OPTIONS)
const i18nInstance = createI18n({ const i18nInstance = createI18n({
legacy: false, legacy: false,

View File

@ -1,11 +1,13 @@
import { mergeMessage } from '@/locales/helper' import { combineI18nMessages } from '@/locales/helper'
const modules = import.meta.glob('./en-US/**/*.json', { import type { I18nModules } from '@/locales/type'
const modules: I18nModules = import.meta.glob('./en-US/**/*.json', {
eager: true, eager: true,
}) })
export default { export default {
message: { message: {
...mergeMessage(modules, 'en-US'), ...combineI18nMessages(modules, 'en-US'),
}, },
} }

View File

@ -1,11 +1,13 @@
import { mergeMessage } from '@/locales/helper' import { combineI18nMessages } from '@/locales/helper'
const modules = import.meta.glob('./zh-CN/**/*.json', { import type { I18nModules } from '@/locales/type'
const modules: I18nModules = import.meta.glob('./zh-CN/**/*.json', {
eager: true, eager: true,
}) })
export default { export default {
message: { message: {
...mergeMessage(modules, 'zh-CN'), ...combineI18nMessages(modules, 'zh-CN'),
}, },
} }

View File

@ -4,6 +4,7 @@ import type {
DropdownDividerOption, DropdownDividerOption,
DropdownRenderOption, DropdownRenderOption,
} from 'naive-ui' } from 'naive-ui'
import type { Recordable } from '@/types/modules/helper'
export interface CurrentAppMessages { export interface CurrentAppMessages {
'zh-CN': object 'zh-CN': object
@ -21,3 +22,5 @@ export interface AppLocalesModules {
message: UnknownObjectKey message: UnknownObjectKey
} }
} }
export type I18nModules = Record<string, { default: Recordable }>

View File

@ -30,6 +30,7 @@ export const useI18n = (namespace?: string) => {
return (t as any)(getI18nKey(namespace, key), ...args) return (t as any)(getI18nKey(namespace, key), ...args)
} }
/** 重写 locale 方法 */
const overrideLocaleFunc = (lang: string) => { const overrideLocaleFunc = (lang: string) => {
const localeRef = locale as WritableComputedRef<string> const localeRef = locale as WritableComputedRef<string>
@ -50,4 +51,4 @@ export const useI18n = (namespace?: string) => {
* *
* t path * t path
*/ */
export const t = <T = unknown>(key: T) => key export const t = (key: string) => key

View File

@ -9,7 +9,7 @@
* @remark * @remark
*/ */
import type { AppRouteRecordRaw, AutoImportRouteModule } from '@/router/type' import type { AppRouteRecordRaw, RouteModules } from '@/router/type'
/** /**
* *
@ -17,15 +17,21 @@ import type { AppRouteRecordRaw, AutoImportRouteModule } from '@/router/type'
* *
* @remark , ts route module views * @remark , ts route module views
*/ */
export const autoMergeRoute = () => { export const combineRawRouteModules = () => {
const modulesFiles = import.meta.glob('../modules/**/*.ts', { const modulesFiles: RouteModules = import.meta.glob('../modules/**/*.ts', {
eager: true, eager: true,
}) })
const modules = Object.keys(modulesFiles).reduce((modules, modulePath) => { const modules = Object.keys(modulesFiles).reduce((modules, modulePath) => {
const value = modulesFiles[modulePath] as AutoImportRouteModule const route = modulesFiles[modulePath].default
modules.push(value.default) if (route) {
modules.push(route)
} else {
throw new Error(
'router helper combine: an exception occurred while parsing the routing file!',
)
}
return modules return modules
}, [] as AppRouteRecordRaw[]) }, [] as AppRouteRecordRaw[])

View File

@ -141,7 +141,6 @@ export const redirectRouterToDashboard = (isReplace = true) => {
const { path } = ROOT_ROUTE const { path } = ROOT_ROUTE
setCache('menuKey', path) setCache('menuKey', path)
console.log('path', path)
isReplace ? push(path) : replace(path) isReplace ? push(path) : replace(path)
} }

View File

@ -13,7 +13,7 @@ export let router: Router
const createVueRouter = () => { const createVueRouter = () => {
return createRouter({ return createRouter({
history: createWebHashHistory(), history: createWebHashHistory(),
routes: constantRoutes as unknown as RouteRecordRaw[], routes: constantRoutes() as unknown as RouteRecordRaw[],
scrollBehavior: (to) => { scrollBehavior: (to) => {
scrollViewToTop(to) scrollViewToTop(to)

View File

@ -20,11 +20,8 @@
* order , * order ,
*/ */
import { autoMergeRoute } from '@/router/helper/merge' import { combineRawRouteModules } from '@/router/helper/combine'
import { orderRoutes } from '@/router/helper/orderRoutes' import { orderRoutes } from '@/router/helper/orderRoutes'
import type { AppRouteRecordRaw } from '@/router/type' /** 获取所有被合并与排序的路由 */
export const getAppRawRoutes = () => orderRoutes(combineRawRouteModules())
const routes: AppRouteRecordRaw[] = orderRoutes(autoMergeRoute())
export default routes

View File

@ -1,11 +1,11 @@
import Layout from '@/layout/index' import Layout from '@/layout/index'
import childrenRoutes from './routeModules' import { getAppRawRoutes } from './routeModules'
import { ROOT_ROUTE } from '@/appConfig/appConfig' import { ROOT_ROUTE } from '@/appConfig/appConfig'
import { expandRoutes } from '@/router/helper/expandRoutes' import { expandRoutes } from '@/router/helper/expandRoutes'
const { path } = ROOT_ROUTE const { path } = ROOT_ROUTE
export default [ export default () => [
{ {
path: '/', path: '/',
name: 'login', name: 'login',
@ -16,7 +16,7 @@ export default [
name: 'layout', name: 'layout',
redirect: path, redirect: path,
component: Layout, component: Layout,
children: expandRoutes(childrenRoutes), children: expandRoutes(getAppRawRoutes()),
}, },
{ {
path: '/:catchAll(.*)', path: '/:catchAll(.*)',

View File

@ -32,6 +32,8 @@ export interface AppRouteRecordRaw extends Omit<RouteRecordRaw, 'meta'> {
fullPath?: string fullPath?: string
} }
export interface AutoImportRouteModule extends Object { export interface RouteModules {
[propName: string]: {
default: AppRouteRecordRaw default: AppRouteRecordRaw
}
} }

View File

@ -11,7 +11,7 @@
/** 本方法感谢 <https://yunkuangao.me/> 的支持 */ /** 本方法感谢 <https://yunkuangao.me/> 的支持 */
import { MENU_COLLAPSED_CONFIG, ROOT_ROUTE } from '@/appConfig/appConfig' import { APP_MENU_CONFIG, ROOT_ROUTE } from '@/appConfig/appConfig'
import RayIcon from '@/components/RayIcon/index' import RayIcon from '@/components/RayIcon/index'
import { isValueType } from '@/utils/hook' import { isValueType } from '@/utils/hook'
import { getCache, setCache } from '@/utils/cache' import { getCache, setCache } from '@/utils/cache'
@ -31,12 +31,20 @@ import type {
* *
* @remark * @remark
*/ */
const check = ( const isMatch = (
node: AppMenuOption, node: AppMenuOption,
key: string | number, key: string | number,
value: string | number, value: string | number,
) => { ) => {
return node[key] === value || node.key === value if (!node || typeof node !== 'object') {
return false
}
if (node[key] === value) {
return true
}
return false
} }
/** /**
@ -47,7 +55,7 @@ const check = (
* *
* @remark * @remark
*/ */
const process = ( const findMatchingNodes = (
options: AppMenuOption, options: AppMenuOption,
key: string | number, key: string | number,
value: string | number, value: string | number,
@ -55,7 +63,7 @@ const process = (
const temp: AppMenuOption[] = [] const temp: AppMenuOption[] = []
// 检查当前节点是否匹配值 // 检查当前节点是否匹配值
if (check(options, key, value)) { if (isMatch(options, key, value)) {
temp.push(options) temp.push(options)
return temp return temp
@ -65,7 +73,7 @@ const process = (
if (options.children && options.children.length > 0) { if (options.children && options.children.length > 0) {
for (const it of options.children) { for (const it of options.children) {
// 子节点递归调用 // 子节点递归调用
const innerTemp = process(it, key, value) const innerTemp = findMatchingNodes(it, key, value)
// 如果子节点匹配到了,则将当前节点加入数组 // 如果子节点匹配到了,则将当前节点加入数组
if (innerTemp.length > 0) { if (innerTemp.length > 0) {
@ -83,7 +91,7 @@ const process = (
* @param key * @param key
* @param value * @param value
*/ */
export const parse = ( export const parseAndFindMatchingNodes = (
options: AppMenuOption[], options: AppMenuOption[],
key: string | number, key: string | number,
value: string | number, value: string | number,
@ -91,7 +99,7 @@ export const parse = (
const temp = [] const temp = []
for (const it of options) { for (const it of options) {
const innerTemp = process(it, key, value) const innerTemp = findMatchingNodes(it, key, value)
if (innerTemp.length > 0) { if (innerTemp.length > 0) {
temp.push(...innerTemp) temp.push(...innerTemp)
@ -155,7 +163,7 @@ export const hasMenuIcon = (option: AppMenuOption) => {
RayIcon, RayIcon,
{ {
name: meta!.icon as string, name: meta!.icon as string,
size: MENU_COLLAPSED_CONFIG.MENU_COLLAPSED_ICON_SIZE, size: APP_MENU_CONFIG.MENU_COLLAPSED_ICON_SIZE,
}, },
{}, {},
) )

View File

@ -27,20 +27,21 @@ import { NEllipsis } from 'naive-ui'
import { getCache, setCache } from '@/utils/cache' import { getCache, setCache } from '@/utils/cache'
import { validMenuItemShow } from '@/router/helper/routerCopilot' import { validMenuItemShow } from '@/router/helper/routerCopilot'
import { import {
parse, parseAndFindMatchingNodes,
matchMenuOption, matchMenuOption,
updateDocumentTitle, updateDocumentTitle,
hasMenuIcon, hasMenuIcon,
getCatchMenuKey, getCatchMenuKey,
} from './helper' } from './helper'
import { useI18n } from '@/locales/useI18n' import { useI18n } from '@/locales/useI18n'
import routeModules from '@/router/routeModules' import { getAppRawRoutes } from '@/router/routeModules'
import { useKeepAlive } from '@/store' import { useKeepAlive } from '@/store'
import { useVueRouter } from '@/router/helper/useVueRouter' import { useVueRouter } from '@/router/helper/useVueRouter'
import type { MenuOption } from 'naive-ui' import type { MenuOption } from 'naive-ui'
import type { AppRouteMeta } from '@/router/type' import type { AppRouteMeta, AppRouteRecordRaw } from '@/router/type'
import type { AppMenuOption, MenuTagOptions } from '@/types/modules/app' import type { AppMenuOption, MenuTagOptions } from '@/types/modules/app'
import type { MenuState } from '@/store/modules/menu/type'
export const useMenu = defineStore( export const useMenu = defineStore(
'menu', 'menu',
@ -50,12 +51,12 @@ export const useMenu = defineStore(
const { t } = useI18n() const { t } = useI18n()
const { setKeepAliveInclude } = useKeepAlive() const { setKeepAliveInclude } = useKeepAlive()
const menuState = reactive({ const menuState = reactive<MenuState>({
menuKey: getCatchMenuKey(), // 当前菜单 `key` menuKey: getCatchMenuKey(), // 当前菜单 `key`
options: [] as AppMenuOption[], // 菜单列表 options: [], // 菜单列表
collapsed: false, // 是否折叠菜单 collapsed: false, // 是否折叠菜单
menuTagOptions: [] as MenuTagOptions[], // tag 标签菜单 menuTagOptions: [], // tag 标签菜单
breadcrumbOptions: [] as AppMenuOption[], // 面包屑菜单 breadcrumbOptions: [], // 面包屑菜单
}) })
/** /**
@ -69,21 +70,21 @@ export const useMenu = defineStore(
options: AppMenuOption[], options: AppMenuOption[],
key: string | number, key: string | number,
) => { ) => {
const ops = parse(options, 'key', key) const ops = parseAndFindMatchingNodes(options, 'key', key)
return ops return ops
} }
/** /**
* *
* @param key `key` * @param key key
* @param item `item` * @param option option
* *
* @remark `menu key` * @remark `menu key`
* @remark , key (router push ) * @remark , key (router push )
*/ */
const menuModelValueChange = (key: string | number, item: MenuOption) => { const changeMenuModelValue = (key: string | number, option: MenuOption) => {
const meta = item.meta as AppRouteMeta const { meta, path } = option as unknown as AppRouteRecordRaw
if (meta.windowOpen) { if (meta.windowOpen) {
window.open(meta.windowOpen) window.open(meta.windowOpen)
@ -91,26 +92,30 @@ export const useMenu = defineStore(
// 防止重复点击做重复操作处理 // 防止重复点击做重复操作处理
if (menuState.menuKey !== key) { if (menuState.menuKey !== key) {
matchMenuOption( matchMenuOption(
item as unknown as MenuTagOptions, option as unknown as MenuTagOptions,
menuState.menuKey, menuState.menuKey,
menuState.menuTagOptions, menuState.menuTagOptions,
) )
updateDocumentTitle(item as unknown as AppMenuOption) updateDocumentTitle(option as unknown as AppMenuOption)
setKeepAliveInclude(item as unknown as AppMenuOption) setKeepAliveInclude(option as unknown as AppMenuOption)
menuState.breadcrumbOptions = parse(menuState.options, 'key', key) // 获取面包屑 menuState.breadcrumbOptions = parseAndFindMatchingNodes(
menuState.options,
'key',
key,
) // 获取面包屑
/** 是否为根路由 */ /** 是否为根路由 */
if (key[0] !== '/') { if (!String(key).startsWith('/')) {
/** 如果不是根路由, 则拼接完整路由并跳转 */ /** 如果不是根路由, 则拼接完整路由并跳转 */
const path = getCompleteRoutePath(menuState.options, key) const _path = getCompleteRoutePath(menuState.options, key)
.map((curr) => curr.key) .map((curr) => curr.key)
.join('/') .join('/')
router.push(path) router.push(_path)
} else { } else {
/** 根路由直接跳转 */ /** 根路由直接跳转 */
router.push(item.path as string) router.push(path)
} }
menuState.menuKey = key menuState.menuKey = key
@ -136,7 +141,7 @@ export const useMenu = defineStore(
} }
if (path === i.path) { if (path === i.path) {
menuModelValueChange(i.path, i) changeMenuModelValue(i.path, i)
break break
} }
@ -168,7 +173,8 @@ export const useMenu = defineStore(
* @remark , * @remark ,
* @remark , * @remark ,
*/ */
const setupAppRoutes = () => { const setupAppMenu = () => {
return new Promise<void>((resolve) => {
const resolveOption = (option: AppMenuOption) => { const resolveOption = (option: AppMenuOption) => {
const { meta } = option const { meta } = option
@ -222,16 +228,22 @@ export const useMenu = defineStore(
} }
/** 缓存菜单列表 */ /** 缓存菜单列表 */
menuState.options = resolveRoutes(routeModules as AppMenuOption[], 0) menuState.options = resolveRoutes(
getAppRawRoutes() as AppMenuOption[],
0,
)
resolve()
/** 初始化后渲染面包屑 */ /** 初始化后渲染面包屑 */
nextTick(() => { nextTick(() => {
menuState.breadcrumbOptions = parse( menuState.breadcrumbOptions = parseAndFindMatchingNodes(
menuState.options, menuState.options,
'key', 'key',
menuState.menuKey as string, menuState.menuKey as string,
) )
}) })
})
} }
/** /**
@ -275,8 +287,8 @@ export const useMenu = defineStore(
return { return {
...toRefs(menuState), ...toRefs(menuState),
menuModelValueChange, changeMenuModelValue,
setupAppRoutes, setupAppMenu,
collapsedMenu, collapsedMenu,
spliceMenTagOptions, spliceMenTagOptions,
emptyMenuTagOptions, emptyMenuTagOptions,

View File

@ -0,0 +1,13 @@
import type {
AppMenuOption,
MenuTagOptions,
AppMenuKey,
} from '@/types/modules/app'
export interface MenuState {
menuKey: AppMenuKey
options: AppMenuOption[]
collapsed: boolean
menuTagOptions: MenuTagOptions[]
breadcrumbOptions: AppMenuOption[]
}

View File

@ -1,9 +1,9 @@
import { getDefaultLocal } from '@/locales/helper' import { getAppDefaultLanguage } from '@/locales/helper'
import { setCache } from '@use-utils/cache' import { setCache } from '@use-utils/cache'
import { set } from 'lodash-es' import { set } from 'lodash-es'
import { addClass, removeClass, colorToRgba } from '@/utils/element' import { addClass, removeClass, colorToRgba } from '@/utils/element'
import { useI18n } from '@/locales/useI18n' import { useI18n } from '@/locales/useI18n'
import { APP_NAIVE_UI_THEME_OVERRIDES } from '@/appConfig/designConfig' import { APP_THEME } from '@/appConfig/designConfig'
import { useDayjs } from '@/dayjs/index' import { useDayjs } from '@/dayjs/index'
import type { ConditionalPick } from '@/types/modules/helper' import type { ConditionalPick } from '@/types/modules/helper'
@ -22,7 +22,7 @@ export const useSetting = defineStore(
const settingState = reactive<SettingState>({ const settingState = reactive<SettingState>({
drawerPlacement: 'right', drawerPlacement: 'right',
primaryColorOverride: { primaryColorOverride: {
...APP_NAIVE_UI_THEME_OVERRIDES, ...APP_THEME.APP_NAIVE_UI_THEME_OVERRIDES,
common: { common: {
primaryColor: primaryColor, // 主题色 primaryColor: primaryColor, // 主题色
primaryColorHover: primaryColor, primaryColorHover: primaryColor,
@ -34,7 +34,7 @@ export const useSetting = defineStore(
spinSwitch: false, // 全屏加载 spinSwitch: false, // 全屏加载
invertSwitch: false, // 反转色模式 invertSwitch: false, // 反转色模式
breadcrumbSwitch: true, // 面包屑开关 breadcrumbSwitch: true, // 面包屑开关
localeLanguage: getDefaultLocal(), localeLanguage: getAppDefaultLanguage(),
lockScreenSwitch: false, // 锁屏开关 lockScreenSwitch: false, // 锁屏开关
lockScreenInputSwitch: false, // 锁屏输入状态开关(预留该字段是为了方便拓展用, 但是舍弃了该字段, 改为使用 useAppLockScreen 方法) lockScreenInputSwitch: false, // 锁屏输入状态开关(预留该字段是为了方便拓展用, 但是舍弃了该字段, 改为使用 useAppLockScreen 方法)
}) })

View File

@ -2,11 +2,12 @@ import type { CreateAxiosDefaults } from 'axios'
export type CollapsedMode = 'transform' | 'width' export type CollapsedMode = 'transform' | 'width'
export interface MenuCollapsedConfig { export interface AppMenuConfig {
MENU_COLLAPSED_WIDTH: number MENU_COLLAPSED_WIDTH: number
MENU_COLLAPSED_MODE: CollapsedMode MENU_COLLAPSED_MODE: CollapsedMode
MENU_COLLAPSED_ICON_SIZE: number MENU_COLLAPSED_ICON_SIZE: number
MENU_COLLAPSED_INDENT: number MENU_COLLAPSED_INDENT: number
MENU_ACCORDION: boolean
} }
export interface AppKeepAlive { export interface AppKeepAlive {

View File

@ -6,6 +6,7 @@ import type {
UserConfigExport, UserConfigExport,
} from 'vite' } from 'vite'
import type { Recordable } from '@/types/modules/helper' import type { Recordable } from '@/types/modules/helper'
import type { GlobalThemeOverrides } from 'naive-ui'
export interface LayoutSideBarLogo { export interface LayoutSideBarLogo {
icon?: string icon?: string
@ -73,3 +74,9 @@ export interface AppConfig {
} }
export type AppConfigExport = Config & UserConfigExport export type AppConfigExport = Config & UserConfigExport
export interface AppTheme {
APP_THEME_COLOR: string[]
APP_PRIMARY_COLOR: AppPrimaryColor
APP_NAIVE_UI_THEME_OVERRIDES: GlobalThemeOverrides
}

View File

@ -1,5 +1,5 @@
import { isValueType } from '@use-utils/hook' import { isValueType } from '@use-utils/hook'
import { ELEMENT_UNIT } from '@/appConfig/regConfig' import { APP_REGEX } from '@/appConfig/regConfig'
import type { EventListenerOrEventListenerObject } from '@/types/modules/utils' import type { EventListenerOrEventListenerObject } from '@/types/modules/utils'
@ -132,9 +132,9 @@ export const hasClass = (element: HTMLElement, className: string) => {
* addStyle(styles) * addStyle(styles)
* ``` * ```
*/ */
export const addStyle = ( export const addStyle = <K extends keyof CSSStyleDeclaration & string>(
el: HTMLElement, el: HTMLElement,
styles: string | Partial<CSSStyleDeclaration>, styles: K | Partial<CSSStyleDeclaration>,
) => { ) => {
if (el) { if (el) {
if (isValueType<object>(styles, 'Object')) { if (isValueType<object>(styles, 'Object')) {
@ -160,10 +160,13 @@ export const addStyle = (
* @param el Target element dom * @param el Target element dom
* @param styles * @param styles
*/ */
export const removeStyle = (el: HTMLElement, styles: string[]) => { export const removeStyle = <K extends keyof CSSStyleDeclaration & string>(
el: HTMLElement,
styles: K[],
) => {
if (el) { if (el) {
styles.forEach((item) => { styles.forEach((curr) => {
el.style[item] = null el.style.removeProperty(curr)
}) })
} }
} }
@ -259,7 +262,10 @@ export const getElement = <T extends Element>(element: string) => {
export const completeSize = (size: number | string, unit = 'px') => { export const completeSize = (size: number | string, unit = 'px') => {
if (typeof size === 'number') { if (typeof size === 'number') {
return size.toString() + unit return size.toString() + unit
} else if (isValueType<string>(size, 'String') && ELEMENT_UNIT.test(size)) { } else if (
isValueType<string>(size, 'String') &&
APP_REGEX.validerCSSUnit.test(size)
) {
return size return size
} else { } else {
return size + unit return size + unit