This commit is contained in:
ZhuJW
2026-07-10 18:55:55 +08:00
commit fce40c7d6c
317 changed files with 170079 additions and 0 deletions
+16
View File
@@ -0,0 +1,16 @@
import type { AccessEnum } from '~@/utils/constant'
import { toArray } from '@v-c/utils'
export function useAccess() {
const userStore = useUserStore()
const roles = computed(() => userStore.roles)
const hasAccess = (roles: (string | number)[] | string | number | AccessEnum) => {
const accessRoles = userStore.roles
const roleArr = toArray(roles).flat(1)
return roleArr.some(role => accessRoles?.includes(role))
}
return {
hasAccess,
roles,
}
}
+14
View File
@@ -0,0 +1,14 @@
import type { GlobalToken } from 'ant-design-vue/es/theme'
import { theme } from 'ant-design-vue'
export const useAntdToken = createSharedComposable(() => {
const { token: antdToken } = theme.useToken()
const token = ref<GlobalToken>(antdToken.value)
const setToken = (globalToken: GlobalToken) => {
token.value = globalToken
}
return {
token,
setToken,
}
})
+8
View File
@@ -0,0 +1,8 @@
import { useDelete, useGet, usePost, usePut } from '~/utils/request'
export {
useDelete,
useGet,
usePost,
usePut,
}
@@ -0,0 +1,3 @@
export const STORAGE_AUTHORIZE_KEY = 'Authorization'
export const useAuthorization = createGlobalState(() => useStorage<null | string>(STORAGE_AUTHORIZE_KEY, null))
+51
View File
@@ -0,0 +1,51 @@
import type { LoadingEnum } from '~#/loading-enum'
import baseLoading from '@/components/base-loading/index.vue'
interface LoadingType {
text?: string
textColor?: string
background?: string
spin?: LoadingEnum
minTime?: number
modal?: boolean
}
/**
* 全局loading配置
* @param config
*/
export function useLoading(config: LoadingType = {}) {
const loadingConstructor = createApp(baseLoading, { ...config })
let instance: any = null
let startTime = 0
let endTime = 0
const minTime = config.minTime || 0
const open = (target: HTMLElement = document.body) => {
if (!instance)
instance = loadingConstructor.mount(document.createElement('div'))
if (!instance || !instance.$el)
return
target?.appendChild?.(instance.$el)
startTime = performance.now()
}
const close = () => {
if (!instance || !instance.$el)
return
endTime = performance.now()
if (endTime - startTime < minTime) {
setTimeout(() => {
instance.$el.parentNode?.removeChild(instance.$el)
}, Math.floor(minTime - (endTime - startTime)))
}
else {
instance.$el.parentNode?.removeChild(instance.$el)
}
}
return {
open,
close,
}
}
+40
View File
@@ -0,0 +1,40 @@
import type { VNode } from 'vue'
import { createVNode } from 'vue'
const compMap = new Map<string, VNode>()
export function useCompConsumer() {
const route = useRoute()
const getComp = (component: VNode): VNode => {
// 判断当前是否包含name,如果不包含name,那就直接处理掉name
if (!route.name)
return component
// 获取当前组件的name
// @ts-expect-error this is obj
const compName = component?.type?.name
const routeName = route.name as string
if (compMap.has(routeName))
return compMap.get(routeName) as VNode
// 不存在的情况下,就需要进行组织
const node = component
if (compName && compName === routeName) {
compMap.set(routeName, node)
return node
}
const Comp = createVNode(node)
if (!Comp.type)
Comp.type = {}
// @ts-expect-error this is obj
Comp.type.name = routeName
compMap.set(routeName, Comp)
return Comp
}
return {
getComp,
}
}
+15
View File
@@ -0,0 +1,15 @@
import router from '@/router'
export function useCurrentRoute() {
const currentRoute = router.currentRoute
const layoutMenuStore = useLayoutMenu()
const { menuDataMap } = storeToRefs(layoutMenuStore)
const pathsKeys = menuDataMap.value?.keys()
const currentPath = currentRoute.value.path
// router.
// 通过校验判断是否在menuItem中
console.log('currentPath', currentPath, pathsKeys)
return {
currentRoute,
}
}
+36
View File
@@ -0,0 +1,36 @@
import type { message, notification } from 'ant-design-vue'
import type { ModalFunc } from 'ant-design-vue/es/modal/Modal'
interface GlobalConfigIntl {
message?: Omit<typeof message, 'useMessage'>
modal?: {
readonly info: ModalFunc
readonly success: ModalFunc
readonly error: ModalFunc
readonly warning: ModalFunc
readonly confirm: ModalFunc
}
notification?: Omit<typeof notification, 'useNotification'>
}
const globalConfig = reactive<GlobalConfigIntl>({})
export function useGlobalConfig() {
return globalConfig
}
export function useSetGlobalConfig(config: GlobalConfigIntl) {
globalConfig.message = config.message
globalConfig.modal = config.modal
globalConfig.notification = config.notification
}
export function useMessage() {
return globalConfig.message!
}
export function useModal() {
return globalConfig.modal!
}
export function useNotification() {
return globalConfig.notification!
}
+78
View File
@@ -0,0 +1,78 @@
import dayjs from 'dayjs'
import { i18n, loadLanguageAsync } from '~@/locales'
import router from '~@/router'
import { useMetaTitle } from '~/composables/meta-title'
import 'dayjs/locale/zh-cn'
const LOCALE_KEY = 'locale'
export const preferredLanguages = usePreferredLanguages()
export const lsLocaleState = useStorage(LOCALE_KEY, preferredLanguages.value[0])
export const useI18nLocale = createSharedComposable(() => {
// 加载多语言的loading状态
const loading = ref(false)
const localeStore = useAppStore()
// 多语言的信息
const locale = computed<string>(() => {
if (!i18n)
return 'zh-CN'
return unref(i18n.global.locale)
})
// 获取antd的多语言
const antd = computed(() => {
return (i18n?.global?.getLocaleMessage?.(unref(locale)) as any)?.antd || undefined
})
// 切换多语言
const setLocale = async (locale: string) => {
if (!i18n)
return
if (loading.value)
return
loading.value = true
try {
// 加载多语言
localeStore.toggleLocale(locale)
await loadLanguageAsync(locale)
// 判断是否存在兼容模式
if (i18n.mode === 'legacy')
i18n.global.locale = locale as any
else
(i18n.global.locale as any).value = locale as any
loading.value = false
}
catch (e) {
loading.value = false
}
}
watch(locale, () => {
if (antd.value && antd.value.locale)
dayjs.locale(antd.value.locale)
const route = router.currentRoute.value
useMetaTitle(route)
}, {
immediate: true,
})
// 切换多语言功能
const t = (key: string, defaultMessage?: string) => {
const message = (i18n?.global as any)?.t?.(key)
if (message !== key)
return (i18n?.global as any)?.t?.(key)
else
return defaultMessage ?? key
}
return {
locale,
t,
antd,
setLocale,
}
})
+20
View File
@@ -0,0 +1,20 @@
export function useLoadingCheck() {
const loading = document.querySelector('body > #loading-app')
if (loading) {
const body = document.querySelector('body')
setTimeout(() => {
body?.removeChild(loading)
}, 100)
}
}
export function useScrollToTop() {
const app = document.getElementById('app')
if (app) {
setTimeout(() => {
app.scrollTo({
top: 0,
})
}, 300)
}
}
+12
View File
@@ -0,0 +1,12 @@
import type { RouteLocationNormalizedLoaded, RouteRecordRaw } from 'vue-router'
import { i18n } from '~@/locales'
export function useMetaTitle(route: RouteRecordRaw | RouteLocationNormalizedLoaded) {
const { title, locale } = route.meta ?? {}
if (title || locale) {
if (locale)
useTitle((i18n?.global as any).t?.(locale) ?? title)
else
useTitle(title)
}
}
@@ -0,0 +1,25 @@
export const breakpointsEnum = {
xl: 1600,
lg: 1199,
md: 991,
sm: 767,
xs: 575,
}
export function useQueryBreakpoints() {
const breakpoints = reactive(useBreakpoints(breakpointsEnum))
// 手机端
const isMobile = breakpoints.smaller('sm')
// pad端
const isPad = breakpoints.between('sm', 'md')
// pc端
const isDesktop = breakpoints.greater('md')
return {
breakpoints,
isMobile,
isPad,
isDesktop,
}
}
+189
View File
@@ -0,0 +1,189 @@
import type { PaginationProps } from 'ant-design-vue'
import type { TableRowSelection } from 'ant-design-vue/es/table/interface'
import { assign } from 'lodash'
/**
* 表格分页扩展类型
*/
export interface TablePaginationProps extends PaginationProps {
/**
* 排序字段
*/
column: string
/**
* 排序方式
*/
order: string
}
/**
* 表格选择框扩展类型
*/
export interface TableRowSelectionsProps extends TableRowSelection {
/**
* 选择行
*/
selectedRows: any[]
/**
* 选择行key
*/
selectedRowKeys: any[]
}
interface TableQueryResult<D = any> {
records: D[]
total: number
[key: string]: any
}
/**
* 表格查询配置
*/
export interface TableQueryOptions<D = any> {
/**
*查询接口
*/
queryApi: (params?: any) => Promise<any>
/**
* 是否加载中
*/
loading: boolean
/**
* 数据源
*/
dataSource: D[]
/**
* 查询参数
*/
queryParams: Record<string, any>
/**
* 选择配置
*/
rowSelections: TableRowSelectionsProps
/**
* 挂载时进行查询
*/
queryOnMounted: boolean
/**
* 分页配置
*/
pagination: TablePaginationProps
/**
* 是否展开
*/
expand: boolean
/**
* 展开变化
*/
expandChange: () => void
/**
* 查询前回调
*/
beforeQuery: () => void | Promise<void>
/**
* 查询后回调
*/
afterQuery: <R extends TableQueryResult<D> = any>(data: R) => R | Promise<R>
}
/**
* 表格查询方法
*/
export function useTableQuery(_options: Partial<TableQueryOptions>) {
const state = reactive<TableQueryOptions>(assign({
queryApi: () => Promise.resolve(),
loading: false,
queryParams: {},
dataSource: [],
rowSelections: {
selectedRowKeys: [],
selectedRows: [],
onChange(selectedRowKeys: any[], selectedRows: any[]) {
state.rowSelections.selectedRowKeys = selectedRowKeys
state.rowSelections.selectedRows = selectedRows
},
},
queryOnMounted: true,
pagination:
assign({
pageSize: 10,
pageSizeOptions: ['10', '20', '30', '40'],
current: 1,
total: 0,
order: 'desc',
column: 'createTime',
showSizeChanger: true,
showQuickJumper: true,
showTotal: total => `总数据位:${total}`,
onChange(current, pageSize) {
state.pagination!.pageSize = pageSize
state.pagination!.current = current
query()
},
} as TablePaginationProps, _options.pagination),
expand: false,
expandChange() {
state.expand = !state.expand
},
beforeQuery() {
},
afterQuery(data: TablePaginationProps) {
return data
},
}, _options))
// 查询方法
async function query() {
if (state.loading)
return
state.loading = true
try {
await state.beforeQuery()
const { data } = await state.queryApi({
current: state.pagination.current,
pageSize: state.pagination.pageSize,
column: state.pagination.column,
order: state.pagination.order,
...state.queryParams,
})
if (data) {
const _data = await state.afterQuery(data)
state.dataSource = _data.records ?? []
state.pagination.total = _data.total ?? 0
}
}
catch (e) {
throw new Error(`Query Failed: ${e}`)
}
finally {
state.loading = false
}
}
// 重置方法
function resetQuery() {
state.pagination.current = 1
state.queryParams = {}
query()
}
// 初始化查询
function initQuery() {
state.pagination.current = 1
query()
}
onMounted(() => {
if (!state.queryOnMounted)
return
query()
})
return {
query,
resetQuery,
initQuery,
state,
}
}
+2
View File
@@ -0,0 +1,2 @@
export const isDark = useDark()
export const toggleDark = useToggle(isDark)