关于在antv chart 的开发过程

60 阅读5分钟

相关链接

开发过程中我发现文档信息不全面,根据ai的一些总结发现并不适用当前的版本或者可以说不适用当前组件, 只能在开发过程中不停摸索,有一些是在线示例中实现的代码,在本地项目中并没有任何表现,所以说只能是自己在相关链接里翻找,文档中的api在项目中也实现不了。先这样吧,有空再详写

柱图高级

api

高级交互


import { getChartData } from '@/controllers/API/leafNode';
import { SelectedOptionContext } from '@/layouts/context';
import { getUrlParam } from '@/utils/util';
import { CopyOutlined } from '@ant-design/icons';
import { Column, Pie } from '@ant-design/plots';
import { PageContainer } from '@ant-design/pro-components';
import {
    Breadcrumb,
    Button,
    Col,
    Form,
    Input,
    InputNumber,
    message,
    Modal,
    Row,
    Select,
    Spin,
    Switch,
    Table,
    Tooltip,
    Typography,
} from 'antd';
import { ColumnsType } from 'antd/es/table';
import copy from 'copy-to-clipboard';
import React, { useEffect, useState } from 'react';
// import { CopyToClipboard } from 'react-copy-to-clipboard';
import { history } from '@ali/lfe';

const { Title } = Typography;

const TableList: React.FC<unknown> = () => {
    const [form] = Form.useForm();
    const [listRecord, setListRecord] = useState<any>({});
    const [filterData, setFilterData] = useState<any>();
    const [paginationData, setPaginationData] = useState<any>();
    const [valueR, setValueR] = useState<string>('GREATER_THAN');
    const [valueV, setValueV] = useState<string | number | undefined>();
    const [occurR, setOccurR] = useState<string>('GREATER_THAN');
    const [occurV, setOccurV] = useState<string | number | undefined>();
    const [isBetweenV, setIsBetweenV] = useState<boolean>(false);
    const [isBetweenO, setIsBetweenO] = useState<boolean>(false);

    const [numData, setNumData] = useState(null);
    const [enumData, setEnumData] = useState(null);
    const [isNumModal, setIsNumModal] = useState(false);
    const [isEnumModal, setIsEnumModal] = useState(false);

    const [isModalOpen, setIsModalOpen] = useState(false);
    const [modalContent, setModalContent] = useState('');

    const { globalAppName, globalAoneId } = React.useContext(SelectedOptionContext);
    const name = globalAppName;
    const aoneID = globalAoneId;

    useEffect(() => {
        if (name || aoneID) {
            const currentUrl = window.location.href;
            const url = new URL(currentUrl);
            const params = new URLSearchParams('?' + new URL(currentUrl).href.split('?')[1]);

            if (name) {
                params.set('appName', name);
            }

            if (aoneID) {
                params.set('aoneID', aoneID);
            }
            const newUrl = `${url.origin}${url.pathname}?${params.toString()}`;
            window.history.replaceState(null, '', newUrl);
        }
    }, [location, history, name, aoneID]);

    const changeFeature = (params) => {
        fetch(`/api/feature/feature?id=${params.id}&isFeature=${params.isFeature}`)
            .then((res) => res.json())
            .then((res) => {
                if (res && res.errorCode === 0) {
                    getListData({ pageNum: listRecord?.current, pageSize: listRecord?.size });
                }
            });
    };

    const changeEnum = (params) => {
        fetch(`/api/feature/enum?id=${params.id}&isEnum=${params.isEnum}`)
            .then((res) => res.json())
            .then((res) => {
                if (res && res.errorCode === 0) {
                    getListData({
                        pageNum: listRecord?.current,
                        pageSize: listRecord?.size,
                    });
                }
            });
    };

    const areNumbers = (arr: any[]) => {
        if (arr) {
            return arr?.every(function (item) {
                return typeof item === 'number';
            });
        }
    };

    const areStringToNumbers = (arr) => {
        if (arr) {
            return arr?.every((item) => {
                return !isNaN(parseInt(item)) || !isNaN(parseFloat(item));
            });
        }
    };

    const pathTypes = ['id', 'num', 'enum', 'time', 'geo', 'text'];

    const columns: ColumnsType<{
        id: number;
        name: string; // 名称
        gmtCreate: Date; // 创建时间
    }> = [
        {
            title: 'ID',
            dataIndex: 'id',
            align: 'center',
            width: 80,
        },
        {
            title: '数据集ID',
            dataIndex: 'datasetId',
            width: 80,
            align: 'center',
        },
        {
            title: '接口',
            dataIndex: 'requestPathCute',
            ellipsis: true,
            width: 100,
            render: (text) => {
                return (
                    <Tooltip
                        title={text}
                        overlayInnerStyle={{
                            // background: '#fff',
                            // color: '#333',
                            maxHeight: '300px',
                            overflowY: 'auto',
                        }}
                        overlayStyle={{
                            minWidth: '400px',
                        }}
                    >
                        <div
                            style={{
                                width: '100%',
                                overflow: 'hidden',
                                textOverflow: 'ellipsis',
                                whiteSpace: 'nowrap',
                                wordBreak: 'break-all',
                            }}
                        >
                            {text}
                        </div>
                    </Tooltip>
                );
            },
        },
        {
            title: '特征路径',
            dataIndex: 'jmespath',
            width: 250,
            render: (text) => {
                return (
                    <Tooltip
                        title={text}
                        overlayInnerStyle={{
                            maxHeight: '300px',
                            overflowY: 'auto',
                        }}
                        overlayStyle={{
                            minWidth: '400px',
                        }}
                    >
                        <div
                            style={{
                                // overflow: 'hidden',
                                // textOverflow: 'ellipsis',
                                // whiteSpace: 'nowrap',
                                wordBreak: 'break-all',
                            }}
                        >
                            {text}
                        </div>
                    </Tooltip>
                );
            },
        },
        {
            title: 'value数量',
            dataIndex: 'valuesSetCount',
            width: 100,
        },
        // {
        //     title: '业务相关度',
        //     dataIndex: 'occurSetRatio',
        //     width: 100,
        // },
        {
            title: '是否特征',
            dataIndex: 'isFeature',
            align: 'center',
            width: 110,
            filters: [
                {
                    text: '是',
                    value: 1,
                },
                {
                    text: '否',
                    value: 0,
                },
            ],
            render: (text, record: any) => {
                const isFeatureDisabled = record.pathType !== 'num' && record.pathType !== 'enum'; // 检查pathType是否为num或enum
                return record.isEnum === null ? (
                    <span />
                ) : (
                    <Switch
                        size="small"
                        checkedChildren="取消特征"
                        unCheckedChildren="设为特征"
                        checked={record.isFeature}
                        disabled={isFeatureDisabled} // 根据节点类型是否禁用
                        onChange={(e) => {
                            if (isFeatureDisabled) return; // 如果禁用则不执行
                            if (
                                record.isEnum === 1 &&
                                record.valuesSetCount > 100 &&
                                record.isFeature === 0
                            ) {
                                Modal.confirm({
                                    title: '该节点Value数量过多,若设置为特征,可能将显著增加场景语料数,请确认其业务必要性及其生产语料可能引起的量级增加,如仍确认,请点击确定',
                                    onOk: () => {
                                        changeFeature({
                                            id: record.id,
                                            isFeature: e ? 1 : 0,
                                        });
                                    },
                                });
                            } else {
                                changeFeature({
                                    id: record.id,
                                    isFeature: e ? 1 : 0,
                                });
                            }
                        }}
                    />
                );
            },
        },

        {
            title: '字段类型',
            dataIndex: 'pathType',
            width: 100,
            align: 'center',
            filters: [
                { text: 'ID', value: 'id' },
                { text: 'NUM', value: 'num' },
                { text: 'ENUM', value: 'enum' },
                { text: 'TIME', value: 'time' },
                { text: 'GEO', value: 'geo' },
                { text: 'TEXT', value: 'text' },
            ],
            render: (text) => <span>{text}</span>,
        },
        {
            title: '字段类型判断原因',
            dataIndex: 'analyRes',
            align: 'center',
            width: 200,
            render(value, record, index) {
                const analyRes = JSON.parse(record.analyRes || '{}');
                let shortContent = '';
                if (analyRes.user_text !== undefined && analyRes.user_text !== null) {
                    shortContent = analyRes.user_text.slice(0, 50) + '...';
                }
                return (
                    <>
                        <span>{shortContent}</span>
                        <Button
                            type="link"
                            onClick={() => {
                                setModalContent(analyRes.raw_res || '暂无详细信息');
                                setIsModalOpen(true);
                            }}
                        >
                            查看详情
                        </Button>
                    </>
                );
            },
        },
        // {
        //     title: '枚举/数值',
        //     dataIndex: 'isEnum',
        //     align: 'center',
        //     width: 110,
        //     filters: [
        //         {
        //             text: '枚举',
        //             value: 1,
        //         },
        //         {
        //             text: '数值',
        //             value: 0,
        //         },
        //     ],
        //     render: (text, record) => {
        //         let valueSetData = [];
        //         try {
        //             if (record.valuesSet) {
        //                 valueSetData = JSON.parse(record.valuesSet);
        //             }
        //         } catch (error) {}
        //         return record.isEnum === null ? (
        //             <span />
        //         ) : (
        //             <Switch
        //                 size="small"
        //                 checkedChildren="枚举"
        //                 unCheckedChildren="数值"
        //                 disabled={
        //                     (record.isEnum === 0 && record.valuesSetCount >= 100) ||
        //                     (record.isEnum === 1 &&
        //                         !areNumbers(valueSetData) &&
        //                         !areStringToNumbers(valueSetData))
        //                 }
        //                 checked={record.isEnum}
        //                 onChange={(e) => {
        //                     changeEnum({
        //                         id: record.id,
        //                         isEnum: e ? 1 : 0,
        //                     });
        //                 }}
        //             />
        //         );
        //     },
        // },
        {
            title: '人工修改',
            dataIndex: 'updateBy',
            align: 'center',
            width: 100,
            filters: [
                {
                    text: 'AUTO',
                    value: 'AUTO',
                },
                {
                    text: 'USER',
                    value: 'USER',
                },
            ],
        },
        {
            title: '特征值',
            dataIndex: 'valuesSet',
            width: 240,
            render: (text, record) => {
                return (
                    <div style={{ display: 'flex', justifyContent: 'flex-start' }}>
                        <Tooltip
                            title={text}
                            overlayInnerStyle={{
                                maxHeight: '300px',
                                overflowY: 'auto',
                            }}
                            overlayStyle={{
                                minWidth: '400px',
                            }}
                        >
                            <div
                                style={{
                                    overflow: 'hidden',
                                    textOverflow: 'ellipsis',
                                    whiteSpace: 'nowrap',
                                    wordBreak: 'break-all',
                                }}
                            >
                                {text}
                            </div>
                        </Tooltip>
                        <CopyOutlined
                            style={{
                                display: 'inline-block',
                                cursor: 'pointer',
                                width: '14px',
                                height: '14px',
                                fontSize: '14px',
                                color: '#1890ff',
                                marginTop: '4px',
                            }}
                            title="拷贝"
                            onClick={() => {
                                copy(text);
                                message.success('复制成功');
                            }}
                        />
                    </div>
                );
            },
        },
        {
            title: '判定特征原因',
            dataIndex: 'judgeFeatureReason',
            width: 140,
            align: 'center',
        },

        {
            title: '值分布情况',
            align: 'center',
            width: 100,
            render: (_: any, record: { id: number }) => {
                const loading = loadingStates[record.id]; // 获取当前行的 loading 状态
                return record?.pathType === 'num' || record?.pathType === 'enum' ? (
                    <Button
                        type="link"
                        onClick={() => {
                            message.warning('生成中,请稍候...');
                            fetchDistributionData(record);
                        }} // 点击“查看”
                        loading={loading} // 控制 loading 状态
                        disabled={loading} // 根据状态禁用按钮
                    >
                        {loading ? '加载中' : '查看'}
                    </Button>
                ) : null;
            },
        },
    ];

    const [pathType, setPathType] = useState<string>(''); // 默认路径类型
    // 获取
    const getListData = async (params?: any, isReset?: boolean) => {
        const values = await form.validateFields(); // 获取from表单的值
        const encodeJmesPath = values.jmespath ? encodeURIComponent(values.jmespath) : '';
        const encodeUrl = values.url ? encodeURIComponent(values.url) : '';
        const bodyData = [];
        if (valueR && valueV && valueV !== '-') {
            bodyData.push({
                name: 'valuesSetCount',
                relation: valueR,
                value: valueV,
            });
        }
        if (occurR && occurV && occurV !== '-') {
            bodyData.push({
                name: 'occurSetRatio',
                relation: occurR,
                value: occurV,
            });
        }
        fetch(
            `/api/feature/list?url=${encodeUrl}&datasetId=${values.datasetId}&jmespath=${encodeJmesPath}&pageNum=${paginationData?.current || 1}&pageSize=${paginationData?.pageSize || 10}&isFeature=${
                filterData?.isFeature && filterData?.isFeature?.length === 1
                    ? filterData.isFeature[0]
                    : ''
            }&isEnum=${
                filterData?.isEnum && filterData?.isEnum?.length === 1 ? filterData.isEnum[0] : ''
            }&updateBy=${
                filterData?.updateBy && filterData?.updateBy?.length === 1
                    ? filterData.updateBy[0]
                    : ''
            }&pathType=${filterData?.pathType ? filterData.pathType.join() : ''}`,
            {
                method: 'POST',
                body: JSON.stringify(isReset ? [] : bodyData),
                headers: {
                    'Content-Type': 'application/json',
                },
            },
        )
            .then((res) => res.json())
            .then((res) => {
                if (res && res.errorCode === 0) {
                    setListRecord(res.data);

                    return res.data;
                }
            });
    };

    useEffect(() => {
        getListData();
    }, []);

    useEffect(() => {
        getListData({
            pageNum: listRecord?.current,
            pageSize: listRecord?.size,
        });
    }, [filterData]);

    const BreadData = [
        {
            title: <span style={{ color: '#1890ff' }}>应用列表</span>,
            href: '/app/list',
        },
        {
            title: getUrlParam()?.appName,
        },
        {
            title: '模型管理',
            href: `/coverage/scenario/dataset/baseline?appName=${getUrlParam()?.appName}&aoneID=${getUrlParam()?.aoneID}`,
        },
        {
            title: '基线数据集详情',
            href: `/coverage/scenario/dataset/baseline/detail?datasetId=${getUrlParam()?.datasetId}&datasetType=${getUrlParam()?.datasetType}&appName=${getUrlParam()?.appName}&aoneID=${getUrlParam()?.aoneID}`,
        },
        {
            title: '叶节点列表',
        },
    ];

    const relationOption = [
        {
            label: '大于',
            value: 'GREATER_THAN',
        },
        {
            label: '等于',
            value: 'EQUAL_TO',
        },
        {
            label: '不等于',
            value: 'NOT_EQUAL_TO',
        },
        {
            label: '小于',
            value: 'LESS_THAN',
        },
        {
            label: '介于',
            value: 'BETWEEN',
        },
        {
            label: '小于等于',
            value: 'LESS_THAN_OR_EQUAL_TO',
        },
        {
            label: '大于等于',
            value: 'GREATER_THAN_OR_EQUAL_TO',
        },
    ];

    const [isLoading, setIsLoading] = useState(false);
    const [activeModal, setActiveModal] = useState<'num' | 'enum' | null>(null); // 控制是打开数值还是枚举的 modal
    const [loadingStates, setLoadingStates] = useState<{ [key: number]: boolean }>({}); // 每行的loading状态

    const fetchDistributionData = async (val) => {
        const id = val.id; // 获取行 id
        if (loadingStates[id]) {
            // 如果此行正在加载
            message.warning('当前正在加载,请稍候...');
            return; // 直接返回,阻止新的请求
        }

        // 更新行的 loading 状态
        setLoadingStates((prevState) => ({ ...prevState, [id]: true }));

        try {
            const res = await getChartData({
                urlId: Number(getUrlParam()?.urlId),
                jmespath: getUrlParam()?.jmespath || val?.jmespath,
                pathType: val?.pathType,
            });

            if (val?.pathType === 'num') {
                setIsNumModal(true);
                setNumData(res?.data);
                setActiveModal('num'); // 设置当前打开的是数值 modal
            } else if (val?.pathType === 'enum') {
                setEnumData(res?.data);
                setIsNumModal(false);
                setActiveModal('enum'); // 设置当前打开的是枚举 modal
            }
        } catch (error) {
            message.error('数据请求失败,请重试');
        } finally {
            setLoadingStates((prevState) => ({ ...prevState, [id]: false })); // 请求结束,关闭 loading 状态
        }
    };

    const stats = [
        { label: '最小值', value: numData?.minValue },
        { label: '最大值', value: numData?.maxValue },
        { label: '平均值', value: numData?.average },
        { label: '中位数', value: numData?.median },
        { label: '标准差', value: numData?.stddev },
        { label: '九分位数', value: numData?.percentile90 },
        { label: '总数据量(条)', value: numData?.total },
    ];
    const formatNumber = (num) => {
        const parsedNum = parseFloat(num);
        if (isNaN(parsedNum)) return '—';
        const roundedNum = Math.round(parsedNum * 100) / 100;
        if (Number.isInteger(roundedNum)) {
            return roundedNum.toLocaleString();
        } else {
            return roundedNum.toFixed(2).toLocaleString().replace(/\.00$/, ''); // 移除 .00
        }
    };
    const showNumChart = (data) => {
        if (!data || !data.intervalRatios) {
            return <div>没有可用于展示的数据</div>; // 防止无效数据时抛出错误
        }

        const chartData = Object.entries(data?.intervalRatios).map(([key, value]) => ({
            interval: key,
            count: value.count,
        }));

        const config = {
            data: chartData,
            xField: 'interval',
            yField: 'count',
            title: {
                title: '数据区间分布',
                align: 'center',
            },

            label: {
                text: (d) =>
                    d.count == 0
                        ? d?.count
                        : `${d?.count}${((d?.count / numData?.total) * 100).toFixed(2)}%`,
                textBaseline: 'bottom',
            },

            axis: {
                // y: { labelFormatter: '~s' },
                x: {
                    // labelSpacing: 4,
                    style: {
                        labelTransform: 'rotate(-45)',
                    },
                },
            },

            // xAxis: {
            //     label: {
            //         autoHide: false, // 可以根据需要设置为 true 来隐藏超出范围的标签
            //         autoRotate: false, // 关闭自动旋转,因为我们手动设置了旋转角度
            //         angle: 45, // 设置为 45 度
            //         style: {
            //             fontSize: 12,
            //             padding: [5, 0], // 上下间距
            //         },
            //     },
            // },
            // meta: {
            //     type: {
            //         alias: '类别',
            //     },
            //     sales: {
            //         alias: '销售额',
            //     },
            // },
            // label: {
            //     // content: (item) => {
            //     //     const percentage = ((item.sales / 10000) * 100).toFixed(2); // 计算百分比
            //     //     return `${item.sales}--${percentage}`; // 返回希望显示的内容
            //     // },
            //     position: 'top', // label 显示的位置
            //     style: { fill: '#000', fontSize: 12 }, // label 的样式
            // },

            // axis: {
            //     y: {
            //         labelFormatter: '.0%',
            //     },
            // },
            style: {
                // 圆角样式
                // radiusTopLeft: 10,
                // radiusTopRight: 10,
                // minWidth: '100%',
            },
        };

        return (
            <div style={{ isolation: 'isolate' }}>
                <div style={{ marginBottom: '10px', color: 'gray' }}>
                    {stats.map(({ label, value }) => (
                        <span key={label} style={{ marginRight: '15px' }}>
                            <b>{`${label}`}</b>
                            {`:${formatNumber(value)}`}
                        </span>
                    ))}
                </div>
                <Column {...config} />
            </div>
        );
    };

    const showEnumChart = (data) => {
        if (!data || !data.enumRatios) {
            return <div>没有可用于展示的数据</div>; // 防止无效数据时抛出错误
        }

        // 原积色
        const startColor = '#0e205b'; // 起始颜色
        const endColor = '#fbfbd5'; // 终止颜色

        // 简单生成颜色的方法,返回所需数量的渐变颜色
        const getColorScale = (startColor, endColor, steps) => {
            const start = hexToRgb(startColor);
            const end = hexToRgb(endColor);
            const colorScale = [];

            for (let i = 0; i < steps; i++) {
                const r = Math.round(start[0] + (end[0] - start[0]) * (i / (steps - 1)));
                const g = Math.round(start[1] + (end[1] - start[1]) * (i / (steps - 1)));
                const b = Math.round(start[2] + (end[2] - start[2]) * (i / (steps - 1)));
                colorScale.push(`rgb(${r},${g},${b})`);
            }

            return colorScale;
        };

        function hexToRgb(hex) {
            hex = hex.replace('#', '');
            const r = parseInt(hex.substring(0, 2), 16);
            const g = parseInt(hex.substring(2, 4), 16);
            const b = parseInt(hex.substring(4, 6), 16);
            return [r, g, b];
        }
        const totalCount = data?.total || 0; // 总数据量

        // 处理图表数据
        const chartData = Object.entries(data?.enumRatios)
            .map(([key, value]) => {
                const count = value?.count || 0; // 获取count
                const ratio = parseFloat(value?.ratio) || 0; // 获取ratio并解析为浮点数

                // 计算基于count的百分比
                const percentage =
                    totalCount > 0 ? ((count / totalCount) * 100).toFixed(6) : '0.000000'; // 计算百分比
                return {
                    value: key,
                    count: count,
                    percentage: parseFloat(percentage), // 转换为浮点数用于后续处理
                };
            })
            // .sort((a, b) => a.count - b.count);
            .sort((a, b) => b.count - a.count); // 从大到小排序

        const colorScale = getColorScale(startColor, endColor, chartData?.length);

        // 更新饼图配置
        const config = {
            data: chartData,
            angleField: 'count', // 使用计算后的百分比
            colorField: 'value',
            innerRadius: 0.5,
            // color: colorPalette, // 添加这个属性,自定义颜色
            // label: {
            //     text: (d) => `${d.type}\n ${d.value}`,
            //     position: 'spider',
            // },
            // legend: {
            //     color: {
            //         title: false,
            //         position: 'right',
            //         rowPadding: 5,
            //     },
            // },
            // scale: {
            //     color: {
            //         palette: 'ylGnBu',
            //     },
            // },
            scale: {
                color: {
                    range: colorScale,

                    // palette: 'YlGnBu',
                },
            },

            label: {
                position: 'spider',
                text: (d) => `${d.value}${d.percentage.toFixed(2)}%`, // 显示为 "名称 百分比"
            },

            // legend: false,
            legend: {
                color: {
                    // layout: 'vertical',
                    maxRows: 3, // 调整行数以适应更多内容
                    layout: 'vertical',
                    position: 'right', // 可以再改为 'top', 'bottom'
                    // flipPage: false,
                    // maxRow: 10000,
                    // maxRows: 99,
                    // rowPadding: 5,
                    // marker: {
                    //     style: {
                    //         size: 6, // 缩小标记点
                    //     },
                    // },
                    // pageNavigator: false, // 彻底禁用分页器
                    itemMarker: {
                        style: {
                            size: 6,
                        },
                    },
                    itemLabelText: (_, index) => {
                        const match = chartData.find((item) => {
                            return item.value === _.label;
                        });

                        if (match) {
                            return `${_.label}:${match.count}(${match.percentage.toFixed(2)}%)`;
                        }

                        return '';
                    },
                    // itemLabelText: (_, index) => logo[index][0],
                    // maxRows: 1,
                },
            },

            legend: {
                // itemName: {
                //     formatter: (name, item) => {
                //         const match = chartData.find((chartItem) => chartItem.value === name);
                //         return match
                //             ? `${name}: ${match.count} (${match.percentage.toFixed(2)}%)`
                //             : name; // 完全显示数据
                //     },
                // },
                // itemMarker: {
                //     style: {
                //         size: 6,
                //     },
                // },
                // maxRows: 5, // 可以根据需要调整行数
            },

            style: {
                // 圆角样式
                // radiusTopLeft: 10,
                // radiusTopRight: 10,
                minWidth: '100%',
            },
            // style: {
            //     height: '600px', // 根据需要设置高度,您可以调整此值
            // },
            tooltip: ({ type, value }) => {
                // Extra fields
                return { type, value };
            },
            interactions: [{ type: 'element-active' }], // 鼠标悬停高亮显示
            interaction: {
                tooltip: {
                    render: (e, { items }) => {
                        return (
                            <React.Fragment>
                                {items.map((item) => {
                                    const { type, value, color } = item;

                                    // 找到当前类型在 chartData 中对应的数据
                                    const correspondingData = chartData.find(
                                        (chartItem) => chartItem.value === value,
                                    );
                                    const count = correspondingData ? correspondingData.count : 0; // 找到的 count
                                    const percentage = correspondingData
                                        ? correspondingData.percentage
                                        : '0.000000'; // 找到的比例,转为百分比

                                    return (
                                        <div
                                            key={type}
                                            style={{
                                                margin: 0,
                                                display: 'flex',
                                                justifyContent: 'space-between',
                                            }}
                                        >
                                            <div>
                                                <span
                                                    style={{
                                                        display: 'inline-block',
                                                        width: 6,
                                                        height: 6,
                                                        borderRadius: '50%',
                                                        backgroundColor: color,
                                                        marginRight: 6,
                                                    }}
                                                ></span>
                                                <span>{type}</span>
                                            </div>
                                            <b style={{ marginRight: '3px' }}>{value + ':'}</b>
                                            {`${count}`}
                                            {count ? ` (${percentage} %)` : null}
                                        </div>
                                    );
                                })}
                            </React.Fragment>
                        );
                    },
                },
            },
        };

        return (
            <div>
                <div style={{ marginBottom: '10px' }}>
                    <span style={{ marginRight: '15px', color: 'gray' }}>
                        <b>{`总数据量(条)`}</b>
                        {`:${formatNumber(data?.total)}`}
                    </span>
                </div>
                <Pie {...config} />
            </div>
        );
    };
    const [urlParams, setUrlParams] = useState({}); // 用于存储 URL 的所有参数

    const getUrlParams = () => {
        const searchParams = new URLSearchParams(window.location.search);
        const params = {
            datasetId: searchParams.get('datasetId') || '',
            url: searchParams.get('url') || '',
            jmespath: searchParams.get('jmespath') || '',
        };
        return params;
    };

    // 使用URL参数填充表单
    useEffect(() => {
        const params = getUrlParams();
        setUrlParams(params); // 保存当前的 URL 参数
        form.setFieldsValue(params); // 填充表单字段
    }, [form]);

    const onFinish = (values) => {
        const params = new URLSearchParams(window.location.search);

        params.set('datasetId', values.datasetId || '');
        params.set('url', values.url || '');
        params.set('jmespath', values.jmespath || '');

        const newUrl = `${window.location.pathname}?${params.toString()}`;
        window.history.pushState({}, '', newUrl);
    };

    useEffect(() => {
        if (activeModal !== null) {
            setTimeout(() => {
                window.dispatchEvent(new Event('resize')); // 手动触发窗口大小改变事件
            }, 0);
        }
    }, [activeModal]);

    return (
        <PageContainer
            header={{
                title: '',
                children: <Breadcrumb items={BreadData} style={{ height: '100%' }} />,
            }}
            style={{ overflow: 'auto', marginTop: '8px' }}
        >
            <div
                style={{
                    background: '#fff',
                    padding: '20px 20px 10px',
                    borderRadius: '10px',
                }}
            >
                <Title level={4} style={{ margin: '0px' }}>
                    特征查询
                </Title>

                <Form
                    form={form}
                    name="basic"
                    initialValues={{ remember: true }}
                    autoComplete="off"
                    onFinish={onFinish}
                >
                    <Row>
                        <Col span={8}>
                            <Form.Item
                                name="datasetId"
                                label={<div style={{ width: '62px' }}>数据集ID</div>}
                                style={{ marginTop: '20px', width: '100%' }}
                                initialValue={
                                    getUrlParam()?.datasetId ? getUrlParam()?.datasetId : ''
                                }
                            >
                                <Input placeholder="请输入数据集ID" style={{ width: '100%' }} />
                            </Form.Item>
                        </Col>
                        <Col span={9}>
                            <Form.Item
                                name="url"
                                label={<div style={{ width: '90px' }}>接口</div>}
                                style={{ margin: '20px 0px 0px 0px' }}
                                initialValue={
                                    getUrlParam()?.url ? decodeURIComponent(getUrlParam()?.url) : ''
                                }
                            >
                                <Input placeholder="请输入接口名称" style={{ width: '100%' }} />
                            </Form.Item>
                        </Col>
                        <Col span={7}>
                            <Form.Item
                                name="jmespath"
                                label="特征路径"
                                style={{ margin: '20px 0px 0px 50px' }}
                                initialValue={
                                    getUrlParam()?.jmespath
                                        ? decodeURIComponent(getUrlParam()?.jmespath)
                                        : ''
                                }
                            >
                                <Input placeholder="请输入特征路径" style={{ width: '100%' }} />
                            </Form.Item>
                        </Col>
                    </Row>
                    <Row>
                        <Col span={8}>
                            <Form.Item name="valuesSetCount" label="value数量">
                                <Select
                                    value={valueR}
                                    style={{ width: '30%' }}
                                    onChange={(e) => {
                                        setValueR(e);
                                        setValueV('');
                                    }}
                                >
                                    {relationOption.map((item) => {
                                        return (
                                            <Select.Option value={item.value}>
                                                {item.label}
                                            </Select.Option>
                                        );
                                    })}
                                </Select>
                                {valueR === 'BETWEEN' ? (
                                    <div
                                        style={{
                                            width: '70%',
                                            display: 'inline-block',
                                            border: '1px solid #ccc',
                                            borderRadius: '8px',
                                        }}
                                        key={'value'}
                                    >
                                        <InputNumber
                                            value={valueV?.toString()?.split('-')[0] || ''}
                                            bordered={false}
                                            style={{ width: '48%' }}
                                            onChange={(e) => {
                                                setValueV(
                                                    `${e || ''}-${valueV?.toString()?.split('-')[1] || ''}`,
                                                );
                                            }}
                                        />
                                        <span>-</span>
                                        <InputNumber
                                            bordered={false}
                                            value={valueV?.toString()?.split('-')[1] || ''}
                                            style={{ width: '48%' }}
                                            onChange={(e) => {
                                                setValueV(
                                                    `${valueV?.toString()?.split('-')[0] || ''}-${e || ''}`,
                                                );
                                            }}
                                        />
                                        {isBetweenV && (
                                            <span
                                                style={{
                                                    fontSize: '12px',
                                                    color: 'red',
                                                    position: 'absolute',
                                                    top: '34px',
                                                    left: '30%',
                                                }}
                                            >
                                                * 请填写value数量区间
                                            </span>
                                        )}
                                    </div>
                                ) : (
                                    <InputNumber
                                        placeholder="请输入value数量"
                                        value={valueV}
                                        style={{ width: '70%' }}
                                        onChange={(e) => {
                                            setValueV(e);
                                        }}
                                    />
                                )}
                            </Form.Item>
                        </Col>
                        <Col span={9}>
                            <Form.Item
                                name="occurSetRatio"
                                label="业务相关度"
                                style={{ margin: '0 0 0 20px' }}
                            >
                                <Select
                                    value={occurR}
                                    style={{ width: '30%', borderRadius: 0 }}
                                    onChange={(e) => {
                                        setOccurR(e);
                                    }}
                                >
                                    {relationOption.map((item) => {
                                        return (
                                            <Select.Option value={item.value}>
                                                {item.label}
                                            </Select.Option>
                                        );
                                    })}
                                </Select>

                                {occurR === 'BETWEEN' ? (
                                    <div
                                        style={{
                                            width: '70%',
                                            display: 'inline-block',
                                            border: '1px solid #ccc',
                                            borderRadius: '8px',
                                        }}
                                        key={'occur'}
                                    >
                                        <InputNumber
                                            value={occurV?.toString()?.split('-')[0] || ''}
                                            bordered={false}
                                            style={{ width: '48%' }}
                                            onChange={(e) => {
                                                setOccurV(
                                                    `${e || ''}-${occurV?.toString()?.split('-')[1] || ''}`,
                                                );
                                            }}
                                        />
                                        <span>-</span>
                                        <InputNumber
                                            value={occurV?.toString()?.split('-')[1] || ''}
                                            bordered={false}
                                            style={{ width: '48%' }}
                                            onChange={(e) => {
                                                setOccurV(
                                                    `${occurV?.toString()?.split('-')[0] || ''}-${e || ''}`,
                                                );
                                            }}
                                        />
                                        {isBetweenO && (
                                            <span
                                                style={{
                                                    fontSize: '12px',
                                                    color: 'red',
                                                    position: 'absolute',
                                                    top: '34px',
                                                    left: '30%',
                                                }}
                                            >
                                                * 请填写业务相关度区间
                                            </span>
                                        )}
                                    </div>
                                ) : (
                                    <InputNumber
                                        value={occurV}
                                        placeholder="请输入业务相关度"
                                        style={{ width: '70%' }}
                                        onChange={(e) => {
                                            setOccurV(e);
                                        }}
                                    />
                                )}
                            </Form.Item>
                        </Col>
                        <Col span={7}>
                            <div
                                style={{
                                    display: 'flex',
                                    justifyContent: 'flex-end',
                                }}
                            >
                                <Button
                                    type="primary"
                                    htmlType="submit"
                                    style={{ marginRight: '5px' }}
                                    onClick={async () => {
                                        const values = await form.validateFields(); // 获取from表单的值
                                        let flag = false;
                                        if (
                                            valueR === 'BETWEEN' &&
                                            valueV?.split('-').filter((item) => item).length === 1
                                        ) {
                                            setIsBetweenV(true);
                                        } else {
                                            setIsBetweenV(false);
                                        }
                                        if (
                                            occurR === 'BETWEEN' &&
                                            occurV?.split('-').filter((item) => item).length === 1
                                        ) {
                                            setIsBetweenO(true);
                                        } else {
                                            setIsBetweenO(false);
                                        }

                                        if (
                                            (valueR === 'BETWEEN' &&
                                                valueV?.split('-').filter((item) => item).length ===
                                                    1) ||
                                            (occurR === 'BETWEEN' &&
                                                occurV?.split('-').filter((item) => item).length ===
                                                    1)
                                        ) {
                                            flag = true;
                                        }
                                        if (!flag) {
                                            getListData({ ...values, pageNum: 1, pageSize: 10 });
                                        }
                                    }}
                                >
                                    查询
                                </Button>
                                <Button
                                    onClick={() => {
                                        form.setFieldsValue({
                                            datasetId: '',
                                            jmespath: '',
                                            url: '',
                                        });
                                        setOccurR('');
                                        setOccurV('');
                                        setValueR('');
                                        setValueV('');
                                        getListData({ pageNum: 1, pageSize: 10 }, true);
                                    }}
                                >
                                    重置
                                </Button>
                            </div>
                        </Col>
                    </Row>
                </Form>
            </div>

            <div
                style={{
                    background: '#fff',
                    padding: '20px',
                    marginTop: '20px',
                    borderRadius: '10px',
                }}
            >
                <Table
                    columns={columns}
                    rowKey={'id'}
                    dataSource={listRecord?.records}
                    scroll={{ x: 1000 }}
                    pagination={{
                        current: listRecord.current,
                        pageSize: listRecord?.size,
                        total: listRecord?.total,
                        size: 'small',
                        showTotal: (total) => `共${total}条数据`,
                        onChange: async (page, pageSize) => {
                            // 刷新数据
                            getListData({
                                pageSize,
                                pageNum: page,
                            });
                        },
                    }}
                    onChange={(
                        pagination,
                        filters,
                        _sorter,
                        extra: { currentDataSource: []; action: any },
                    ) => {
                        setFilterData(filters);
                        setPaginationData(pagination);
                    }}
                />
            </div>

            <Modal
                title={isNumModal ? '数值分布状态' : '枚举分布状态'}
                open={activeModal != null} // 只在 activeModal 不为 null 时打开
                onCancel={() => setActiveModal(null)} // 关闭 modal
                onOk={() => setActiveModal(null)} // 关闭 modal
                width={1500}
                footer={null}
            >
                {isLoading ? (
                    <div style={{ textAlign: 'center', padding: '50px' }}>
                        <Spin tip="加载中..." size="large" />
                    </div>
                ) : (
                    <div>{isNumModal ? showNumChart(numData) : showEnumChart(enumData)}</div>
                )}
            </Modal>

            <Modal
                title="字段类型详细信息"
                visible={isModalOpen}
                footer={null}
                width={1000}
                style={{ maxWidth: '1000px', width: '80%' }} // 这里设置宽度
                onCancel={() => setIsModalOpen(false)}
            >
                <div
                    style={{
                        padding: '20px',
                        backgroundColor: '#f3f4f6',
                        borderRadius: '8px',
                        whiteSpace: 'normal',
                        overflow: 'auto',
                        maxHeight: '800px',
                    }}
                >
                    <pre
                        style={{
                            fontFamily: 'monospace',
                            lineHeight: '1.6',
                            wordWrap: 'break-word',
                        }}
                    >
                        {modalContent}
                    </pre>
                </div>
            </Modal>
        </PageContainer>
    );
};

export default TableList;