react 封装一个table

封装index

import { Table, TableProps } from "antd";
import { ColumnType } from "antd/lib/table";
import { DataIndex } from "rc-table/lib/interface";
import Column from "antd/lib/table/Column";

export interface CustomColumnsType<T = unknown> extends ColumnType<T> {
  custom?: boolean;
}
export interface IRecord<T> {
  dataIndex: DataIndex;
  text: any;
  record: T;
  index: number;
}
type Props<RecordType> = Omit<TableProps<RecordType>, "children" | "columns"> & {
  children?: (record: IRecord<RecordType>) => React.ReactNode;
  columns: CustomColumnsType<RecordType>[];
};

const CustomTable = <T extends object = any>(props: Props<T>) => {
  const { columns, ...other } = props;

  const defaultRender = (text: any) => {
      return (
          <div>
              <div>{text || "--"}</div>
          </div>
      );
  };
  return (
      <Table bordered {...other}>
          {(columns || []).map(item => {
              const { dataIndex = "", title, key, ...other } = item;
              if (item.custom) {
                  return (
                      <Column
                          title={title}
                          dataIndex={dataIndex}
                          key={key}
                          align="center"
                          render={(text, record, index) => {
                              return props.children ? props.children({ dataIndex, text, record, index }) : null;
                          }}
                          {...other}
                      />
                  );
              }
              return (
                  <Column
                      title={title}
                      className={item.className}
                      dataIndex={item.dataIndex}
                      key={item.key}
                      align="center"
                      render={item.render || defaultRender}
                      {...other}
                  />
              );
          })}
      </Table>
  );
};

export default CustomTable;

config.tsx

import { Input, Select, DatePicker, MenuProps } from "antd";
import { billStatus, payStatus, payType } from "../const";
import { IFormConfig } from "@/components/SearchForm";
import { CustomColumnsType } from "@/components/Table";
import { V1OmcGameServerOrdersListData } from "@/api/modules/omgGameServer/data-contracts";

const { RangePicker } = DatePicker;
type ITableList = V1OmcGameServerOrdersListData["data"]["list"][0];

export const formConfig: IFormConfig[] = [
    { name: "startRange", children: <RangePicker /> },
    { name: "nickname", children: <Input placeholder="请输入用户昵称" /> },
    { name: "uid", children: <Input placeholder="请输入用户ID" /> },
    { name: "order_no", children: <Input placeholder="请输入充值订单号" /> },
    { name: "out_order_no", children: <Input placeholder="请输入支付订单号" /> },
    { name: "pay_status", children: <Select style={{ width: "140px" }} placeholder="请选择支付状态" options={payStatus} /> },
    { name: "status", children: <Select style={{ width: "140px" }} placeholder="请选择上账状态" options={billStatus} /> },
    { name: "pay_type", children: <Select style={{ width: "140px" }} placeholder="请选择类型" options={payType} /> }
];

export const tableColumns: CustomColumnsType<ITableList>[] = [
    { title: "订单号", dataIndex: "order_no", key: "order_no", fixed: "left", width: "200px" },
    { title: "支付时间", dataIndex: "paid_at", key: "paid_at", custom: true, width: "160px" },
    { title: "用户昵称", dataIndex: "nickname", key: "nickname", width: "120px" },
    { title: "用户ID", dataIndex: "uid", key: "uid", width: "120px" },
    { title: "金额", dataIndex: "real_money", key: "real_money", custom: true, width: "120px" },
    { title: "配置", dataIndex: "config_string", key: "config_string", width: "140px" },
    { title: "游戏服", dataIndex: "server_string", key: "server_string", width: "120px" },
    { title: "时长", dataIndex: "days", key: "days", width: "120px" },
    { title: "支付订单号", dataIndex: "out_order_no", key: "out_order_no", width: "230px" },
    { title: "类型", dataIndex: "pay_type", key: "pay_type", custom: true, width: "120px" },
    { title: "支付状态", dataIndex: "pay_status", key: "pay_status", custom: true, width: "120px" },
    { title: "上账状态", dataIndex: "status", key: "status", custom: true, width: "120px" },
    { title: "备注", dataIndex: "remark", key: "remark", width: "250px", custom: true },
    { title: "操作", dataIndex: "action", key: "action", width: "200px", custom: true }
];

export const enum ActionType {
    RESET = 1, //重启
    CLOSE = 2, //关机
    UPDATE = 3, //更新游戏
    DELAY = 4, //延时
    RELOAD = 5, //重启
    NO = 0
}
export const items: MenuProps["items"] = [
    {
        key: ActionType.RESET,
        label: "重启"
    },
    {
        key: ActionType.CLOSE,
        label: "关机"
    },
    {
        key: ActionType.UPDATE,
        label: "更新游戏"
    },
    {
        key: ActionType.DELAY,
        label: "延时"
    },
    {
        key: ActionType.RELOAD,
        label: "重启"
    }
];

界面使用

import SearchForm, { IFormConfig } from "@/shared/components/SearchForm";
import { ActionType, formConfig, items, MenuItem, tableColumns } from "./config";
import Table from "@/shared/components/Table";
import { TablePaginationConfig } from "antd/lib/table";
import { Button, Dropdown, MenuProps, Popconfirm, Space, message } from "antd";
import { useState } from "react";
import { IPageInfo } from "@/interface/common";
import api from "@/api/modules/omgGameServer/Api";
import { useDeepEqualEffect } from "@/hooks/useDeepEqualEffect";
import { V1OmcGameServerOrdersListData } from "@/api/modules/omgGameServer/data-contracts";
import findEnumLabel from "@/utils/findEnumLabel";
import { IBillStatus, billStatus, payStatus, payType } from "../const";
import dayjs from "dayjs";
import { centsToYuan } from "@/utils/util";
import parseDurationTime from "@/utils/parseDurationTime";
import UpdateImage from "./Update";
import { updateImage } from "@/api/modules/order";
import { DownOutlined } from "@ant-design/icons";
import SetDelay from "./setDelay";
const searchFormInitValue = { pay_status: "0", pay_type: "0", status: "0" };

type ITableList = V1OmcGameServerOrdersListData["data"]["list"][0];

const App: React.FC = () => {
    const [loading, setLoading] = useState(false);
    const [tableData, setTableData] = useState<ITableList[]>([]);
    const [searchFormValue, setSearchFormValue] = useState(searchFormInitValue);
    const [pageInfo, setPageInfo] = useState<IPageInfo>({
        current: 1,
        pageSize: 20,
        total: 0
    });

    const [isShowUpdateImage, setIsShowUpdateImage] = useState(false);
    const [info, setInfo] = useState({
        current_image: "",
        instance_id: 0
    });
    const [currentRecord, setCurrentRecord] = useState<ITableList | null>(null);
    const [confirmVisible, setConfirmVisible] = useState(false);
    const [currentAction, setCurrentAction] = useState<ActionType | null>(null);
    const [isShowSetDelay, setIsShowSetDelay] = useState(false);

    useDeepEqualEffect(() => {
        queryList();
    }, [searchFormValue, pageInfo.pageSize, pageInfo.current]);

    const onFinish = (values: any) => {
        const { startRange, ...other } = values;
        const [start_time, end_time] = startRange || [];
        setPageInfo({
            ...pageInfo,
            current: 1
        });
        setSearchFormValue({
            ...other,
            start_time: start_time ? dayjs(start_time.format("YYYY-MM-DD")).unix() : null,
            end_time: end_time ? dayjs(end_time.format("YYYY-MM-DD")).unix() + (24 * 60 * 60 - 1) : null
        });
    };

    const confirm = async (id?: number) => {
        if (!id) {
            message.error("id不能为空");
            return;
        }
        setLoading(true);
        await api.v1OmcGameServerOrderClearCreate({ id }).finally(() => setLoading(false));
        queryList();
        message.success("清理成功");
    };

    const queryList = async () => {
        setLoading(true);
        const { pageSize, current } = pageInfo;
        const { data } = await api
            .v1OmcGameServerOrdersList({
                ...searchFormValue,
                page: `${current}`,
                page_size: `${pageSize}`
            })
            .finally(() => setLoading(false));
        const list = data?.list;

        setTableData(list || []);
        setPageInfo({ ...pageInfo, total: data?.total || 0 });
    };
    const handleIsShowUpdateImage = (isTrue: boolean) => {
        setIsShowUpdateImage(isTrue);
    };
    const handleImage = (record: ITableList) => {
        setInfo({ current_image: record.current_image, instance_id: record.instance_id });
        handleIsShowUpdateImage(true);
    };
    /* 强制更新实例 */
    const handleSubmit = async (instance_id: number) => {
        const res: any = await updateImage(instance_id).finally(() => {
            setLoading(false);
            handleIsShowUpdateImage(false);
        });
        if (res.code == 0) {
            message.success("更新成功");
        } else {
            message.error(res.msg);
        }
    };
    const pageChange = (page: TablePaginationConfig) => {
        const { current = 1, pageSize = 20 } = page;
        setPageInfo({
            ...pageInfo,
            current,
            pageSize
        });
    };
    const handleCancle = () => {
        setCurrentAction(ActionType.NO);
        setCurrentRecord(null);
        setConfirmVisible(false);
    };
    // 处理确认后的实际操作
    const handleActionConfirm = () => {
        if (!currentAction || !currentRecord) return;
        console.log(currentAction, currentAction === ActionType.RESET);

        // 根据不同操作类型执行对应逻辑
        switch (currentAction) {
            case ActionType.RESET:
                console.log("执行重启操作", currentRecord);
                // 实际重启逻辑:api调用等
                message.success("重启成功");
                break;
            case ActionType.CLOSE:
                console.log("执行关机操作", currentRecord);
                // 实际关机逻辑
                message.success("关机成功");
                break;
            case ActionType.UPDATE:
                console.log("执行更新游戏操作", currentRecord);
                // 实际更新逻辑
                message.success("更新游戏成功");
                break;
            case ActionType.RELOAD:
                console.log("执行重开操作", currentRecord);
                // 实际重开逻辑
                message.success("重开成功");
                break;
            default:
                break;
        }

        // 关闭确认弹窗
        setConfirmVisible(false);
        // 可根据需要刷新列表
        queryList();
    };

    // 获取操作对应的中文名称(用于弹窗提示)
    const getActionName = (action: ActionType) => {
        switch (action) {
            case ActionType.RESET:
                return "重启";
            case ActionType.CLOSE:
                return "关机";
            case ActionType.UPDATE:
                return "更新游戏";
            case ActionType.RELOAD:
                return "重开";
            default:
                return "";
        }
    };

    // 点击下拉菜单项时显示确认弹窗
    const handleMenuItemClick = (record: ITableList, action: ActionType) => {
        // 保存当前操作和记录
        setCurrentAction(action);
        setCurrentRecord(record);
        // 显示确认弹窗
        if (action != ActionType.DELAY) {
            setConfirmVisible(true);
        } else {
            handleIsShowSetDelay(true);
        }
    };
    const handleIsShowSetDelay = (val: boolean) => {
        setIsShowSetDelay(val);
    };
    const handleDelaySubmit = (val: number) => {
        console.log(val, "handleDelaySubmit");
        queryList();
    };
    return (
        <div className="content-box card">
            <Space direction="vertical" size="middle">
                <SearchForm className="search-form" formConfig={formConfig} initialValues={searchFormInitValue} onFinish={onFinish} />
                <Table<ITableList>
                    columns={tableColumns}
                    scroll={{ x: 1000, y: 600 }}
                    dataSource={tableData}
                    loading={loading}
                    rowKey="id"
                    onChange={pageChange}
                    pagination={{
                        ...pageInfo
                    }}
                >
                    {({ dataIndex, text, record }) => {
                        switch (dataIndex) {
                            case "real_money":
                                return text ? `${centsToYuan(text)}元` : "-";
                            case "paid_at":
                                return text ? dayjs.unix(text).format("YYYY-MM-DD HH:mm:ss") : "-";
                            case "pay_type":
                                return findEnumLabel(payType, text);
                            case "pay_status":
                                return findEnumLabel(payStatus, text);
                            case "status":
                                return findEnumLabel(billStatus, text);
                            case "remark":
                                return (
                                    <div style={{ textAlign: "left", paddingLeft: "20px" }}>
                                        <div>最高人数:{record.max_online_num ? record.max_online_num : "--"}</div>
                                        <div>连接时长:{record.total_link_time ? parseDurationTime(record.total_link_time) : "--"}</div>
                                    </div>
                                );
                            case "action":
                                return (
                                    <div className="flex">
                                        <Popconfirm
                                            title="确定清理吗?"
                                            disabled={`${record.status}` === IBillStatus.RETURNED_BILL}
                                            onConfirm={() => confirm(record.id!)}
                                        >
                                            <Button type="link" disabled={`${record.status}` === IBillStatus.RETURNED_BILL}>
                                                清理
                                            </Button>
                                        </Popconfirm>
                                        <Button
                                            type="link"
                                            onClick={() => {
                                                handleImage(record);
                                            }}
                                        >
                                            镜像
                                        </Button>
                                        <div className="flex center">
                                            <Dropdown
                                                className="flex center"
                                                // 关键修改:通过menu的onClick回调传递record
                                                menu={{
                                                    items, // 确保类型匹配
                                                    onClick: (menuInfo: any) => {
                                                        // 同时获取菜单信息和行数据
                                                        handleMenuItemClick(record, JSON.parse(menuInfo.key));
                                                    }
                                                }}
                                            >
                                                <a onClick={e => e.preventDefault()}>
                                                    <Space>
                                                        更多
                                                        <DownOutlined />
                                                    </Space>
                                                </a>
                                            </Dropdown>
                                            <Popconfirm
                                                open={confirmVisible && currentAction && currentRecord && record.id === currentRecord.id}
                                                title={`确定要${currentAction ? getActionName(JSON.parse(currentAction + "")) : ""}吗?`}
                                                onConfirm={handleActionConfirm}
                                                onCancel={() => handleCancle()}
                                                okText="确认"
                                                cancelText="取消"
                                            />
                                        </div>
                                    </div>
                                );
                        }
                    }}
                </Table>
            </Space>

            <UpdateImage
                isShow={isShowUpdateImage}
                handleClose={() => {
                    handleIsShowUpdateImage(false);
                }}
                handleSubmit={handleSubmit}
                info={info}
            />
            <SetDelay
                isShow={isShowSetDelay}
                handleClose={() => {
                    handleIsShowSetDelay(false);
                }}
                handleSubmit={handleDelaySubmit}
            />
        </div>
    );
};

export default App;

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容