<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 = () =&gt; {
const actionRefs = useRef<ActionType>();
const [editKeys, setEditKeys] = useState&lt;Record&lt;string, React.Key[]&gt;&gt;({});
const [activeKeys, setActiveKeys] = useState&lt;string[]&gt;([]);
const [versionData, setVersionData] = useState([]);
const [selectSesion, setSelectSesion] = useState([]);
const [selectValue, setSelectValue] = useState<string>('');
const [tableDatas, setTableData] = useState({});</p>
<p>const fetchVersionList = async (values: string) =&gt; {
const result = await getNodeVersionList({ testNodeType: values });
setVersionData(result.data);
const tableData = {};
const resluets: Record&lt;string, unknown[]&gt; = result.data;
Object.entries(resluets).forEach(([version, datas]) =&gt; {
tableData[version] = datas.map((item: any) =&gt; ({
...item,
key: item.id,
}));
});
setTableData(tableData);
};
// 测试节点类型
const getTestNode = async () =&gt; {
try {
const { data } = await getTestNodeType({});
const newData = data.map((item: string) =&gt; {
return { label: item, value: item };
});
setSelectSesion(newData);
setSelectValue(newData[0]?.label);
fetchVersionList(newData[0]?.label);
} catch (error) {}
};</p>
<p>const handleSelectChange = async (val: string) =&gt; {
setSelectValue(val);
fetchVersionList(val);
setEditKeys({}); // 清空editKeys
};</p>
<p>useEffect(() =&gt; {
getTestNode();
}, []);</p>
<p>const handleUploadChange = (info: UploadChangeParam) =&gt; {
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 }) =&gt; isNumber(index) &amp;&amp; &lt;&gt;{index + 1}&lt;/&gt;,
},
{
title: '测试项',
dataIndex: 'testItem',
align: 'center',
formItemProps: () =&gt; ({
rules: [{ required: true, message: '请输入测试项' }],
}),
},
{
title: '负责人',
dataIndex: 'leader',
align: 'center',
formItemProps: () =&gt; ({
rules: [{ required: true, message: '请输入负责人' }],
}),
renderFormItem: () =&gt; {
return <UserSelect />;
},
},
{
title: '用例库名称',
dataIndex: 'caseLibraryName',
align: 'center',
},
{
title: '交付件模板',
dataIndex: 'deliveryTemplate',
align: 'center',
ellipsis: true,
renderFormItem: () =&gt; {
return (
&lt;TclUpload
uploadButton={&lt;Button style={{ marginRight: '10px' }}&gt;上传文件</Button>}
action={'/project-test-arts-api/web/upload_file'}
onChange={handleUploadChange}
/&gt;
);
},
render: (<em>, record) =&gt; {
return <Link href={record.deliveryTemplate}>{record.deliveryTemplate}</Link>;
},
},
{
title: '测试周期(天)',
dataIndex: 'testCycle',
align: 'center',
formItemProps: () =&gt; ({
rules: [
{
validator: (</em>, value) =&gt; {
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) =&gt; [
&lt;a
key=&quot;editable&quot;
onClick={() =&gt; {
action?.startEditable?.(record.key);
}}
&gt;
配置
</a>,</p>
<pre><code>    &lt;Popconfirm
      title=&quot;确定删除此行?&quot;
      key=&quot;delete&quot;
      onConfirm={async () =&gt; {
        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=&quot;确定&quot;
      cancelText=&quot;取消&quot;
    &gt;
      &lt;a&gt;删除&lt;/a&gt;
    &lt;/Popconfirm&gt;,
  ],
},
</code></pre>
<p>];</p>
<p>return (
<ContentCard title="测试节点的版本配置" className="configuration-page-wrap">
&lt;div className=&quot;riskmMonthly&quot; style={{ marginBottom: 15 }}&gt;
&lt;span style={{ fontWeight: 'bold', fontSize: '14px' }}&gt;测试节点类型:</span>
&lt;Select
style={{ width: 230 }}
options={selectSesion}
value={selectValue}
onChange={handleSelectChange}
/&gt;
</div></p>
<pre><code>  &lt;div className=&quot;check-list-wrap&quot;&gt;
    &lt;Collapse
      ghost
      activeKey={activeKeys}
      onChange={(keys) =&gt; setActiveKeys(isArray(keys) ? keys : [keys])}
    &gt;
      {Object.entries(versionData).map(([version], index) =&gt; {
        const isPanelActive = activeKeys.includes(`${index}`);
        return (
          &lt;Panel header={version} key={index}&gt;
            {isPanelActive &amp;&amp; (
              &lt;EditableProTable
                rowKey=&quot;key&quot;
                recordCreatorProps={false}
                actionRef={actionRefs}
                columns={columns}
                value={tableDatas[version]}
                editable={{
                  type: 'single',
                  editableKeys: editKeys[version] || [],
                  onSave: async (_, row) =&gt; {
                    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) =&gt; {
                    setEditKeys((prevEditKeysMap) =&gt; ({
                      ...prevEditKeysMap,
                      [index]: keys, // 使用索引index作为键
                    }));
                  },
                  actionRender: (row, config, defaultDoms) =&gt; {
                    return [defaultDoms.save, defaultDoms.cancel];
                  },
                }}
                search={false}
                pagination={false}
                bordered
              /&gt;
            )}

            &lt;Space&gt;
              &lt;Button
                style={{ border: '1px' }}
                onClick={() =&gt; {
                  actionRefs.current?.addEditRecord?.({
                    key: getOnlyKey(),
                  });
                }}
              &gt;
                &lt;PlusCircleOutlined /&gt;
              &lt;/Button&gt;
            &lt;/Space&gt;
          &lt;/Panel&gt;
        );
      })}
    &lt;/Collapse&gt;
  &lt;/div&gt;
&lt;/ContentCard&gt;
</code></pre>
<p>);
};
export default VersionConfiguration;
所有面板打开,新增第一个面板下的表格会被新增到其他面板下的表格,怎么解决这个问题onChange: (keys) =&gt; { setEditKeys((prevEditKeysMap) =&gt; ({ ...prevEditKeysMap, [index]: keys, // 使用索引index作为键 })); },类型“(prevEditKeysMap: Key[]) =&gt; { [x: number]: Key | Key[]; length: number; toString(): string; toLocaleString(): string; pop(): Key | undefined; push(...items: Key[]): number; ... 31 more ...; [Symbol.unscopables]: { ...; }; }”的参数不能赋给类型“SetStateAction&lt;Key[]&gt;”的参数。 不能将类型“(prevEditKeysMap: Key[]) =&gt; { [x: number]: Key | Key[]; length: number; toString(): string; toLocaleString(): string; pop(): Key | undefined; push(...items: Key[]): number; ... 31 more ...; [Symbol.unscopables]: { ...; }; }”分配给类型“(prevState: Key[]) =&gt; 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[]内容:在useState的初始值中,将editKeys设置为空对象{}而不是空数组[]。修改代码如下: <code>jsx const [editKeys, setEditKeys] = useState&lt;Record&lt;string, React.Key[]&gt;&gt;({}); </code> 然后在handleSelectChange函数中也要做相应的修改: <code>jsx const handleSelectChange = async (val: string) =&gt; { setSelectValue(val); fetchVersionList(val); setEditKeys({}); // 清空editKeys }; </code> 最后,在onChange回调中,将索引index作为键: <code>jsx onChange: (keys) =&gt; { setEditKeys((prevEditKeysMap) =&gt; ({ ...prevEditKeysMap, [index]: keys, })); }, </code> 这样就可以正确地将不同面板的编辑状态分开了。</p>
测试节点版本配置 - 管理和配置您的测试环境

原文地址: https://www.cveoy.top/t/topic/qqTl 著作权归作者所有。请勿转载和采集!

免费AI点我,无需注册和登录