import { ref, shallowRef, ShallowRef, Ref, effectScope, watch, onUnmounted, isRef, computed } from 'vue' import { type DataTableProps, type DataTableSlots, type PaginationProps, type PaginationSlots, NDataTable, NPagination, NButton, } from 'naive-ui' import { translation, TranslationModule, type TranslationLocale } from '../locals/translation' import { useMessage } from './useMessage' import type { UseTableOptions, TableInstanceWithComponent, TableResponse, TablePageInstanceWithComponent, TablePageProps, } from '../types/table' // 获取当前语言 const currentLocale = localStorage.getItem('locale-active') || 'zhCN' // 获取翻译文本 const hookT = (key: string, params?: string) => { const locale = currentLocale.replace('-', '_').replace(/"/g, '') as TranslationLocale const translationFn = (translation[locale as TranslationLocale] as TranslationModule).useTable[ key as keyof TranslationModule['useTable'] ] || translation.zhCN.useTable[key as keyof typeof translation.zhCN.useTable] return typeof translationFn === 'function' ? translationFn(params || '') : translationFn } /** * 表格钩子函数 * @param options 表格配置选项 * @returns 表格实例,包含表格状态和方法 */ export default function useTable, Z extends Record = Record>({ config, // 表格列配置 request, // 数据请求函数 defaultValue = ref({}) as Ref, // 默认请求参数,支持响应式 watchValue = false, // 监听参数 }: UseTableOptions) { const scope = effectScope() // 创建一个作用域,用于管理副作用 return scope.run(() => { // 表格状态 const columns = shallowRef(config) // 表格列配置 const loading = ref(false) // 加载状态 const data = ref({ list: [], total: 0 }) as Ref<{ list: T[]; total: number }> // 表格数据 const alias = ref({ total: 'total', list: 'list' }) // 表格别名 const example = ref() // 表格引用 const param = (isRef(defaultValue) ? defaultValue : ref({ ...(defaultValue as Z) })) as Ref // 表格请求参数 const total = ref(0) // 分页参数 const props = shallowRef({}) as ShallowRef // 表格属性 // const watchData = ref([]) // 监听参数 const { error: errorMsg } = useMessage() /** * 获取表格数据 */ const fetchData = async () => { try { loading.value = true const rdata: TableResponse = await request(param.value) total.value = rdata[alias.value.total as keyof TableResponse] as number data.value = { list: rdata[alias.value.list as keyof TableResponse] as [], total: rdata[alias.value.total as keyof TableResponse] as number, } return data.value } catch (error: any) { errorMsg(error.message) console.error('请求数据失败:', error) } finally { loading.value = false } } /** * 重置表格状态和数据 */ const reset = async () => { param.value = defaultValue.value return await fetchData() } /** * 渲染表格组件 */ const component = (props: DataTableProps, context: { slots?: DataTableSlots }) => { const { slots, ...attrs } = props as any const s2 = context console.log(slots, s2) return ( {{ empty: () => (slots?.empty || s2?.slots?.empty ? slots?.empty() || s2?.slots?.empty() : null), loading: () => (slots?.loading || s2?.slots?.loading ? slots?.loading() || s2?.slots?.loading() : null), }} ) } // 检测到参数变化时,重新请求数据 if (Array.isArray(watchValue)) { // 只监听指定的字段 const source = computed(() => watchValue.map((key) => param.value[key])) watch(source, fetchData, { deep: true }) } // 检测到默认参数变化时,合并参数 // watch(defaultValue, () => (param.value = { ...defaultValue.value, ...param.value }), { deep: true }) onUnmounted(() => { scope.stop() // 停止作用域 }) // 清理副作用 // 返回表格实例 return { loading, example, data, alias, param, total, reset: reset, fetch: fetchData, component, config: columns, props, } }) as TableInstanceWithComponent } /** * @description 扩展表格实例方法 */ const useTablePage = = Record>({ param, total, alias = { page: 'page', pageSize: 'page_size' }, // 字段别名映射 props = {}, slot = {}, refresh = () => {}, }: TablePageProps & { refresh?: () => void }) => { const scope = effectScope() // 创建一个作用域,用于管理副作用 return scope.run(() => { const { page, pageSize } = { ...{ page: 'page', pageSize: 'page_size' }, ...alias } const pageSizeOptionsRef = ref([10, 20, 50, 100, 200]) // 当前页码 const propsRef = ref({ ...props }) // 如果分页参数不存在,则设置默认值 if (!(param.value as Record)[page]) { ;(param.value as Record)[page] = 1 // 当前页码 } // 如果分页参数不存在,则设置默认值 if (!(param.value as Record)[pageSize]) { ;(param.value as Record)[pageSize] = 20 // 每页条数 } /** * @description 更新页码 * @param {number} currentPage 当前页码 */ const handlePageChange = (currentPage: number) => { param.value = { ...param.value, [page]: currentPage, } if (refresh) { refresh() } } /** * @description 更新每页条数 * @param {number} size 每页条数 */ const handlePageSizeChange = (size: number) => { param.value = { ...param.value, [page]: 1, // 重置页码为1 [pageSize]: size, } if (refresh) { refresh() } } // 渲染分页组件 const component = (props: PaginationProps, context: { slots?: PaginationSlots }) => { // 处理插槽 const mergedSlots = { ...slot, ...(context.slots || {}), } return ( ) } // 组件卸载 onUnmounted(() => { scope.stop() // 停止作用域 }) // 清理副作用 return { component, handlePageChange, handlePageSizeChange, pageSizeOptions: pageSizeOptionsRef, } }) as TablePageInstanceWithComponent } /** * @description 表格列hook--操作列 */ const useTableOperation = ( options: { title: string width?: number onClick: (row: any) => void isHide?: boolean | ((row: any) => boolean) }[], others?: any, ) => { const width = options.reduce((accumulator, option) => accumulator + (option.width || 40), 0) + 20 return { title: hookT('operation'), key: 'CreatedAt', width, fixed: 'right' as const, align: 'right' as const, render: (row: any) => { const buttons: JSX.Element[] = [] for (let index = 0; index < options.length; index++) { const option = options[index] const isHide = typeof option.isHide === 'function' ? option.isHide(row) : typeof option.isHide === 'boolean' ? option.isHide : false if (isHide) continue buttons.push( option.onClick(row)}> {option.title} , ) } return (
{buttons.map((button, index) => ( <> {button} {index < buttons.length - 1 && |} ))}
) }, ...others, } } export { useTablePage, useTableOperation }