mirror of
https://github.com/XiaoDaiGua-Ray/ray-template.git
synced 2025-09-19 02:30:02 +08:00
v4.0.0
This commit is contained in:
parent
5daa7d67a4
commit
6498898a43
1
.yarnrc.yml
Normal file
1
.yarnrc.yml
Normal file
@ -0,0 +1 @@
|
||||
nodeLinker: node-modules
|
18
CHANGELOG.md
18
CHANGELOG.md
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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'
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -11,7 +11,7 @@
|
||||
|
||||
/** 系统颜色风格配置入口 */
|
||||
|
||||
import type { AppPrimaryColor } from '@/types/cfg'
|
||||
import type { AppPrimaryColor } from '@/types/modules/cfg'
|
||||
import type { GlobalThemeOverrides } from 'naive-ui'
|
||||
|
||||
/**
|
||||
|
@ -24,6 +24,8 @@
|
||||
|
||||
import useRequest from '@/axios/instance'
|
||||
|
||||
import type { AxiosResponseBody } from '@/types/modules/axios'
|
||||
|
||||
interface AxiosTestResponse extends UnknownObjectKey {
|
||||
data: UnknownObjectKey[]
|
||||
city?: string
|
||||
|
@ -30,6 +30,7 @@ import type {
|
||||
ErrorImplementQueue,
|
||||
FetchType,
|
||||
} from '@/axios/type'
|
||||
import type { AnyFunc } from '@/types/modules/utils'
|
||||
|
||||
/** 当前请求的实例 */
|
||||
const axiosFetchInstance = {
|
||||
|
@ -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
|
||||
|
@ -8,6 +8,7 @@ import type {
|
||||
InternalAxiosRequestConfig,
|
||||
AxiosResponse,
|
||||
} from 'axios'
|
||||
import type { AnyFunc } from '@/types/modules/utils'
|
||||
|
||||
export type AxiosHeaderValue =
|
||||
| AxiosHeaders
|
||||
|
@ -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>
|
||||
)
|
||||
},
|
||||
|
@ -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
|
||||
|
@ -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>
|
||||
),
|
||||
}}
|
||||
|
@ -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',
|
||||
|
@ -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,
|
||||
|
@ -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 // 向左固定
|
||||
|
@ -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
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
import type { DebounceSettings } from 'lodash-es'
|
||||
import type { AnyFunc } from '@/types/modules/utils'
|
||||
|
||||
export interface DebounceBindingOptions {
|
||||
func: AnyFunc
|
||||
|
@ -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
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
import type { ThrottleSettings } from 'lodash-es'
|
||||
import type { AnyFunc } from '@/types/modules/utils'
|
||||
|
||||
export interface ThrottleBindingOptions {
|
||||
func: AnyFunc
|
||||
|
@ -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 |
@ -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',
|
||||
|
@ -34,8 +34,6 @@ $menuTagWrapperWidth: 76px;
|
||||
& .menu-tag__right-setting {
|
||||
width: 28px;
|
||||
height: 20px;
|
||||
// display: inline-flex;
|
||||
// align-items: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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>
|
||||
|
@ -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)
|
||||
|
@ -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: {
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -7,7 +7,7 @@ const iframe: AppRouteRecordRaw = {
|
||||
name: 'IframeDemo',
|
||||
component: () => import('@/views/iframe/index'),
|
||||
meta: {
|
||||
icon: 'rely',
|
||||
icon: 'other',
|
||||
order: 2,
|
||||
noLocalTitle: 'iframe',
|
||||
},
|
||||
|
@ -10,7 +10,7 @@ const multiMenu: AppRouteRecordRaw = {
|
||||
component: LAYOUT,
|
||||
meta: {
|
||||
i18nKey: t('menu.MultiMenu'),
|
||||
icon: 'table',
|
||||
icon: 'other',
|
||||
order: 4,
|
||||
},
|
||||
children: [
|
||||
|
@ -8,7 +8,7 @@ const precision: AppRouteRecordRaw = {
|
||||
component: () => import('@/views/precision/index'),
|
||||
meta: {
|
||||
i18nKey: t('menu.CalculatePrecision'),
|
||||
icon: 'rely',
|
||||
icon: 'other',
|
||||
order: 2,
|
||||
},
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ const table: AppRouteRecordRaw = {
|
||||
component: () => import('@/views/table/index'),
|
||||
meta: {
|
||||
i18nKey: t('menu.Table'),
|
||||
icon: 'table',
|
||||
icon: 'other',
|
||||
order: 2,
|
||||
},
|
||||
}
|
||||
|
@ -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> =
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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(() => {
|
||||
|
@ -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'
|
||||
|
||||
|
@ -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
15
src/types/axios.d.ts
vendored
@ -1,15 +0,0 @@
|
||||
export {}
|
||||
|
||||
declare global {
|
||||
/**
|
||||
*
|
||||
* 请求响应体类型
|
||||
*
|
||||
* 可以根据自己实际情况更改
|
||||
*/
|
||||
declare interface AxiosResponseBody<T = unknown> {
|
||||
data: T
|
||||
message: string
|
||||
code: number
|
||||
}
|
||||
}
|
5
src/types/cache.d.ts
vendored
5
src/types/cache.d.ts
vendored
@ -1,5 +0,0 @@
|
||||
export {}
|
||||
|
||||
declare global {
|
||||
declare type CacheType = 'sessionStorage' | 'localStorage'
|
||||
}
|
26
src/types/component.d.ts
vendored
26
src/types/component.d.ts
vendored
@ -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
|
||||
}
|
15
src/types/index.d.ts → src/types/global.d.ts
vendored
15
src/types/index.d.ts → src/types/global.d.ts
vendored
@ -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
163
src/types/micro.d.ts
vendored
@ -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
|
||||
/**
|
||||
* 路由同步开关
|
||||
* 如果false,子应用跳转主应用路由无变化,但是主应用的history还是会增加
|
||||
* 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
21
src/types/modules/app.ts
Normal 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
|
5
src/types/modules/axios.ts
Normal file
5
src/types/modules/axios.ts
Normal file
@ -0,0 +1,5 @@
|
||||
export interface AxiosResponseBody<T = unknown> {
|
||||
data: T
|
||||
message: string
|
||||
code: number
|
||||
}
|
@ -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>
|
||||
|
||||
/**
|
||||
*
|
||||
* 全局注入配置
|
10
src/types/modules/component.ts
Normal file
10
src/types/modules/component.ts
Normal 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
|
44
src/types/modules/utils.ts
Normal file
44
src/types/modules/utils.ts
Normal 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
26
src/types/store.d.ts
vendored
@ -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
39
src/types/utils.d.ts
vendored
@ -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
|
||||
}
|
@ -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)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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)
|
||||
})
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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('')
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
@ -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',
|
||||
|
@ -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',
|
||||
|
@ -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() {
|
||||
|
@ -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>
|
||||
)
|
||||
},
|
||||
|
@ -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,
|
||||
|
@ -47,6 +47,7 @@
|
||||
"src/*.vue",
|
||||
"src/*",
|
||||
"components.d.ts",
|
||||
"auto-imports.d.ts"
|
||||
"auto-imports.d.ts",
|
||||
"src/types/global.d.ts"
|
||||
]
|
||||
}
|
||||
|
@ -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'
|
||||
|
Loading…
x
Reference in New Issue
Block a user