init
This commit is contained in:
@@ -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,
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
})
|
||||
@@ -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))
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
@@ -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!
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
})
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
export const isDark = useDark()
|
||||
export const toggleDark = useToggle(isDark)
|
||||
Reference in New Issue
Block a user