相关链接
开发过程中我发现文档信息不全面,根据ai的一些总结发现并不适用当前的版本或者可以说不适用当前组件, 只能在开发过程中不停摸索,有一些是在线示例中实现的代码,在本地项目中并没有任何表现,所以说只能是自己在相关链接里翻找,文档中的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;