You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
358 lines
10 KiB
358 lines
10 KiB
import type { ComputedRef, Ref } from 'vue' |
|
import { computed, onMounted, reactive, ref, unref, watch, watchEffect } from 'vue' |
|
import { useTimeoutFn } from '@vueuse/core' |
|
import { cloneDeep, get, merge } from 'lodash-es' |
|
import type { BasicTableProps, FetchParams, SorterResult } from '../types/table' |
|
import type { PaginationProps } from '../types/pagination' |
|
import { FETCH_SETTING, PAGE_SIZE, ROW_KEY } from '../const' |
|
import { buildUUID } from '@/utils/uuid' |
|
import { isBoolean, isFunction, isObject } from '@/utils/is' |
|
|
|
interface ActionType { |
|
getPaginationInfo: ComputedRef<boolean | PaginationProps> |
|
setPagination: (info: Partial<PaginationProps>) => void |
|
setLoading: (loading: boolean) => void |
|
getFieldsValue: () => Recordable |
|
clearSelectedRowKeys: () => void |
|
tableData: Ref<Recordable[]> |
|
} |
|
|
|
interface SearchState { |
|
sortInfo: Recordable |
|
filterInfo: Record<string, string[]> |
|
} |
|
export function useDataSource( |
|
propsRef: ComputedRef<BasicTableProps>, |
|
{ getPaginationInfo, setPagination, setLoading, getFieldsValue, clearSelectedRowKeys, tableData }: ActionType, |
|
emit: EmitType, |
|
) { |
|
const searchState = reactive<SearchState>({ |
|
sortInfo: {}, |
|
filterInfo: {}, |
|
}) |
|
const dataSourceRef = ref<Recordable[]>([]) |
|
const rawDataSourceRef = ref<Recordable>({}) |
|
|
|
watchEffect(() => { |
|
tableData.value = unref(dataSourceRef) |
|
}) |
|
|
|
watch( |
|
() => unref(propsRef).dataSource, |
|
() => { |
|
const { dataSource, api } = unref(propsRef) |
|
!api && dataSource && (dataSourceRef.value = dataSource) |
|
}, |
|
{ |
|
immediate: true, |
|
}, |
|
) |
|
|
|
function handleTableChange(pagination: PaginationProps, filters: Partial<Recordable<string[]>>, sorter: SorterResult) { |
|
const { clearSelectOnPageChange, sortFn, filterFn } = unref(propsRef) |
|
if (clearSelectOnPageChange) |
|
clearSelectedRowKeys() |
|
|
|
setPagination(pagination) |
|
|
|
const params: Recordable = {} |
|
if (sorter && isFunction(sortFn)) { |
|
const sortInfo = sortFn(sorter) |
|
searchState.sortInfo = sortInfo |
|
params.sortInfo = sortInfo |
|
} |
|
|
|
if (filters && isFunction(filterFn)) { |
|
const filterInfo = filterFn(filters) |
|
searchState.filterInfo = filterInfo |
|
params.filterInfo = filterInfo |
|
} |
|
fetch(params) |
|
} |
|
|
|
function setTableKey(items: any[]) { |
|
if (!items || !Array.isArray(items)) |
|
return |
|
items.forEach((item) => { |
|
if (!item[ROW_KEY]) |
|
item[ROW_KEY] = buildUUID() |
|
|
|
if (item.children && item.children.length) |
|
setTableKey(item.children) |
|
}) |
|
} |
|
|
|
const getAutoCreateKey = computed(() => { |
|
return unref(propsRef).autoCreateKey && !unref(propsRef).rowKey |
|
}) |
|
|
|
const getRowKey = computed(() => { |
|
const { rowKey } = unref(propsRef) |
|
return unref(getAutoCreateKey) ? ROW_KEY : rowKey |
|
}) |
|
|
|
const getDataSourceRef = computed(() => { |
|
const dataSource = unref(dataSourceRef) |
|
if (!dataSource || dataSource.length === 0) |
|
return unref(dataSourceRef) |
|
|
|
if (unref(getAutoCreateKey)) { |
|
const firstItem = dataSource[0] |
|
const lastItem = dataSource[dataSource.length - 1] |
|
|
|
if (firstItem && lastItem) { |
|
if (!firstItem[ROW_KEY] || !lastItem[ROW_KEY]) { |
|
const data = cloneDeep(unref(dataSourceRef)) |
|
data.forEach((item) => { |
|
if (!item[ROW_KEY]) |
|
item[ROW_KEY] = buildUUID() |
|
|
|
if (item.children && item.children.length) |
|
setTableKey(item.children) |
|
}) |
|
dataSourceRef.value = data |
|
} |
|
} |
|
} |
|
return unref(dataSourceRef) |
|
}) |
|
|
|
function updateTableData(index: number, key: string, value: any) { |
|
const record = dataSourceRef.value[index] |
|
if (record) |
|
dataSourceRef.value[index][key] = value |
|
|
|
return dataSourceRef.value[index] |
|
} |
|
|
|
function updateTableDataRecord(rowKey: string | number, record: Recordable): Recordable | undefined { |
|
const row = findTableDataRecord(rowKey) |
|
|
|
if (row) { |
|
for (const field in row) { |
|
if (Reflect.has(record, field)) |
|
row[field] = record[field] |
|
} |
|
|
|
return row |
|
} |
|
} |
|
|
|
function deleteTableDataRecord(rowKey: string | number | string[] | number[]) { |
|
if (!dataSourceRef.value || dataSourceRef.value.length === 0) |
|
return |
|
const rowKeyName = unref(getRowKey) |
|
if (!rowKeyName) |
|
return |
|
const rowKeys = !Array.isArray(rowKey) ? [rowKey] : rowKey |
|
|
|
function deleteRow(data, key) { |
|
const row: { index: number; data: [] } = findRow(data, key) |
|
if (row === null || row.index === -1) |
|
return |
|
|
|
row.data.splice(row.index, 1) |
|
|
|
function findRow(data, key) { |
|
if (data === null || data === undefined) |
|
return null |
|
|
|
for (let i = 0; i < data.length; i++) { |
|
const row = data[i] |
|
let targetKeyName: string = rowKeyName as string |
|
if (isFunction(rowKeyName)) |
|
targetKeyName = rowKeyName(row) |
|
|
|
if (row[targetKeyName] === key) |
|
return { index: i, data } |
|
|
|
if (row.children?.length > 0) { |
|
const result = findRow(row.children, key) |
|
if (result != null) |
|
return result |
|
} |
|
} |
|
return null |
|
} |
|
} |
|
|
|
for (const key of rowKeys) { |
|
deleteRow(dataSourceRef.value, key) |
|
deleteRow(unref(propsRef).dataSource, key) |
|
} |
|
setPagination({ |
|
total: unref(propsRef).dataSource?.length, |
|
}) |
|
} |
|
|
|
function insertTableDataRecord(record: Recordable | Recordable[], index: number): Recordable[] | undefined { |
|
// if (!dataSourceRef.value || dataSourceRef.value.length == 0) return; |
|
index = index ?? dataSourceRef.value?.length |
|
const _record = isObject(record) ? [record as Recordable] : (record as Recordable[]) |
|
unref(dataSourceRef).splice(index, 0, ..._record) |
|
return unref(dataSourceRef) |
|
} |
|
|
|
function findTableDataRecord(rowKey: string | number) { |
|
if (!dataSourceRef.value || dataSourceRef.value.length === 0) |
|
return |
|
|
|
const rowKeyName = unref(getRowKey) |
|
if (!rowKeyName) |
|
return |
|
|
|
const { childrenColumnName = 'children' } = unref(propsRef) |
|
|
|
const findRow = (array: any[]) => { |
|
let ret |
|
array.some(function iter(r) { |
|
if (typeof rowKeyName === 'function') { |
|
if ((rowKeyName(r)) === rowKey) { |
|
ret = r |
|
return true |
|
} |
|
} |
|
else { |
|
if (Reflect.has(r, rowKeyName) && r[rowKeyName] === rowKey) { |
|
ret = r |
|
return true |
|
} |
|
} |
|
return r[childrenColumnName] && r[childrenColumnName].some(iter) |
|
}) |
|
return ret |
|
} |
|
|
|
// const row = dataSourceRef.value.find(r => { |
|
// if (typeof rowKeyName === 'function') { |
|
// return (rowKeyName(r) as string) === rowKey |
|
// } else { |
|
// return Reflect.has(r, rowKeyName) && r[rowKeyName] === rowKey |
|
// } |
|
// }) |
|
return findRow(dataSourceRef.value) |
|
} |
|
|
|
async function fetch(opt?: FetchParams) { |
|
const { api, searchInfo, defSort, fetchSetting, beforeFetch, afterFetch, useSearchForm, pagination } = unref(propsRef) |
|
if (!api || !isFunction(api)) |
|
return |
|
try { |
|
setLoading(true) |
|
const { pageField, sizeField, listField, totalField } = Object.assign({}, FETCH_SETTING, fetchSetting) |
|
let pageParams: Recordable = {} |
|
|
|
const { current = 1, pageSize = PAGE_SIZE } = unref(getPaginationInfo) as PaginationProps |
|
|
|
if ((isBoolean(pagination) && !pagination) || isBoolean(getPaginationInfo)) { |
|
pageParams = {} |
|
} |
|
else { |
|
pageParams[pageField] = (opt && opt.page) || current |
|
pageParams[sizeField] = pageSize |
|
} |
|
|
|
const { sortInfo = {}, filterInfo } = searchState |
|
|
|
let params: Recordable = merge( |
|
pageParams, |
|
useSearchForm ? getFieldsValue() : {}, |
|
searchInfo, |
|
opt?.searchInfo ?? {}, |
|
defSort, |
|
sortInfo, |
|
filterInfo, |
|
opt?.sortInfo ?? {}, |
|
opt?.filterInfo ?? {}, |
|
) |
|
if (beforeFetch && isFunction(beforeFetch)) |
|
params = (await beforeFetch(params)) || params |
|
|
|
const res = await api(params) |
|
rawDataSourceRef.value = res |
|
|
|
const isArrayResult = Array.isArray(res) |
|
|
|
let resultItems: Recordable[] = isArrayResult ? res : get(res, listField) |
|
const resultTotal: number = isArrayResult ? res.length : get(res, totalField) |
|
|
|
// 假如数据变少,导致总页数变少并小于当前选中页码,通过getPaginationRef获取到的页码是不正确的,需获取正确的页码再次执行 |
|
if (Number(resultTotal)) { |
|
const currentTotalPage = Math.ceil(resultTotal / pageSize) |
|
if (current > currentTotalPage) { |
|
setPagination({ |
|
current: currentTotalPage, |
|
}) |
|
return await fetch(opt) |
|
} |
|
} |
|
|
|
if (afterFetch && isFunction(afterFetch)) |
|
resultItems = (await afterFetch(resultItems)) || resultItems |
|
|
|
dataSourceRef.value = resultItems |
|
setPagination({ |
|
total: resultTotal || 0, |
|
}) |
|
if (opt && opt.page) { |
|
setPagination({ |
|
current: opt.page || 1, |
|
}) |
|
} |
|
emit('fetch-success', { |
|
items: unref(resultItems), |
|
total: resultTotal, |
|
}) |
|
return resultItems |
|
} |
|
catch (error) { |
|
emit('fetch-error', error) |
|
dataSourceRef.value = [] |
|
setPagination({ |
|
total: 0, |
|
}) |
|
} |
|
finally { |
|
setLoading(false) |
|
} |
|
} |
|
|
|
function setTableData<T extends Ref<Recordable<any>[]>>(values: T[]) { |
|
dataSourceRef.value = values |
|
} |
|
|
|
function getDataSource<T = Recordable>() { |
|
return getDataSourceRef.value as T[] |
|
} |
|
|
|
function getRawDataSource<T = Recordable>() { |
|
return rawDataSourceRef.value as T |
|
} |
|
|
|
async function reload(opt?: FetchParams) { |
|
return await fetch(opt) |
|
} |
|
|
|
onMounted(() => { |
|
useTimeoutFn(() => { |
|
unref(propsRef).immediate && fetch() |
|
}, 16) |
|
}) |
|
|
|
return { |
|
getDataSourceRef, |
|
getDataSource, |
|
getRawDataSource, |
|
getRowKey, |
|
setTableData, |
|
getAutoCreateKey, |
|
fetch, |
|
reload, |
|
updateTableData, |
|
updateTableDataRecord, |
|
deleteTableDataRecord, |
|
insertTableDataRecord, |
|
findTableDataRecord, |
|
handleTableChange, |
|
} |
|
}
|
|
|