mirror of
https://github.com/continew-org/continew-admin-ui.git
synced 2025-11-05 08:57:10 +08:00
first commit
This commit is contained in:
24
src/hooks/modules/useBreakpoint.ts
Normal file
24
src/hooks/modules/useBreakpoint.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { computed, type ComputedRef } from 'vue'
|
||||
import { useBreakpoints } from '@vueuse/core'
|
||||
import type { ColProps } from '@arco-design/web-vue'
|
||||
|
||||
type ColBreakpoint = Pick<ColProps, 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'xxl'>
|
||||
type Breakpoint = keyof ColBreakpoint
|
||||
|
||||
export function useBreakpoint() {
|
||||
const breakpoints = useBreakpoints({
|
||||
xs: 576, // <576
|
||||
sm: 576, // >= 576
|
||||
md: 768, // >=768
|
||||
lg: 992, // >=992
|
||||
xl: 1200, // >=1200
|
||||
xxl: 1600 // >=1600
|
||||
})
|
||||
|
||||
const arr = breakpoints.current() as ComputedRef<Breakpoint[]>
|
||||
const breakpoint = computed(() => {
|
||||
return arr.value[arr.value.length - 1] || 'xs'
|
||||
})
|
||||
|
||||
return { breakpoint }
|
||||
}
|
||||
24
src/hooks/modules/useBreakpointIndex.ts
Normal file
24
src/hooks/modules/useBreakpointIndex.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { useBreakpoint } from '@/hooks'
|
||||
|
||||
type BreakpointMap = {
|
||||
xs: number
|
||||
sm: number
|
||||
md: number
|
||||
lg: number
|
||||
xl: number
|
||||
xxl: number
|
||||
}
|
||||
|
||||
export function useBreakpointIndex(callback: (v: number) => void, breakpointObj?: BreakpointMap) {
|
||||
const { breakpoint } = useBreakpoint()
|
||||
|
||||
watch(
|
||||
() => breakpoint.value,
|
||||
(v) => {
|
||||
const def = { xs: 0, sm: 0, md: 0, lg: 1, xl: 1, xxl: 2 }
|
||||
const obj = breakpointObj ? breakpointObj : def
|
||||
callback(obj[v as keyof typeof obj])
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
}
|
||||
26
src/hooks/modules/useChart.ts
Normal file
26
src/hooks/modules/useChart.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import { computed } from 'vue'
|
||||
import type { EChartsOption } from 'echarts'
|
||||
import { useAppStore } from '@/stores'
|
||||
|
||||
// 获取代码提示
|
||||
// 从'echarts'中导入{ SeriesOption };
|
||||
// 因为配置项太多,这提供了一个相对方便的代码提示。
|
||||
// 当使用vue时,注意反应性问题。需要保证对应的函数可以被触发,TypeScript不会报错,代码编写方便。
|
||||
|
||||
interface optionsFn {
|
||||
(isDark: boolean): EChartsOption
|
||||
}
|
||||
|
||||
export function useChart(sourceOption: optionsFn) {
|
||||
const appStore = useAppStore()
|
||||
const isDark = computed(() => appStore.theme === 'dark')
|
||||
|
||||
// echarts support https://echarts.apache.org/zh/theme-builder.html
|
||||
// 这里不使用
|
||||
// TODO 图表主题
|
||||
const option = computed<EChartsOption>(() => {
|
||||
return sourceOption(isDark.value)
|
||||
})
|
||||
|
||||
return { option }
|
||||
}
|
||||
17
src/hooks/modules/useDevice.ts
Normal file
17
src/hooks/modules/useDevice.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { computed } from 'vue'
|
||||
import { useWindowSize } from '@vueuse/core'
|
||||
|
||||
/**
|
||||
* 响应式布局容器固定宽度
|
||||
*
|
||||
* 大屏(>=1200px)
|
||||
* 中屏(>=992px)
|
||||
* 小屏(>=768px)
|
||||
*/
|
||||
export function useDevice() {
|
||||
const { width } = useWindowSize()
|
||||
const isDesktop = computed(() => width.value > 768)
|
||||
const isMobile = computed(() => !isDesktop.value)
|
||||
|
||||
return { isMobile, isDesktop }
|
||||
}
|
||||
17
src/hooks/modules/useForm.ts
Normal file
17
src/hooks/modules/useForm.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { reactive } from 'vue'
|
||||
import _ from 'lodash'
|
||||
|
||||
export function useForm<F extends object>(initValue: F) {
|
||||
const getInitValue = () => _.cloneDeep(initValue)
|
||||
|
||||
const form = reactive(getInitValue())
|
||||
|
||||
const resetForm = () => {
|
||||
for (const key in form) {
|
||||
delete form[key]
|
||||
}
|
||||
Object.assign(form, getInitValue())
|
||||
}
|
||||
|
||||
return { form, resetForm }
|
||||
}
|
||||
19
src/hooks/modules/useLoading.ts
Normal file
19
src/hooks/modules/useLoading.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { ref } from 'vue'
|
||||
|
||||
export function useLoading(initValue = false) {
|
||||
const loading = ref(initValue)
|
||||
|
||||
const setLoading = (value: boolean) => {
|
||||
loading.value = value
|
||||
}
|
||||
|
||||
const toggle = () => {
|
||||
loading.value = !loading.value
|
||||
}
|
||||
|
||||
return {
|
||||
loading,
|
||||
setLoading,
|
||||
toggle
|
||||
}
|
||||
}
|
||||
57
src/hooks/modules/usePagination.ts
Normal file
57
src/hooks/modules/usePagination.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
import { reactive, toRefs, watch } from 'vue'
|
||||
import { useBreakpoint } from '@/hooks'
|
||||
|
||||
type Callback = () => void
|
||||
|
||||
type Options = {
|
||||
defaultPageSize: number
|
||||
}
|
||||
|
||||
export function usePagination(callback: Callback, options: Options = { defaultPageSize: 10 }) {
|
||||
const { breakpoint } = useBreakpoint()
|
||||
|
||||
const pagination = reactive({
|
||||
showPageSize: true,
|
||||
showTotal: true,
|
||||
current: 1,
|
||||
pageSize: options.defaultPageSize,
|
||||
total: 0,
|
||||
simple: false,
|
||||
onChange: (size: number) => {
|
||||
pagination.current = size
|
||||
callback && callback()
|
||||
},
|
||||
onPageSizeChange: (size: number) => {
|
||||
pagination.current = 1
|
||||
pagination.pageSize = size
|
||||
callback && callback()
|
||||
}
|
||||
})
|
||||
|
||||
watch(
|
||||
() => breakpoint.value,
|
||||
() => {
|
||||
pagination.simple = ['xs'].includes(breakpoint.value)
|
||||
pagination.showTotal = !['xs'].includes(breakpoint.value)
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
const changeCurrent = pagination.onChange
|
||||
const changePageSize = pagination.onPageSizeChange
|
||||
function setTotal(value: number) {
|
||||
pagination.total = value
|
||||
}
|
||||
|
||||
const { current, pageSize, total } = toRefs(pagination)
|
||||
|
||||
return {
|
||||
current,
|
||||
pageSize,
|
||||
total,
|
||||
pagination,
|
||||
changeCurrent,
|
||||
changePageSize,
|
||||
setTotal
|
||||
}
|
||||
}
|
||||
20
src/hooks/modules/useRequest.ts
Normal file
20
src/hooks/modules/useRequest.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { ref, type UnwrapRef } from 'vue'
|
||||
import type { AxiosResponse } from 'axios'
|
||||
import { useLoading } from '@/hooks'
|
||||
|
||||
export function useRequest<T>(
|
||||
api: () => Promise<AxiosResponse<ApiRes<T>>>,
|
||||
defaultValue = [] as unknown as T,
|
||||
isLoading = true
|
||||
) {
|
||||
const { loading, setLoading } = useLoading(isLoading)
|
||||
const response = ref<T>(defaultValue)
|
||||
api()
|
||||
.then((res) => {
|
||||
response.value = res.data as unknown as UnwrapRef<T>
|
||||
})
|
||||
.finally(() => {
|
||||
setLoading(false)
|
||||
})
|
||||
return { loading, response }
|
||||
}
|
||||
88
src/hooks/modules/useTable.ts
Normal file
88
src/hooks/modules/useTable.ts
Normal file
@@ -0,0 +1,88 @@
|
||||
import type { TableInstance, TableData } from '@arco-design/web-vue'
|
||||
import { Modal, Message } from '@arco-design/web-vue'
|
||||
import { usePagination } from '@/hooks'
|
||||
|
||||
interface Options<T> {
|
||||
formatResult?: (data: T[]) => any
|
||||
onSuccess?: () => void
|
||||
immediate?: boolean
|
||||
rowKey?: keyof T
|
||||
}
|
||||
|
||||
type PaginationParams = { page: number; size: number }
|
||||
type Api<T> = (params: PaginationParams) => Promise<ApiRes<PageRes<T[]>>>
|
||||
|
||||
export function useTable<T>(api: Api<T>, options?: Options<T>) {
|
||||
const { formatResult, onSuccess, immediate, rowKey } = options || {}
|
||||
const { pagination, setTotal } = usePagination(() => getTableData())
|
||||
const loading = ref(false)
|
||||
const tableData = ref<T[]>([])
|
||||
|
||||
const getTableData = async () => {
|
||||
try {
|
||||
loading.value = true
|
||||
const res = await api({ page: pagination.current, size: pagination.pageSize })
|
||||
tableData.value = formatResult ? formatResult(res.data.list) : res.data.list
|
||||
setTotal(res.data.total)
|
||||
onSuccess && onSuccess()
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 是否立即出发
|
||||
const isImmediate = immediate ?? true
|
||||
isImmediate && getTableData()
|
||||
|
||||
// 查询
|
||||
const search = () => {
|
||||
selectedKeys.value = []
|
||||
pagination.onChange(1)
|
||||
}
|
||||
|
||||
// 多选
|
||||
const selectedKeys = ref<(string | number)[]>([])
|
||||
const select: TableInstance['onSelect'] = (rowKeys) => {
|
||||
selectedKeys.value = rowKeys
|
||||
}
|
||||
|
||||
// 全选
|
||||
const selectAll: TableInstance['onSelectAll'] = (checked) => {
|
||||
const key = rowKey ?? 'id'
|
||||
const arr = (tableData.value as TableData[]).filter((i) => !(i?.disabled ?? false))
|
||||
selectedKeys.value = checked ? arr.map((i) => i[key as string]) : []
|
||||
}
|
||||
|
||||
// 删除
|
||||
const handleDelete = async <T>(
|
||||
deleteApi: () => Promise<ApiRes<T>>,
|
||||
options?: { title?: string; content?: string; successTip?: string; showModal?: boolean }
|
||||
): Promise<boolean | undefined> => {
|
||||
const onDelete = async () => {
|
||||
try {
|
||||
const res = await deleteApi()
|
||||
if (res.success) {
|
||||
Message.success(options?.successTip || '删除成功')
|
||||
selectedKeys.value = []
|
||||
getTableData()
|
||||
}
|
||||
return true
|
||||
} catch (error) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
const flag = options?.showModal ?? true // 是否显示对话框
|
||||
if (!flag) {
|
||||
return onDelete()
|
||||
}
|
||||
Modal.warning({
|
||||
title: options?.title || '提示',
|
||||
content: options?.content || '是否确定删除该条数据?',
|
||||
hideCancel: false,
|
||||
maskClosable: false,
|
||||
onBeforeOk: onDelete
|
||||
})
|
||||
}
|
||||
|
||||
return { loading, tableData, getTableData, search, pagination, selectedKeys, select, selectAll, handleDelete }
|
||||
}
|
||||
Reference in New Issue
Block a user