import { Button, Tree, Card, Form, Input, Space, Table, Tooltip, Popover, Dropdown, type TableProps, type TableColumnType, type TreeProps, ConfigProvider, Flex, Divider, } from "antd"; import type {XinTableProps, XinTableInstance, RequestParams, FormMode} from "./typings"; import SearchForm from "./SearchForm"; import XinForm, {type XinFormRef} from "@/components/XinForm"; import {type ReactNode, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState} from "react"; import type {FormColumn} from "@/components/XinFormField/FieldRender/typings"; import {Delete, List, Create, Update} from "@/api/common/table.ts"; import {isArray, isEmpty, omit} from "lodash"; import type {SearchProps} from "antd/es/input"; import { BorderlessTableOutlined, BorderOutlined, ColumnHeightOutlined, DeleteOutlined, EditOutlined, PlusOutlined, ReloadOutlined, SearchOutlined, SettingOutlined } from "@ant-design/icons"; import {useTranslation} from "react-i18next"; import type {DataNode} from "antd/es/tree"; import AuthButton from "@/components/AuthButton"; // 默认分页大小 const DEFAULT_PAGE_SIZE: number = 10; // 默认页数 const DEFAULT_PAGE: number = 1; export default function XinTable = any>(props: XinTableProps) { const { api, accessName, rowKey, columns, tableRef, formProps, modalProps, cardProps, searchProps, operateProps = {}, pagination: customPagination = {}, addShow = true, editShow = true, deleteShow = true, searchShow = true, operateShow = true, paginationShow = true, keywordSearchShow = true, toolBarRender: customToolBalRender, actionBarRender: customActionBarRender, operateRender: customOperateRender, handleRequest: customHandleRequest, requestParams: customRequestParams, handleFinish: customHandleFinish, } = props; const {t} = useTranslation(); const formRef = useRef>(null); // 内部状态 const [loading, setLoading] = useState(true); const [dataSource, setDataSource] = useState([]); const [total, setTotal] = useState(0); const [requestParams, setRequestParams] = useState({ page: DEFAULT_PAGE, pageSize: DEFAULT_PAGE_SIZE, }); const [density, setDensity] = useState(); const [bordered, setBordered] = useState(); const [searchRef] = Form.useForm(); const [columnsChecked, setColumnsChecked] = useState([]); const [searchRender, setSearchRender] = useState(false); // 表单模式状态 const [formMode, setFormMode] = useState('create'); const [formDefaultValues, setFormDefaultValues] = useState(undefined); /** 表格请求 */ const handleRequest = useCallback(async (params?: RequestParams) => { try { setLoading(true); const defaultParams: RequestParams = Object.assign({ page: DEFAULT_PAGE, pageSize: DEFAULT_PAGE_SIZE, }, params); // 自定义参数处理 const finalParams: RequestParams = customRequestParams ? customRequestParams(defaultParams) : defaultParams; // 自定义请求 let listData: { data: T[]; total: number }; if (customHandleRequest) { listData = await customHandleRequest(finalParams); } else { const { data } = await List(api, finalParams); listData = data.data!; } setDataSource(listData.data); setTotal(listData.total); } finally { setLoading(false); } }, [api, customHandleRequest, customRequestParams]); /** 新增按钮点击 */ const handleCreate = useCallback(() => { setFormMode('create'); setFormDefaultValues(undefined); formRef.current?.resetFields(); formRef.current?.open(); }, []); /** 修改按钮点击 */ const handleUpdate = useCallback((record: T) => { setFormMode('update'); setFormDefaultValues(record); formRef.current?.setFieldsValue(record); formRef.current?.open(); }, []); // 暴露方法到 tableRef useImperativeHandle(tableRef, (): XinTableInstance => ({ reload: async () => { await handleRequest(); }, reset: async () => { searchRef.resetFields(); setRequestParams({ page: 1, pageSize: 10 }); await handleRequest({ page: 1, pageSize: 10 }); }, getDataSource: () => dataSource, setDataSource, getTotal: () => total, getLoading: () => loading, setLoading, setPageInfo: (page?, pageSize?) => { setRequestParams(prev => ({ ...prev, ...(page !== undefined && { page }), ...(pageSize !== undefined && { pageSize }), })); }, getForm: () => formRef.current, getSearchForm: () => searchRef, })); /** 初始化 */ useEffect(() => { void handleRequest(); }, []); /** 处理表格变化 */ const handleTableChange: TableProps['onChange'] = async (newPagination, newFilters, newSorter) => { const params: RequestParams = { ...requestParams, page: newPagination.current ?? requestParams.page, pageSize: newPagination.pageSize ?? requestParams.pageSize, }; // 处理筛选 if (!isEmpty(newFilters)) { params.filterValues = newFilters; } // 处理排序 if (newSorter && !isArray(newSorter) && !isEmpty(newSorter) && newSorter.field) { params.sorterValue = { field: String(newSorter.field), order: newSorter.order === 'ascend' ? 'asc' : 'desc', }; } else { delete params.sorterValue; } setRequestParams(params); await handleRequest(params); }; /** 快速搜索 */ const handleKeywordSearch: SearchProps['onSearch'] = async (value: string) => { if( !value ) { window.$message?.warning(t('xin.table.keywordEmpty')); return; } const params: RequestParams = { ...requestParams, page: 1, keywordSearch: value, } setRequestParams(params); await handleRequest(params); }; /** 快速搜索 Change */ const keywordSearchChange: SearchProps['onChange'] = (e) => { if( !e.target.value ) { const params = { ...requestParams }; delete params.keywordSearch; setRequestParams(params); } else { setRequestParams({ ...requestParams, keywordSearch: e.target.value, }); } }; /** 搜索表单提交 */ const handleSearch = async () => { const searchValues: T = searchRef.getFieldsValue(); // 移除 空值 Object.keys(searchValues).forEach((key) => { if (searchValues[key] === '' || searchValues[key] === undefined) { delete searchValues[key]; } }); await handleRequest({ page: 1, ...requestParams, ...searchValues, }); }; /** 搜索列 */ const searchColumn: FormColumn[] = useMemo(() => { if(!searchShow) return []; return columns.filter((column) => column.hideInSearch !== true); }, [columns, searchShow]); /** 表单列 */ const formColumn: FormColumn[] = useMemo(() => { return columns.filter((column) => { if(formMode === 'update') { return column.hideInForm !== true && column.hideInUpdate !== true; } else { return column.hideInForm !== true && column.hideInCreate !== true; } }); }, [columns, formMode]); /** 默认表格列 */ const defaultTableColumns = useMemo(() => { return columns .filter((column) => column.hideInTable !== true && column.dataIndex) .map(column => omit(column, ['hideInTable', 'hideInForm', 'hideInSearch', 'search'])); }, [columns]); /** 初始化列设置树数据 */ useEffect(() => { const dataIndexList = defaultTableColumns.map(item => item.dataIndex!); setColumnsChecked(dataIndexList); }, [defaultTableColumns]); /** 表格操作列渲染 */ const operateRender = (record: T): ReactNode[] => { // 编辑按钮渲染 const editRender = (): ReactNode => { const show = typeof editShow === 'function' ? editShow(record) : editShow; return show ? ( ) : null; // 搜索按钮渲染 const searchButtonRender = searchShow ? ( ) : null; // 关键字搜索按钮渲染 const keywordSearchRender = keywordSearchShow ? ( ) : null; // 自定义顶部操作栏渲染 if (customActionBarRender) { return customActionBarRender({ add: addButtonRender, search: searchButtonRender, keywordSearch: keywordSearchRender }).filter(Boolean); } else { return [addButtonRender, searchButtonRender, keywordSearchRender].filter(Boolean); } } /** 顶部工具栏渲染 */ const toolBarRender = (): ReactNode[] => { // 刷新 const reload = (