This commit is contained in:
ray_wuhao 2023-06-28 16:01:58 +08:00
parent 5daa7d67a4
commit 6498898a43
67 changed files with 303 additions and 539 deletions

1
.yarnrc.yml Normal file
View File

@ -0,0 +1 @@
nodeLinker: node-modules

View File

@ -1,5 +1,23 @@
# CHANGE LOG
## 4.0.0
### Feats
- 重构 types 包设计,现在的类型包更加清晰
- 重构 utils 包设计,该包下的所有 hook 提供了更加友好的类型提示
- RayIframe 组件新增 lazy 属性
- 新增 v-disabled 指令
- demo 页面展示优化
### Fixes
- 修复一些已知的 bug
### 补充
> 这次花了一点时间,将模板进行重新梳理,进行了一些很大的破坏性更新改动。核心重点是 types 包与 utils 包的重大更新。不过只是做了初步的一些大方向的更新,后续的细节更新还在继续。。。
## 3.3.7
### Feats

View File

@ -8,6 +8,8 @@ import { get } from 'lodash-es'
import { useSetting } from '@/store'
import { addClass, removeClass, addStyle, colorToRgba } from '@/utils/element'
import type { SettingState } from '@/store/modules/setting/type'
const App = defineComponent({
name: 'App',
setup() {
@ -22,12 +24,15 @@ const App = defineComponent({
} = __APP_CFG__ // 默认主题色
const body = document.body
const primaryColorOverride = getCache('piniaSettingStore', 'localStorage')
const primaryColorOverride = getCache<SettingState>(
'piniaSettingStore',
'localStorage',
)
const _p = get(
primaryColorOverride,
'primaryColorOverride.common.primaryColor',
)
const _fp = colorToRgba(_p, 0.3)
const _fp = colorToRgba(_p || primaryColor, 0.3)
/** 设置全局主题色 css 变量 */
body.style.setProperty('--ray-theme-primary-color', _p || primaryColor)

View File

@ -15,8 +15,11 @@ import type {
LayoutSideBarLogo,
PreloadingConfig,
RootRoute,
} from '@/types/cfg'
import type { MenuCollapsedConfig, AppKeepAlive } from '@/types/appConfig'
} from '@/types/modules/cfg'
import type {
MenuCollapsedConfig,
AppKeepAlive,
} from '@/types/modules/appConfig'
/**
*

View File

@ -11,7 +11,7 @@
/** 系统颜色风格配置入口 */
import type { AppPrimaryColor } from '@/types/cfg'
import type { AppPrimaryColor } from '@/types/modules/cfg'
import type { GlobalThemeOverrides } from 'naive-ui'
/**

View File

@ -24,6 +24,8 @@
import useRequest from '@/axios/instance'
import type { AxiosResponseBody } from '@/types/modules/axios'
interface AxiosTestResponse extends UnknownObjectKey {
data: UnknownObjectKey[]
city?: string

View File

@ -30,6 +30,7 @@ import type {
ErrorImplementQueue,
FetchType,
} from '@/axios/type'
import type { AnyFunc } from '@/types/modules/utils'
/** 当前请求的实例 */
const axiosFetchInstance = {

View File

@ -39,7 +39,7 @@ const { setImplement } = useAxiosInterceptor()
* request instance ,
*/
const requestHeaderToken = (ins: RequestInterceptorConfig, mode: string) => {
const token = getCache(APP_CATCH_KEY.token)
const token = getCache<string>(APP_CATCH_KEY.token)
if (ins.url) {
// TODO: 根据 url 不同是否设置 token

View File

@ -8,6 +8,7 @@ import type {
InternalAxiosRequestConfig,
AxiosResponse,
} from 'axios'
import type { AnyFunc } from '@/types/modules/utils'
export type AxiosHeaderValue =
| AxiosHeaders

View File

@ -27,6 +27,7 @@ import { getCache } from '@/utils/cache'
import type { PropType } from 'vue'
import type { AvatarProps, SpaceProps } from 'naive-ui'
import type { SigninCallback } from '@/store/modules/signin/type'
const AppAvatar = defineComponent({
name: 'AppAvatar',
@ -47,7 +48,7 @@ const AppAvatar = defineComponent({
},
},
setup(props) {
const signin = getCache(APP_CATCH_KEY.signin)
const signin = getCache<SigninCallback>(APP_CATCH_KEY.signin)
const cssVars = computed(() => {
const vars = {
'--app-avatar-cursor': props.cursor,
@ -73,12 +74,12 @@ const AppAvatar = defineComponent({
<NAvatar
// eslint-disable-next-line prettier/prettier, @typescript-eslint/no-explicit-any
{...(this.$props as any)}
src={this.signin.avatar}
src={this.signin?.avatar}
objectFit="cover"
round
size={this.avatarSize}
/>
<div class="app-avatar__name">{this.signin.name}</div>
<div class="app-avatar__name">{this.signin?.name}</div>
</NSpace>
)
},

View File

@ -43,6 +43,8 @@ import { cloneDeep, debounce } from 'lodash-es'
import { on, off, addStyle, completeSize } from '@/utils/element'
import type { PropType } from 'vue'
import type { EChartsInstance } from '@/types/modules/component'
import type { AnyFunc } from '@/types/modules/utils'
export type AutoResize =
| boolean

View File

@ -93,6 +93,11 @@ const RayIframe = defineComponent({
type: Object as PropType<SpinProps>,
default: () => ({}),
},
lazy: {
/** 是否延迟加载 iframe */
type: Boolean,
default: true,
},
},
setup(props, { expose }) {
const cssVars = computed(() => {
@ -160,6 +165,9 @@ const RayIframe = defineComponent({
allow={this.allow}
name={this.name}
title={this.title}
{...{
loading: this.lazy ? 'lazy' : null,
}}
></iframe>
),
}}

View File

@ -15,6 +15,7 @@ import { NPopover, NCard } from 'naive-ui'
import RayIcon from '@/components/RayIcon/index'
import type { TableSettingProvider } from '@/components/RayTable/src/type'
import type { ComponentSize } from '@/types/modules/component'
const TableSize = defineComponent({
name: 'TableSize',

View File

@ -52,6 +52,7 @@ import type { WritableComputedRef } from 'vue'
import type { DropdownOption } from 'naive-ui'
import type { ExportExcelHeader } from '@use-utils/xlsx'
import type { DataTableInst } from 'naive-ui'
import type { ComponentSize } from '@/types/modules/component'
const RayTable = defineComponent({
name: 'RayTable',
@ -60,8 +61,8 @@ const RayTable = defineComponent({
setup(props, { emit, expose }) {
const rayTableInstance = ref<DataTableInst>()
const tableUUID = uuid() // 表格 id, 用于打印表格
const rayTableUUID = uuid() // RayTable id, 用于全屏表格
const tableUUID = uuid(16) // 表格 id, 用于打印表格
const rayTableUUID = uuid(16) // RayTable id, 用于全屏表格
const modelRightClickMenu = computed(() => props.rightClickMenu)
const modelColumns = computed({
get: () => props.columns,

View File

@ -7,6 +7,7 @@ import type {
DataTableInst,
} from 'naive-ui'
import type { ComputedRef, WritableComputedRef, VNode } from 'vue'
import type { ComponentSize } from '@/types/modules/component'
export interface ActionOptions extends DataTableBaseColumn {
leftFixedActivated?: boolean // 向左固定

View File

@ -19,6 +19,7 @@ import { on, off } from '@use-utils/element'
import type { Directive } from 'vue'
import type { DebounceBindingOptions } from './type'
import type { AnyFunc } from '@/types/modules/utils'
let debounceFunction: AnyFunc | null

View File

@ -1,4 +1,5 @@
import type { DebounceSettings } from 'lodash-es'
import type { AnyFunc } from '@/types/modules/utils'
export interface DebounceBindingOptions {
func: AnyFunc

View File

@ -19,6 +19,7 @@ import { on, off } from '@use-utils/element'
import type { Directive } from 'vue'
import type { ThrottleBindingOptions } from './type'
import type { AnyFunc } from '@/types/modules/utils'
let throttleFunction: AnyFunc | null

View File

@ -1,4 +1,5 @@
import type { ThrottleSettings } from 'lodash-es'
import type { AnyFunc } from '@/types/modules/utils'
export interface ThrottleBindingOptions {
func: AnyFunc

View File

@ -1,3 +1,6 @@
<svg t="1669090001868" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7911" width="200" height="200">
<path d="M918.954667 880.896c-0.618667-1.322667-154.688-334.378667-177.194667-382.421333-135.402667-288.917333-174.976-369.642667-196.821333-391.957334a31.829333 31.829333 0 0 0-13.013334-12.138666 32 32 0 0 0-42.944 14.293333L109.909333 865.706667a32 32 0 0 0 57.216 28.672l99.349334-198.421334h496.725333a49853.44 49853.44 0 0 1 97.536 211.605334 32.021333 32.021333 0 0 0 58.218667-26.666667zM521.002667 187.626667c39.850667 76.650667 126.698667 260.117333 212.458666 444.330666H298.517333L521.002667 187.626667z" fill="currentColor" p-id="7912"></path>
<svg t="1669090001868" class="icon" viewBox="0 0 1024 1024" version="1.1"
xmlns="http://www.w3.org/2000/svg" p-id="7911" width="200" height="200">
<path
d="M918.954667 880.896c-0.618667-1.322667-154.688-334.378667-177.194667-382.421333-135.402667-288.917333-174.976-369.642667-196.821333-391.957334a31.829333 31.829333 0 0 0-13.013334-12.138666 32 32 0 0 0-42.944 14.293333L109.909333 865.706667a32 32 0 0 0 57.216 28.672l99.349334-198.421334h496.725333a49853.44 49853.44 0 0 1 97.536 211.605334 32.021333 32.021333 0 0 0 58.218667-26.666667zM521.002667 187.626667c39.850667 76.650667 126.698667 260.117333 212.458666 444.330666H298.517333L521.002667 187.626667z"
fill="currentColor" p-id="7912"></path>
</svg>

Before

Width:  |  Height:  |  Size: 712 B

After

Width:  |  Height:  |  Size: 722 B

View File

@ -8,6 +8,7 @@ import { MENU_COLLAPSED_CONFIG, MENU_ACCORDION } from '@/appConfig/appConfig'
import { useVueRouter } from '@/router/helper/useVueRouter'
import type { MenuInst } from 'naive-ui'
import type { NaiveMenuOptions } from '@/types/modules/component'
const LayoutMenu = defineComponent({
name: 'LayoutMenu',

View File

@ -34,8 +34,6 @@ $menuTagWrapperWidth: 76px;
& .menu-tag__right-setting {
width: 28px;
height: 20px;
// display: inline-flex;
// align-items: center;
}
}
}

View File

@ -36,6 +36,7 @@ import { ROOT_ROUTE } from '@/appConfig/appConfig'
import { getElement } from '@use-utils/element'
import type { MenuOption, ScrollbarInst } from 'naive-ui'
import type { MenuTagOptions } from '@/types/modules/app'
const MenuTag = defineComponent({
name: 'MenuTag',
@ -148,7 +149,7 @@ const MenuTag = defineComponent({
disabled: false,
},
])
const scrollBarUUID = uuid()
const scrollBarUUID = uuid(16)
const actionMap = {
reloadCurrentPage: () => {
changeSwitcher(false, 'reloadRouteSwitch')
@ -380,7 +381,9 @@ const MenuTag = defineComponent({
/** 动态更新 menu tag 所在位置 */
const positionMenuTag = () => {
nextTick().then(() => {
const tags = getElement(`attr:${MENU_TAG_DATA}="${menuKey.value}"`)
const tags = getElement<HTMLElement>(
`attr:${MENU_TAG_DATA}="${menuKey.value}"`,
)
if (tags?.length) {
const [menuTag] = tags
@ -505,9 +508,9 @@ const MenuTag = defineComponent({
[this.MENU_TAG_DATA]: curr.path,
}}
>
{typeof curr.label === 'function'
? curr.label()
: curr.label}
{typeof curr.label === 'string'
? curr.label
: curr.label?.()}
</NTag>
))}
</NSpace>

View File

@ -21,6 +21,7 @@ import { validMenuItemShow } from '@/router/helper/routerCopilot'
import type { MenuOption } from 'naive-ui'
import type { AppRouteMeta } from '@/router/type'
import type { AppMenuOption } from '@/types/modules/app'
const GlobalSeach = defineComponent({
name: 'GlobalSeach',
@ -49,7 +50,7 @@ const GlobalSeach = defineComponent({
const modelMenuOptions = computed(() => menuStore.options)
const state = reactive({
searchValue: null,
searchOptions: [] as IMenuOptions[],
searchOptions: [] as AppMenuOption[],
})
const tiptextOptions = [
@ -76,9 +77,9 @@ const GlobalSeach = defineComponent({
/** 根据输入值模糊检索菜单 */
const handleSearchMenuOptions = (value: string) => {
const arr: IMenuOptions[] = []
const arr: AppMenuOption[] = []
const filterArr = (options: IMenuOptions[]) => {
const filterArr = (options: AppMenuOption[]) => {
options.forEach((curr) => {
if (curr.children?.length) {
filterArr(curr.children)

View File

@ -16,6 +16,7 @@ import { useSetting } from '@/store'
import { useI18n } from '@/locales/useI18n'
import type { PropType } from 'vue'
import type { Placement } from '@/types/modules/component'
const SettingDrawer = defineComponent({
name: 'SettingDrawer',
@ -25,7 +26,7 @@ const SettingDrawer = defineComponent({
default: false,
},
placement: {
type: String as PropType<NaiveDrawerPlacement>,
type: String as PropType<Placement>,
default: 'right',
},
width: {

View File

@ -29,12 +29,11 @@ import AppAvatar from '@/components/AppComponents/AppAvatar/index'
import { useSetting } from '@/store'
import { LOCAL_OPTIONS } from '@/appConfig/localConfig'
import { useAvatarOptions, avatarDropdownClick } from './hook'
import { getCache } from '@/utils/cache'
import screenfull from 'screenfull'
import { useI18n } from '@/locales/useI18n'
import { APP_CATCH_KEY } from '@/appConfig/appConfig'
import type { IconEventMapOptions, IconEventMap } from './type'
import type { SigninCallback } from '@/store/modules/signin/type'
const SiderBar = defineComponent({
name: 'SiderBar',
@ -46,7 +45,6 @@ const SiderBar = defineComponent({
const { drawerPlacement, breadcrumbSwitch } = storeToRefs(settingStore)
const showSettings = ref(false)
const signin = getCache(APP_CATCH_KEY.signin)
const spaceItemStyle = {
display: 'flex',
}
@ -131,7 +129,6 @@ const SiderBar = defineComponent({
handleIconClick,
showSettings,
updateLocale,
signin,
spaceItemStyle,
drawerPlacement,
breadcrumbSwitch,

View File

@ -22,7 +22,7 @@ import { getCache } from '@use-utils/cache'
import { SYSTEM_DEFAULT_LOCAL } from '@/appConfig/localConfig'
import { APP_CATCH_KEY } from '@/appConfig/appConfig'
import type { Recordable } from '@/types/type-utils'
import type { Recordable } from '@/types/modules/helper'
import type {
AppLocalesModules,
AppLocalesDropdownMixedOption,
@ -126,10 +126,12 @@ export const naiveLocales = (key: string) => {
* @remak , `main.ts` , `i18n`
*/
export const getDefaultLocal = () => {
const catchLanguage = getCache(APP_CATCH_KEY.localeLanguage, 'localStorage')
const catchLanguage = getCache<string>(
APP_CATCH_KEY.localeLanguage,
'localStorage',
)
const locale: string =
catchLanguage !== 'no' ? catchLanguage : SYSTEM_DEFAULT_LOCAL
const locale = catchLanguage ? catchLanguage : SYSTEM_DEFAULT_LOCAL
return locale
}

View File

@ -31,16 +31,17 @@ import type {
NavigationGuardNext,
RouteLocationNormalized,
} from 'vue-router'
import type { AppMenuOption } from '@/types/modules/app'
export const permissionRouter = (router: Router) => {
const { beforeEach } = router
beforeEach((to, from, next) => {
const token = getCache(APP_CATCH_KEY.token)
const route = getCache('menuKey')
const token = getCache<string>(APP_CATCH_KEY.token)
const route = getCache<string>('menuKey') || ROOT_ROUTE.path
if (token !== 'no') {
if (validMenuItemShow(to as unknown as IMenuOptions)) {
if (token !== null) {
if (validMenuItemShow(to as unknown as AppMenuOption)) {
if (to.path === '/' || from.path === '/login') {
if (route !== 'no') {
next(route)

View File

@ -24,6 +24,7 @@ import { setCache } from '@/utils/cache'
import type { Router } from 'vue-router'
import type { AppRouteMeta } from '@/router/type'
import type { AppMenuOption } from '@/types/modules/app'
/**
*
@ -64,7 +65,7 @@ export const validRole = (meta: AppRouteMeta) => {
*
* , 使 validRole
*/
export const validMenuItemShow = (option: IMenuOptions) => {
export const validMenuItemShow = (option: AppMenuOption) => {
const { meta, name } = option
const hidden =
meta?.hidden === undefined || meta?.hidden === false ? false : meta?.hidden

View File

@ -8,8 +8,8 @@ const directive: AppRouteRecordRaw = {
component: () => import('@/views/directive/index'),
meta: {
i18nKey: t('menu.Directive'),
icon: 'rely',
order: 3,
icon: 'other',
order: 2,
},
}

View File

@ -7,7 +7,7 @@ const iframe: AppRouteRecordRaw = {
name: 'IframeDemo',
component: () => import('@/views/iframe/index'),
meta: {
icon: 'rely',
icon: 'other',
order: 2,
noLocalTitle: 'iframe',
},

View File

@ -10,7 +10,7 @@ const multiMenu: AppRouteRecordRaw = {
component: LAYOUT,
meta: {
i18nKey: t('menu.MultiMenu'),
icon: 'table',
icon: 'other',
order: 4,
},
children: [

View File

@ -8,7 +8,7 @@ const precision: AppRouteRecordRaw = {
component: () => import('@/views/precision/index'),
meta: {
i18nKey: t('menu.CalculatePrecision'),
icon: 'rely',
icon: 'other',
order: 2,
},
}

View File

@ -8,7 +8,7 @@ const table: AppRouteRecordRaw = {
component: () => import('@/views/table/index'),
meta: {
i18nKey: t('menu.Table'),
icon: 'table',
icon: 'other',
order: 2,
},
}

View File

@ -1,7 +1,7 @@
/* 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'
import type { Recordable } from '@/types/modules/helper'
import type { DefineComponent, VNode } from 'vue'
export type Component<T = any> =

View File

@ -21,6 +21,7 @@
import { APP_KEEP_ALIVE } from '@/appConfig/appConfig'
import type { KeepAliveStoreState } from './type'
import type { AppMenuOption } from '@/types/modules/app'
export const useKeepAlive = defineStore(
'keepAlive',
@ -40,7 +41,7 @@ export const useKeepAlive = defineStore(
* @remark ,
* @remark ,
*/
const setKeepAliveInclude = (option: IMenuOptions) => {
const setKeepAliveInclude = (option: AppMenuOption) => {
const length = getCurrentKeepAliveLength()
const {
name,

View File

@ -13,10 +13,15 @@
import { MENU_COLLAPSED_CONFIG, ROOT_ROUTE } from '@/appConfig/appConfig'
import RayIcon from '@/components/RayIcon/index'
import { validteValueType } from '@/utils/hook'
import { isValueType } from '@/utils/hook'
import { getCache, setCache } from '@/utils/cache'
import type { VNode } from 'vue'
import type {
AppMenuOption,
MenuTagOptions,
AppMenuKey,
} from '@/types/modules/app'
/**
*
@ -27,7 +32,7 @@ import type { VNode } from 'vue'
* @remark
*/
const check = (
node: IMenuOptions,
node: AppMenuOption,
key: string | number,
value: string | number,
) => {
@ -43,11 +48,11 @@ const check = (
* @remark
*/
const process = (
options: IMenuOptions,
options: AppMenuOption,
key: string | number,
value: string | number,
) => {
const temp: IMenuOptions[] = []
const temp: AppMenuOption[] = []
// 检查当前节点是否匹配值
if (check(options, key, value)) {
@ -79,7 +84,7 @@ const process = (
* @param value
*/
export const parse = (
options: IMenuOptions[],
options: AppMenuOption[],
key: string | number,
value: string | number,
) => {
@ -105,8 +110,8 @@ export const parse = (
* @remark
*/
export const matchMenuOption = (
item: IMenuOptions,
key: MenuKey,
item: AppMenuOption,
key: AppMenuKey,
menuTagOptions: MenuTagOptions[],
) => {
if (item.path !== key) {
@ -125,7 +130,7 @@ export const matchMenuOption = (
* @remark
* @remark sideBarLogo.title
*/
export const updateDocumentTitle = (option: IMenuOptions) => {
export const updateDocumentTitle = (option: AppMenuOption) => {
const { breadcrumbLabel } = option
const {
layout: { sideBarLogo },
@ -135,14 +140,14 @@ export const updateDocumentTitle = (option: IMenuOptions) => {
document.title = breadcrumbLabel + ' - ' + spliceTitle
}
export const hasMenuIcon = (option: IMenuOptions) => {
export const hasMenuIcon = (option: AppMenuOption) => {
const { meta } = option
if (!meta.icon) {
return
}
if (validteValueType(meta.icon, 'Object')) {
if (isValueType<object>(meta.icon, 'Object')) {
return () => meta.icon
}
@ -161,8 +166,10 @@ export const hasMenuIcon = (option: IMenuOptions) => {
/** 获取缓存的 menu key, 如果未获取到则使用 ROOTROUTE path 当作默认激活路由菜单 */
export const getCatchMenuKey = () => {
const { path: rootPath } = ROOT_ROUTE
const cacheMenuKey: MenuKey =
getCache('menuKey') === 'no' ? rootPath : getCache('menuKey')
const cacheMenuKey =
getCache<AppMenuKey>('menuKey') === null
? rootPath
: getCache<AppMenuKey>('menuKey')
return cacheMenuKey
}

View File

@ -40,6 +40,7 @@ import { useVueRouter } from '@/router/helper/useVueRouter'
import type { MenuOption } from 'naive-ui'
import type { AppRouteMeta } from '@/router/type'
import type { AppMenuOption, MenuTagOptions } from '@/types/modules/app'
export const useMenu = defineStore(
'menu',
@ -51,10 +52,10 @@ export const useMenu = defineStore(
const menuState = reactive({
menuKey: getCatchMenuKey(), // 当前菜单 `key`
options: [] as IMenuOptions[], // 菜单列表
options: [] as AppMenuOption[], // 菜单列表
collapsed: false, // 是否折叠菜单
menuTagOptions: [] as MenuTagOptions[], // tag 标签菜单
breadcrumbOptions: [] as IMenuOptions[], // 面包屑菜单
breadcrumbOptions: [] as AppMenuOption[], // 面包屑菜单
})
/**
@ -65,7 +66,7 @@ export const useMenu = defineStore(
* @remark
*/
const getCompleteRoutePath = (
options: IMenuOptions[],
options: AppMenuOption[],
key: string | number,
) => {
const ops = parse(options, 'key', key)
@ -94,8 +95,8 @@ export const useMenu = defineStore(
menuState.menuKey,
menuState.menuTagOptions,
)
updateDocumentTitle(item as unknown as IMenuOptions)
setKeepAliveInclude(item as unknown as IMenuOptions)
updateDocumentTitle(item as unknown as AppMenuOption)
setKeepAliveInclude(item as unknown as AppMenuOption)
menuState.breadcrumbOptions = parse(menuState.options, 'key', key) // 获取面包屑
@ -168,7 +169,7 @@ export const useMenu = defineStore(
* @remark ,
*/
const setupAppRoutes = () => {
const resolveOption = (option: IMenuOptions) => {
const resolveOption = (option: AppMenuOption) => {
const { meta } = option
/** 设置 label, i18nKey 优先级最高 */
@ -184,9 +185,9 @@ export const useMenu = defineStore(
default: () => label.value,
}),
breadcrumbLabel: label.value,
} as IMenuOptions
} as AppMenuOption
/** 合并 icon */
const attr: IMenuOptions = Object.assign({}, route, {
const attr: AppMenuOption = Object.assign({}, route, {
icon: hasMenuIcon(option),
})
@ -203,8 +204,8 @@ export const useMenu = defineStore(
return attr
}
const resolveRoutes = (routes: IMenuOptions[], index: number) => {
const catchArr: IMenuOptions[] = []
const resolveRoutes = (routes: AppMenuOption[], index: number) => {
const catchArr: AppMenuOption[] = []
for (const curr of routes) {
if (curr.children?.length && validMenuItemShow(curr)) {
@ -221,7 +222,7 @@ export const useMenu = defineStore(
}
/** 缓存菜单列表 */
menuState.options = resolveRoutes(routeModules as IMenuOptions[], 0)
menuState.options = resolveRoutes(routeModules as AppMenuOption[], 0)
/** 初始化后渲染面包屑 */
nextTick(() => {

View File

@ -6,7 +6,7 @@ import { useI18n } from '@/locales/useI18n'
import { APP_NAIVE_UI_THEME_OVERRIDES } from '@/appConfig/designConfig'
import { useDayjs } from '@/dayjs/index'
import type { ConditionalPick } from '@/types/type-utils'
import type { ConditionalPick } from '@/types/modules/helper'
import type { SettingState } from '@/store/modules/setting/type'
import type { DayjsLocal } from '@/dayjs/type'

View File

@ -1,7 +1,8 @@
import type { GlobalThemeOverrides } from 'naive-ui'
import type { Placement } from '@/types/modules/component'
export interface SettingState {
drawerPlacement: NaiveDrawerPlacement
drawerPlacement: Placement
primaryColorOverride: GlobalThemeOverrides
themeValue: boolean
reloadRouteSwitch: boolean

15
src/types/axios.d.ts vendored
View File

@ -1,15 +0,0 @@
export {}
declare global {
/**
*
*
*
*
*/
declare interface AxiosResponseBody<T = unknown> {
data: T
message: string
code: number
}
}

View File

@ -1,5 +0,0 @@
export {}
declare global {
declare type CacheType = 'sessionStorage' | 'localStorage'
}

View File

@ -1,26 +0,0 @@
export {}
import type { ECharts } from 'echarts/core'
import type {
MessageApi,
DialogApi,
LoadingBarApi,
NotificationApi,
MenuOption,
MenuDividerOption,
MenuGroupOption,
} from 'naive-ui'
import type { VNodeChild } from 'vue'
declare global {
declare type ComponentSize = 'small' | 'medium' | 'large'
declare type EChartsInstance = ECharts
declare type NaiveDrawerPlacement = 'top' | 'right' | 'bottom' | 'left'
declare type NaiveMenuOptions =
| MenuOption
| MenuDividerOption
| MenuGroupOption
}

View File

@ -1,14 +1,17 @@
export {}
/* eslint-disable @typescript-eslint/no-explicit-any */
import type { AppConfig } from './cfg'
import type {
MessageApi,
DialogApi,
LoadingBarApi,
NotificationApi,
} from 'naive-ui'
import type { AppConfig } from './cfg'
export global {
declare global {
declare interface UnknownObjectKey {
[propName: string]: any
}
declare const __APP_CFG__: AppConfig
declare interface Window {
@ -41,9 +44,9 @@ export global {
$loadingBar: LoadingBarApi
$notification: NotificationApi
// eslint-disable-next-line @typescript-eslint/no-explicit-any
DocsAPI?: any
// eslint-disable-next-line @typescript-eslint/no-explicit-any
DocEditor?: any
msCrypto: Crypto
}
}

163
src/types/micro.d.ts vendored
View File

@ -1,163 +0,0 @@
export {}
declare global {
export declare type lifecycle = (appWindow: Window) => unknown
export declare type loadErrorHandler = (url: string, e: Error) => unknown
export declare type baseOptions = {
/** 唯一性用户必须保证 */
name: string
/** 需要渲染的url */
url: string
/** 代码替换钩子 */
replace?: (code: string) => string
/** 自定义fetch */
fetch?: (input: RequestInfo, init?: RequestInit) => Promise<Response>
/** 注入给子应用的属性 */
props?: { [key: string]: unknown }
/** 自定义iframe属性 */
attrs?: { [key: string]: unknown }
/** 子应用采用fiber模式执行 */
fiber?: boolean
/** 子应用保活state不会丢失 */
alive?: boolean
/** 子应用采用降级iframe方案 */
degrade?: boolean
/** 子应用插件 */
plugins?: Array<plugin>
/** 子应用生命周期 */
beforeLoad?: lifecycle
beforeMount?: lifecycle
afterMount?: lifecycle
beforeUnmount?: lifecycle
afterUnmount?: lifecycle
activated?: lifecycle
deactivated?: lifecycle
loadError?: loadErrorHandler
}
export declare type preOptions = baseOptions & {
/** 预执行 */
exec?: boolean
}
export declare type startOptions = baseOptions & {
/** 渲染的容器 */
el: HTMLElement | string
/**
*
* falsehistory还是会增加
* https://html.spec.whatwg.org/multipage/history.html#the-history-interface
*/
sync?: boolean
/** 子应用短路径替换,路由同步时生效 */
prefix?: { [key: string]: string }
/** 子应用加载时loading元素 */
loading?: HTMLElement
}
export declare type optionProperty = 'url' | 'el'
/**
* preOptions startOptions url el
*/
export declare type cacheOptions = Omit<
preOptions & startOptions,
optionProperty
> &
Partial<Pick<startOptions, optionProperty>>
export declare type startOption = {
/** 唯一性用户必须保证 */
name: string
/** 需要渲染的url */
url: string
/** 渲染的容器 */
el: HTMLElement | string
/** 子应用加载时loading元素 */
loading?: HTMLElement
/** 路由同步开关, false刷新无效但是前进后退依然有效 */
sync?: boolean
/** 子应用短路径替换,路由同步时生效 */
prefix?: { [key: string]: string }
/** 子应用保活模式state不会丢失 */
alive?: boolean
/** 注入给子应用的数据 */
props?: { [key: string]: unknown }
/** js采用fiber模式执行 */
fiber?: boolean
/** 子应用采用降级iframe方案 */
degrade?: boolean
/** 自定义iframe属性 */
attrs?: { [key: string]: unknown }
/** 代码替换钩子 */
replace?: (codeText: string) => string
/** 自定义fetch资源和接口 */
fetch?: (input: RequestInfo, init?: RequestInit) => Promise<Response>
/** 子应插件 */
plugins: Array<plugin>
/** 子应用生命周期 */
beforeLoad?: lifecycle
/** 没有做生命周期改造的子应用不会调用 */
beforeMount?: lifecycle
afterMount?: lifecycle
beforeUnmount?: lifecycle
afterUnmount?: lifecycle
/** 非保活应用不会调用 */
activated?: lifecycle
deactivated?: lifecycle
/** 子应用资源加载失败后调用 */
loadError?: loadErrorHandler
}
export declare type preOptions = {
/** 唯一性用户必须保证 */
name: string
/** 需要渲染的url */
url: string
/** 注入给子应用的数据 */
props?: { [key: string]: unknown }
/** 自定义iframe属性 */
attrs?: { [key: string]: unknown }
/** 代码替换钩子 */
replace?: (code: string) => string
/** 自定义fetch资源和接口 */
fetch?: (input: RequestInfo, init?: RequestInit) => Promise<Response>
/** 子应用保活模式state不会丢失 */
alive?: boolean
/** 预执行模式 */
exec?: boolean
/** js采用fiber模式执行 */
fiber?: boolean
/** 子应用采用降级iframe方案 */
degrade?: boolean
/** 子应插件 */
plugins: Array<plugin>
/** 子应用生命周期 */
beforeLoad?: lifecycle
/** 没有做生命周期改造的子应用不会调用 */
beforeMount?: lifecycle
afterMount?: lifecycle
beforeUnmount?: lifecycle
afterUnmount?: lifecycle
/** 非保活应用不会调用 */
activated?: lifecycle
deactivated?: lifecycle
/** 子应用资源加载失败后调用 */
loadError?: loadErrorHandler
}
export declare class EventBus {
private id
private eventObj
constructor(id: string)
$on(event: string, fn: Function): EventBus
/** 任何$emit都会导致监听函数触发第一个参数为事件名后续的参数为$emit的参数 */
$onAll(fn: (event: string, ...args: Array<unknown>) => unknown): EventBus
$once(event: string, fn: Function): void
$off(event: string, fn: Function): EventBus
$offAll(fn: Function): EventBus
$emit(event: string, ...args: Array<unknown>): EventBus
$clear(): EventBus
}
}

21
src/types/modules/app.ts Normal file
View File

@ -0,0 +1,21 @@
import type { VNode } from 'vue'
import type { AppRouteRecordRaw, AppRouteMeta } from '@/router/type'
export type Key = string | number
export interface AppMenuOption extends AppRouteRecordRaw {
name: string
key: Key
path: string
label: string | (() => VNode)
show?: boolean
children?: AppMenuOption[]
meta: AppRouteMeta
breadcrumbLabel?: string
}
export interface MenuTagOptions extends AppMenuOption {
closeable?: boolean
}
export type AppMenuKey = Key | null

View File

@ -0,0 +1,5 @@
export interface AxiosResponseBody<T = unknown> {
data: T
message: string
code: number
}

View File

@ -5,6 +5,7 @@ import type {
AliasOptions,
UserConfigExport,
} from 'vite'
import type { Recordable } from '@/types/modules/helper'
export interface LayoutSideBarLogo {
icon?: string
@ -49,8 +50,6 @@ export interface Config {
appPrimaryColor?: AppPrimaryColor
}
export type Recordable<T = unknown> = Record<string, T>
/**
*
*

View File

@ -0,0 +1,10 @@
import type { ECharts } from 'echarts/core'
import type { MenuOption, MenuDividerOption, MenuGroupOption } from 'naive-ui'
export type ComponentSize = 'small' | 'medium' | 'large'
export type EChartsInstance = ECharts
export type Placement = 'top' | 'right' | 'bottom' | 'left'
export type NaiveMenuOptions = MenuOption | MenuDividerOption | MenuGroupOption

View File

@ -0,0 +1,44 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import type CryptoJS from 'crypto-js'
export type CacheType = 'sessionStorage' | 'localStorage'
export type EventListenerOrEventListenerObject =
| EventListener
| EventListenerObject
export type ValidteValueType =
| 'Object'
| 'Undefined'
| 'Null'
| 'Boolean'
| 'Number'
| 'String'
| 'Symbol'
| 'Function'
| 'Date'
| 'Array'
| 'RegExp'
| 'Map'
| 'Set'
| 'WeakMap'
| 'WeakSet'
| 'ArrayBuffer'
| 'DataView'
| 'Int8Array'
| 'Uint8Array'
| 'Uint8ClampedArray'
| 'Int16Array'
| 'Uint16Array'
| 'Int32Array'
| 'Uint32Array'
| 'Float32Array'
| 'Float64Array'
export type WordArray = CryptoJS.lib.WordArray
export type CipherParams = CryptoJS.lib.CipherParams
export type AnyFunc = (...args: any[]) => any
export type AnyVoidFunc = (...args: any[]) => void

26
src/types/store.d.ts vendored
View File

@ -1,26 +0,0 @@
export {}
import type { RouteRecordRaw, RouteMeta } from 'vue-router'
import type { MenuOption } from 'naive-ui'
import type { VNode } from 'vue'
import type { AppRouteRecordRaw, AppRouteMeta } from '@/router/type'
declare global {
declare interface IMenuOptions extends AppRouteRecordRaw, MenuOption {
name: string
key: string | number
path: string
label: string | Function
show?: boolean
children?: IMenuOptions[]
meta: AppRouteMeta
breadcrumbLabel?: string
noLocalTitle?: string | number
}
declare interface MenuTagOptions extends IMenuOptions {
closeable?: boolean
}
declare type MenuKey = null | string | number
}

39
src/types/utils.d.ts vendored
View File

@ -1,39 +0,0 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
export {}
import type CryptoJS from 'crypto-js'
import type { VNodeChild } from 'vue'
export global {
declare interface UnknownObjectKey {
[propName: string]: any
}
declare type EventListenerOrEventListenerObject =
| EventListener
| EventListenerObject
declare type ValidteValueType =
| 'Number'
| 'String'
| 'Boolean'
| 'Object'
| 'Function'
| 'Null'
| 'Undefined'
| 'Array'
| 'Date'
| 'Math'
| 'RegExp'
| 'Error'
declare type WordArray = CryptoJS.lib.WordArray
declare type CipherOption = CryptoJS.lib.CipherOption
declare type CipherParams = CryptoJS.lib.CipherParams
declare type VoidFunc = (...args: any[]) => void
declare type AnyFunc = (...args: any[]) => any
}

View File

@ -11,6 +11,8 @@
/** vue3 项目里建议直接用 vueuse useStorage 方法 */
import type { CacheType } from '@/types/modules/utils'
/**
*
* @param key key
@ -32,16 +34,17 @@ export const setCache = <T = unknown>(
*
* @param key key
* @returns
*
* @remark 'no'
*/
export const getCache = (key: string, type: CacheType = 'sessionStorage') => {
export const getCache = <T>(
key: string,
type: CacheType = 'sessionStorage',
): T | null => {
const data =
type === 'localStorage'
? window.localStorage.getItem(key)
: window.sessionStorage.getItem(key)
return Object.is(data, null) ? 'no' : JSON.parse(data as string)
return Object.is(data, null) ? null : JSON.parse(data as string)
}
/**

View File

@ -1,8 +1,10 @@
import HmacSHA256 from 'crypto-js/hmac-sha256'
import SHA256 from 'crypto-js/sha256'
import AES from 'crypto-js/aes'
import MD5 from 'crypto-js/md5'
import BASE64 from 'crypto-js/enc-base64'
// import HmacSHA256 from 'crypto-js/hmac-sha256'
// import SHA256 from 'crypto-js/sha256'
// import AES from 'crypto-js/aes'
// import MD5 from 'crypto-js/md5'
// import BASE64 from 'crypto-js/enc-base64'
// import type { WordArray, CipherParams } from '@/types/modules/utils'
/**
*
@ -11,118 +13,3 @@ import BASE64 from 'crypto-js/enc-base64'
*
* 手动补上官网地址: http://github.com/brix/crypto-js
*/
/**
*
* @param message
* @param key key
*
* @remark HmacSHA256
*/
export const useHmacSHA256 = (
message: WordArray | string,
key: WordArray | string,
) => {
return new Promise((resolve) => {
const cry = HmacSHA256(message, key)
resolve(cry)
})
}
/**
*
* @param message
*
* @remark SHA256
*/
export const useSHA256 = (message: WordArray | string) => {
return new Promise((resolve) => {
const cry = SHA256(message)
resolve(cry)
})
}
/**
*
* @param message
* @param key key
* @param cfg
*
* @remark AES
*/
export const useAESEncrypt = (
message: WordArray | string,
key: WordArray | string,
cfg?: CipherOption,
) => {
return new Promise((resolve) => {
const cry = AES.encrypt(message, key, cfg)
resolve(cry)
})
}
/**
*
* @param ciphertext
* @param key key
* @param cfg
*
* @remark AES
*/
export const useAESDecrypt = (
ciphertext: CipherParams | string,
key: WordArray | string,
cfg?: CipherOption,
) => {
return new Promise((resolve) => {
const cry = AES.decrypt(ciphertext, key, cfg)
resolve(cry)
})
}
/**
*
* @param message
* @param cfg md5
*
* @remark md5
*/
export const useMD5 = (message: WordArray | string, cfg?: object) => {
return new Promise((resolve) => {
const cry = MD5(message, cfg)
resolve(cry)
})
}
/**
*
* @param wordArray base64
*
* @remark base64
*/
export const useBase64Stringify = (wordArray: WordArray) => {
return new Promise((resolve) => {
const cry = BASE64.stringify(wordArray)
resolve(cry)
})
}
/**
*
* @param str base64
*
* @remark base64
*/
export const useBase64Parse = (str: string) => {
return new Promise((resolve) => {
const cry = BASE64.parse(str)
resolve(cry)
})
}

View File

@ -1,11 +1,14 @@
import { validteValueType } from '@use-utils/hook'
import { isValueType } from '@use-utils/hook'
import { ELEMENT_UNIT } from '@/appConfig/regConfig'
import type { EventListenerOrEventListenerObject } from '@/types/modules/utils'
/**
*
* @param element Target element dom
* @param event
* @param handler
* @param useCapture
*
* @remark
*/
@ -13,7 +16,7 @@ export const on = (
element: HTMLElement | Document | Window,
event: string,
handler: EventListenerOrEventListenerObject,
useCapture = false,
useCapture: boolean | AddEventListenerOptions = false,
) => {
if (element && event && handler) {
element.addEventListener(event, handler, useCapture)
@ -25,6 +28,7 @@ export const on = (
* @param element Target element dom
* @param event
* @param handler
* @param useCapture
*
* @remark
*/
@ -32,7 +36,7 @@ export const off = (
element: HTMLElement | Document | Window,
event: string,
handler: EventListenerOrEventListenerObject,
useCapture = false,
useCapture: boolean | AddEventListenerOptions = false,
) => {
if (element && event && handler) {
element.removeEventListener(event, handler, useCapture)
@ -133,12 +137,12 @@ export const addStyle = (
styles: string | Partial<CSSStyleDeclaration>,
) => {
if (el) {
if (validteValueType(styles, 'Object')) {
if (isValueType<object>(styles, 'Object')) {
Object.keys(styles).forEach((item) => {
el.style[item] = styles[item]
})
} else if (validteValueType(styles, 'String')) {
const _styles = styles as string
} else if (isValueType<string>(styles, 'String')) {
const _styles = styles
_styles.split(';').forEach((item) => {
const [_k, _v] = item.split(':')
@ -223,9 +227,9 @@ export const colorToRgba = (color: string, alpha = 1) => {
*
* const el = getElement('attr:type')
*/
export const getElement = (element: string) => {
export const getElement = <T extends Element>(element: string) => {
if (!element) {
return
return null
}
let queryParam: string
@ -237,7 +241,7 @@ export const getElement = (element: string) => {
}
try {
const el = Array.from(document.querySelectorAll(queryParam))
const el = Array.from(document.querySelectorAll<T>(queryParam))
return el
} catch (e) {
@ -248,15 +252,16 @@ export const getElement = (element: string) => {
/**
*
* @param size css size
* @param unit css
*
* @remark
*/
export const completeSize = (size: number | string) => {
export const completeSize = (size: number | string, unit = 'px') => {
if (typeof size === 'number') {
return size.toString() + 'px'
} else if (ELEMENT_UNIT.test(size)) {
return size.toString() + unit
} else if (isValueType<string>(size, 'String') && ELEMENT_UNIT.test(size)) {
return size
} else {
return size + 'px'
return size + unit
}
}

View File

@ -1,3 +1,5 @@
import type { ValidteValueType } from '@/types/modules/utils'
/**
*
* @returns
@ -14,9 +16,11 @@ export const getAppEnvironment = () => {
*
* @returns formate binary to base64 of the image
*/
export const useImagebufferToBase64 = (
data: ArrayBufferLike | ArrayLike<number>,
) => {
export const arrayBufferToBase64Image = (data: ArrayBuffer): string | null => {
if (!data || data.byteLength) {
return null
}
const base64 =
'data:image/png;base64,' +
window.btoa(
@ -34,10 +38,10 @@ export const useImagebufferToBase64 = (
* @param value
* @param type
*/
export const validteValueType = <T = unknown>(
value: T,
export const isValueType = <T>(
value: unknown,
type: ValidteValueType,
) => {
): value is T => {
const valid = Object.prototype.toString.call(value)
return valid.includes(type)
@ -49,35 +53,29 @@ export const validteValueType = <T = unknown>(
* @param radix `uuid`
* @returns `uuid`
*/
export const uuid = (length = 16, radix?: number) => {
const sad =
export const uuid = (length = 16, radix = 62) => {
// 定义可用的字符集,即 0-9, A-Z, a-z
const availableChars =
'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split('')
// 定义存储随机字符串的数组
const arr: string[] = []
// 获取加密对象,兼容 IE11
const cryptoObj = window.crypto || window.msCrypto
let i = 0
radix = radix || sad.length
// 循环 length 次,生成随机字符,并添加到数组中
for (i = 0; i < length; i++) {
// 生成一个随机数
const randomValues = new Uint32Array(1)
if (length) {
for (i = 0; i < length; i++) {
arr[i] = sad[0 | (Math.random() * radix)]
}
} else {
let r
cryptoObj.getRandomValues(randomValues)
arr[23] = '-'
arr[18] = arr[23]
arr[13] = arr[18]
arr[8] = arr[13]
arr[14] = '4'
// 根据随机数生成对应的字符,并添加到数组中
const index = randomValues[0] % radix
for (i = 0; i < 36; i++) {
if (!arr[i]) {
r = 0 | (Math.random() * radix)
arr[i] = sad[i === 19 ? (r & 0x3) | 0x8 : r]
}
}
arr.push(availableChars[index])
}
// 将数组中的字符连接起来,返回最终的字符串
return arr.join('')
}

View File

@ -32,6 +32,7 @@ import currency from 'currency.js'
import { cloneDeep } from 'lodash-es'
import type { Options } from 'currency.js'
import type { AnyFunc } from '@/types/modules/utils'
export type CurrencyArguments = string | number | currency

View File

@ -10,6 +10,7 @@ import {
NButton,
} from 'naive-ui'
import { onAxiosTest } from '@use-api/test'
import { isArray } from 'lodash-es'
const Axios = defineComponent({
name: 'RAxios',

View File

@ -20,7 +20,7 @@ import {
NFormItem,
} from 'naive-ui'
import type { ConditionalPick } from '@/types/type-utils'
import type { ConditionalPick } from '@/types/modules/helper'
const RDirective = defineComponent({
name: 'RDirective',

View File

@ -3,6 +3,8 @@ import './index.scss'
import { NCard, NSwitch, NSpace, NP, NH6, NH2, NH3 } from 'naive-ui'
import RayChart from '@/components/RayChart/index'
import type { EChartsInstance } from '@/types/modules/component'
const Echart = defineComponent({
name: 'REchart',
setup() {

View File

@ -17,7 +17,7 @@
* ,
*/
import { NSpace } from 'naive-ui'
import { NCard, NSpace } from 'naive-ui'
import RayIframe from '@/components/RayIframe/index'
const IframeDemo = defineComponent({
@ -28,18 +28,20 @@ const IframeDemo = defineComponent({
render() {
return (
<NSpace vertical size={[20, 20]}>
<NSpace vertical size={[20, 20]}>
<h2>naive ui</h2>
<NCard title="naive ui延迟加载">
<RayIframe
src="https://www.naiveui.com/zh-CN/dark"
height="500"
height="300"
allow="fullscreen"
/>
</NSpace>
<NSpace vertical size={[20, 20]}>
<h2>vueuse</h2>
<RayIframe src="https://www.vueusejs.com/" height="500" />
</NSpace>
</NCard>
<NCard title="vueuse立即加载">
<RayIframe
src="https://www.vueusejs.com/"
height="300"
lazy={false}
/>
</NCard>
</NSpace>
)
},

View File

@ -16,7 +16,7 @@ import type { PropType } from 'vue'
const Document = defineComponent({
name: 'RDocument',
setup() {
const editorUUID = uuid()
const editorUUID = uuid(16)
const state = reactive({})
const config = {
document: {
@ -34,16 +34,6 @@ const Document = defineComponent({
},
}
const registerEdtior = () => {
const uid = uuid(12)
}
onMounted(() => {
nextTick(() => {
registerEdtior()
})
})
return {
...toRefs(state),
editorUUID,

View File

@ -47,6 +47,7 @@
"src/*.vue",
"src/*",
"components.d.ts",
"auto-imports.d.ts"
"auto-imports.d.ts",
"src/types/global.d.ts"
]
}

View File

@ -7,7 +7,6 @@ import {
viteVueI18nPlugin,
viteSVGIcon,
} from './vite-plugin/index'
import viteVueJSX from '@vitejs/plugin-vue-jsx'
import viteVeI18nPlugin from '@intlify/unplugin-vue-i18n/vite'
import viteInspect from 'vite-plugin-inspect'