测试节点版本配置 - 管理与配置
<p>import React, { useEffect, useRef, useState } from 'react';
import ContentCard from '@/components/ContentCard';
import { Button, Collapse, Popconfirm, Select, Space, message, Typography } from 'antd';
import { ActionType, EditableProTable, ProColumns } from '@ant-design/pro-table';
import { getOnlyKey } from '@/utils/utils';
import { PlusCircleOutlined } from '@ant-design/icons';
import './index.less';
import { isArray, isNumber } from 'lodash';
import {
getNodeVersionList,
getTestNodeType,
addNodeVersion,
updateNodeVersion,
deletedNodeVersion,
} from '@/services/configManage/nodeLibrary';
import { TclUpload } from '@tcljs/react-tcl-design';
import type { UploadChangeParam } from 'antd/lib/upload';
import UserSelect from '@/components/UserSelect';</p>
<p>const { Panel } = Collapse;
const { Link } = Typography;
const VersionConfiguration: React.FC = () => {
const actionRefs = useRef<ActionType>();
const [editKeys, setEditKeys] = useState<Record<string, React.Key[]>>({});
const [activeKeys, setActiveKeys] = useState<string[]>([]);
const [versionData, setVersionData] = useState([]);
const [selectSesion, setSelectSesion] = useState([]);
const [selectValue, setSelectValue] = useState<string>('');
const [tableDatas, setTableData] = useState({});</p>
<p>const fetchVersionList = async (values: string) => {
const result = await getNodeVersionList({ testNodeType: values });
setVersionData(result.data);
const tableData = {};
const resluets: Record<string, unknown[]> = result.data;
Object.entries(resluets).forEach(([version, datas]) => {
tableData[version] = datas.map((item: any) => ({
...item,
key: item.id,
}));
});
setTableData(tableData);
};
// 测试节点类型
const getTestNode = async () => {
try {
const { data } = await getTestNodeType({});
const newData = data.map((item: string) => {
return { label: item, value: item };
});
setSelectSesion(newData);
setSelectValue(newData[0]?.label);
fetchVersionList(newData[0]?.label);
} catch (error) {}
};</p>
<p>const handleSelectChange = async (val: string) => {
setSelectValue(val);
fetchVersionList(val);
};</p>
<p>useEffect(() => {
getTestNode();
}, []);</p>
<p>const handleUploadChange = (info: UploadChangeParam) => {
if (info.file.status === 'done') {
if (info.file.response.code === 'BIZ_SUCCESS') {
message.success('文件上传成功!');
}
}
};
const columns: ProColumns[] = [
{
title: '节点版本',
width: 80,
valueType: 'index',
align: 'center',
renderFormItem: ({ index }) => isNumber(index) && <>{index + 1}</>, },
{
title: '测试项',
dataIndex: 'testItem',
align: 'center',
formItemProps: () => ({
rules: [{ required: true, message: '请输入测试项' }],
}),
},
{
title: '负责人',
dataIndex: 'leader',
align: 'center',
formItemProps: () => ({
rules: [{ required: true, message: '请输入负责人' }],
}),
renderFormItem: () => {
return <UserSelect />;
},
},
{
title: '用例库名称',
dataIndex: 'caseLibraryName',
align: 'center',
},
{
title: '交付件模板',
dataIndex: 'deliveryTemplate',
align: 'center',
ellipsis: true,
renderFormItem: () => {
return (
<TclUpload
uploadButton={<Button style={{ marginRight: '10px' }}>上传文件</Button>}
action={'/project-test-arts-api/web/upload_file'}
onChange={handleUploadChange}
/>
);
},
render: (<em>, record) => {
return <Link href={record.deliveryTemplate}>{record.deliveryTemplate}</Link>;
},
},
{
title: '测试周期(天)',
dataIndex: 'testCycle',
align: 'center',
formItemProps: () => ({
rules: [
{
validator: (</em>, value) => {
const isValid = !isNaN(parseFloat(value));
if (isValid) {
return Promise.resolve();
}
return Promise.reject(new Error('请输入有效的数字'));
},
},
],
}),
},
{
title: '参考投入人力(人天)',
dataIndex: 'referenceInputManpower',
align: 'center',
},
{
title: '操作',
valueType: 'option',
width: 120,
align: 'center',
render: (text, record, _, action) => [
<a
key="editable"
onClick={() => {
action?.startEditable?.(record.key);
}}
>
配置
</a>,</p>
<pre><code> <Popconfirm
title="确定删除此行?"
key="delete"
onConfirm={async () => {
try {
const { code, msg } = await deletedNodeVersion({
id: record.id,
});
if ((code as unknown as string) !== 'BIZ_SUCCESS') throw new Error(msg);
message.success('删除成功!');
fetchVersionList(selectValue);
} catch (error) {}
}}
okText="确定"
cancelText="取消"
>
<a>删除</a>
</Popconfirm>,
],
},
</code></pre>
<p>];</p>
<p>return (
<ContentCard title="测试节点的版本配置" className="configuration-page-wrap">
<div className="riskmMonthly" style={{ marginBottom: 15 }}>
<span style={{ fontWeight: 'bold', fontSize: '14px' }}>测试节点类型:</span>
<Select
style={{ width: 230 }}
options={selectSesion}
value={selectValue}
onChange={handleSelectChange}
/>
</div></p>
<pre><code> <div className="check-list-wrap">
<Collapse
ghost
activeKey={activeKeys}
onChange={(keys) => setActiveKeys(isArray(keys) ? keys : [keys])}
>
{Object.entries(versionData).map(([version, _], index) => {
const isPanelActive = activeKeys.includes(`${index}`);
return (
<Panel header={version} key={index}>
{isPanelActive && (
<EditableProTable
rowKey="key"
recordCreatorProps={false}
actionRef={actionRefs}
columns={columns}
value={tableDatas[version]}
editable={{
type: 'single',
editableKeys: editKeys[version] || [],
onSave: async (_, row) => {
const result = {
id: row.id,
testNode: selectValue,
testItem: row.testItem,
leader: row.leader,
caseLibraryName: row.caseLibraryName,
deliveryTemplate:
row?.deliveryTemplate?.file?.response?.data?.originalFilename || null,
testCycle: row.testCycle,
testNodeVersion: version,
referenceInputManpower: row.referenceInputManpower,
};
try {
const { code, msg } = row.id
? await updateNodeVersion(result)
: await addNodeVersion(result);
if ((code as unknown as string) !== 'BIZ_SUCCESS') throw new Error(msg);
message.success('保存成功!');
fetchVersionList(selectValue);
} catch (error) {}
},
onChange: (keys) => {
setEditKeys((prevEditKeysMap) => ({
...prevEditKeysMap,
[version]: keys,
}));
},
actionRender: (row, config, defaultDoms) => {
return [defaultDoms.save, defaultDoms.cancel];
},
}}
search={false}
pagination={false}
bordered
/>
)}
<Space>
<Button
style={{ border: '1px' }}
onClick={() => {
actionRefs.current?.addEditRecord?.({
key: getOnlyKey(),
});
}}
>
<PlusCircleOutlined />
</Button>
</Space>
</Panel>
);
})}
</Collapse>
</div>
</ContentCard>
</code></pre>
<p>);
};
export default VersionConfiguration;
所有面板打开,新增第一个面板下的表格会被新增到其他面板下的表格,怎么解决这个问题onChange: (keys) => {<br />
setEditKeys((prevEditKeysMap) => ({<br />
...prevEditKeysMap,<br />
[index]: keys, // 使用索引index作为键<br />
}));<br />
},类型“(prevEditKeysMap: Key[]) => { [x: number]: Key | Key[]; length: number; toString(): string; toLocaleString(): string; pop(): Key | undefined; push(...items: Key[]): number; ... 31 more ...; [Symbol.unscopables]: { ...; }; }”的参数不能赋给类型“SetStateAction<Key[]>”的参数。
不能将类型“(prevEditKeysMap: Key[]) => { [x: number]: Key | Key[]; length: number; toString(): string; toLocaleString(): string; pop(): Key | undefined; push(...items: Key[]): number; ... 31 more ...; [Symbol.unscopables]: { ...; }; }”分配给类型“(prevState: Key[]) => Key[]”。
不能将类型“{ [x: number]: React.Key | React.Key[]; length: number; toString(): string; toLocaleString(): string; pop(): React.Key | undefined; push(...items: React.Key[]): number; ... 31 more ...; [Symbol.unscopables]: { ...; }; }”分配给类型“Key[]”。
“number”索引签名不兼容。
不能将类型“Key | Key[]”分配给类型“Key”。
不能将类型“Key[]”分配给类型“Key”。ts(2345)
(parameter) prevEditKeysMap: React.Key[]
内容:您好!根据您提供的代码和问题描述,我注意到在处理<code>editKeys</code>的时候,您使用了一个对象来存储不同版本的编辑键。然而,当您在<code>onChange</code>函数中更新<code>editKeys</code>时,由于<code>setEditKeys</code>的类型定义为<code>React.Dispatch<React.SetStateAction<Key[]>></code>,它期望接收一个<code>Key[]</code>类型的参数,而不是一个对象。</p>
<p>为了解决这个问题,您可以将<code>editKeys</code>的初始值设置为一个空数组,然后在<code>onChange</code>函数中使用<code>index</code>作为键,将编辑键的数组存储在<code>editKeys</code>中。这样,就可以正确地更新编辑键了。下面是修改后的代码示例:</p>
<pre><code class="language-javascript">const [editKeys, setEditKeys] = useState<Record<string, React.Key[]>>({});
...
<EditableProTable
...
editable={{
...
onChange: (keys) => {
setEditKeys((prevEditKeysMap) => ({
...prevEditKeysMap,
[version]: keys,
}));
},
...
}}
...
/>
</code></pre>
<p>请注意,我将<code>editKeys</code>的类型定义从<code>React.Key[]</code>更改为<code>Record<string, React.Key[]></code>,这样就可以正确地存储不同版本的编辑键了。</p>
<p>希望这可以解决您的问题!如果有任何其他疑问,请随时提问。</p>
原文地址: https://www.cveoy.top/t/topic/qqST 著作权归作者所有。请勿转载和采集!