feat(componentTest): 实现组件测试功能模块

- 新增组件测试页面基础结构和样式
- 集成侧边栏组件树形菜单及搜索功能
- 实现实例列表展示与数据获取逻辑
- 添加状态管理和节点选择交互功能
- 配置标签统计显示总数、已测试和未测试数量
- 引入API接口用于获取树形组件测试数据
- 实现父子组件间的数据传递和刷新机制
master
钟良源 2 months ago
parent 8f46a0adf3
commit 6ca6ca34db

@ -0,0 +1,11 @@
import axios from 'axios';
import { apiResData } from '@/api/interface/index';
// 公共路径
const urlPrefix = '/api/v1/bpms-workbench';
// 树形组件列表
export function getTreeComponents(params?: { name: string, identifier: string, runStatus: string }) {
return axios.get(`${urlPrefix}/componentTestCase/tree/instance`, { params });
}

@ -1,4 +1,4 @@
import React from 'react';
import React, { useState } from 'react';
import styles from './style/index.module.less';
import { Button, Input, Radio, Space, Tag, Divider } from '@arco-design/web-react';
import { IconSearch } from '@arco-design/web-react/icon';
@ -8,17 +8,35 @@ import InstanceList from '@/pages/componentDevelopment/componentTest/instanceLis
const Group = Radio.Group;
const ComponentTest = () => {
const [selectedIdentifier, setSelectedIdentifier] = useState<string>('');
const [refreshKey, setRefreshKey] = useState<number>(0);
const [count, setCount] = useState({ total: 0, passed: 0, failed: 0 });
// 标签配置
const tagList = [
{ label: '总数', value: count.total, color: 'purple' },
{ label: '已测试', value: count.passed, color: 'arcoblue' },
{ label: '未测试', value: count.failed, color: 'orangered' }
];
// 处理子节点选择
const handleNodeSelect = (identifier: string) => {
setSelectedIdentifier(identifier);
// 每次选择都更新 refreshKey,确保即使选择同一个节点也会触发刷新
setRefreshKey(prev => prev + 1);
};
return (
<div className={styles['component-test']}>
<div className={styles['header']}>
<Group defaultValue={'Beijing'} name="button-radio-group">
{['总数126', '已测试45', '未测试81'].map((item, index) => {
{tagList.map((item) => {
return (
<Radio key={item} value={item}>
<Radio key={item.label} value={item.label}>
{({ checked }) => {
return (
<Tag size="large" tabIndex={-1} key={item} color={['purple', 'arcoblue', 'orangered'][index]}>
{item}
<Tag size="large" tabIndex={-1} color={item.color}>
{item.label}{item.value}
</Tag>
);
}}
@ -43,10 +61,10 @@ const ComponentTest = () => {
</div>
<div className={styles['content']}>
<div className={styles['left']}>
<SideBar />
<SideBar onNodeSelect={handleNodeSelect} getCount={(value) => setCount(value)} />
</div>
<div className={styles['right']}>
<InstanceList />
<InstanceList identifier={selectedIdentifier} refreshKey={refreshKey} />
</div>
</div>
</div>

@ -1,41 +1,97 @@
import React from 'react';
import { Button, Popconfirm, Table } from '@arco-design/web-react';
import { IconDelete } from '@arco-design/web-react/icon';
import React, { useEffect, useState } from 'react';
import { Button, Table, TableColumnProps } from '@arco-design/web-react';
import { getTreeComponents } from '@/api/componentTestCase';
import { runStatusConstant, runStatusDic, runTypeConstant, runTypeDic } from '@/const/isdp/componentDeploy';
import dayjs from 'dayjs';
const InstanceList = () => {
const data = [];
const InstanceList = ({ identifier, refreshKey }: { identifier: string; refreshKey: number }) => {
const [data, setData] = useState([]);
const [loading, setLoading] = useState(false);
// 获取实例列表数据
const fetchInstanceData = async (identifier: string) => {
if (!identifier) {
setData([]);
return;
}
setLoading(true);
try {
const res: any = await getTreeComponents({ identifier } as any);
if (res.code === 200) {
// 直接使用 children 数据
const instanceData = res.data?.[0]?.children || [];
setData(instanceData);
}
} catch (error) {
console.error('获取实例列表失败:', error);
} finally {
setLoading(false);
}
};
useEffect(() => {
fetchInstanceData(identifier);
}, [identifier, refreshKey]);
// 定义表格列
const columns = [
const columns: TableColumnProps[] = [
{
title: '实例名',
dataIndex: 'name'
title: '实例标识',
dataIndex: 'identifier'
},
{
title: '实例标识',
dataIndex: 'dataType'
title: '实例',
dataIndex: 'name'
},
{
title: '运行类型',
dataIndex: 'defaultValue'
dataIndex: 'runType',
align: 'center',
render: (runType) => {
const item = runTypeDic.find(d => d.value === runTypeConstant[runType]);
return item ? item.label : '-';
}
},
{
title: '实例状态',
dataIndex: 'description'
dataIndex: 'runStatus',
align: 'center',
render: (runStatus) => {
const item = runStatusDic.find(d => d.value === runStatus.toLowerCase());
return item ? item.label : '-';
}
},
{
title: '实例测试时间',
dataIndex: 'description1'
dataIndex: 'lastTestTime',
align: 'center',
render: (lastTestTime) => {
return lastTestTime ? dayjs(lastTestTime).format('YYYY-MM-DD HH:mm:ss') : '-';
}
},
{
title: '操作',
dataIndex: 'operations'
dataIndex: 'operations',
align: 'center',
render: (col, record, index) => {
return (
record.runStatus === 'RUN' && (
<Button
type="text"
></Button>
)
);
}
}
];
return (
<Table
columns={columns}
data={data}
loading={loading}
pagination={false}
rowKey="id"
/>

@ -1,17 +1,121 @@
import React, { useState } from 'react';
import React, { useEffect, useState } from 'react';
import { Input, Tree } from '@arco-design/web-react';
import { IconSearch } from '@arco-design/web-react/icon';
import { menu } from './test/data';
import { getTreeComponents } from '@/api/componentTestCase';
const TreeNode = Tree.Node;
const SideBar = () => {
const SideBar = ({ onNodeSelect, getCount }) => {
const [searchValue, setSearchValue] = useState('');
const [treeData, setTreeData] = useState([]);
const [originalData, setOriginalData] = useState([]);
const [expandedKeys, setExpandedKeys] = useState<string[]>([]);
const handleSearchChange = (value: string) => {
setSearchValue(value);
if (!value.trim()) {
// 搜索为空时,恢复原始数据并收起所有节点
setTreeData(originalData);
setExpandedKeys([]);
return;
}
// 过滤树形数据
const filterTreeData = (data: any[]) => {
const filtered: any[] = [];
const expandKeys: string[] = [];
data.forEach((classifyNode) => {
const filteredChildren = classifyNode.children.filter((child: any) =>
child.title.toLowerCase().includes(value.toLowerCase())
);
// 如果有匹配的子节点,或者分类名称匹配
if (
filteredChildren.length > 0 ||
classifyNode.title.toLowerCase().includes(value.toLowerCase())
) {
filtered.push({
...classifyNode,
children: filteredChildren.length > 0 ? filteredChildren : classifyNode.children
});
// 展开匹配的分类
expandKeys.push(classifyNode.key);
}
});
return { filtered, expandKeys };
};
const { filtered, expandKeys } = filterTreeData(originalData);
setTreeData(filtered);
setExpandedKeys(expandKeys);
};
// 根据 componentClassify 分类数据
const formatTreeData = (data: any[]) => {
const classifyMap = new Map();
data.forEach((item) => {
const classify = item.componentClassify || '未分类';
if (!classifyMap.has(classify)) {
classifyMap.set(classify, {
title: classify,
key: `classify-${classify}`,
children: []
});
}
classifyMap.get(classify).children.push({
title: item.name,
key: item.identifier,
data: item
});
});
return Array.from(classifyMap.values());
};
// 获取树形组件列表
const fetchData = async () => {
const res: any = await getTreeComponents();
if (res.code === 200) {
const formattedData = formatTreeData(res.data);
setTreeData(formattedData);
setOriginalData(formattedData);
const count = res.data.reduce((acc, item) => {
if (item.testCount > 0) {
acc.passed++;
}
else {
acc.failed++;
}
return acc;
}, { total: res.data.length, passed: 0, failed: 0 });
getCount(count);
}
};
// 处理节点选择
const handleTreeSelect = (selectedKeys: any[], info: any) => {
// 判断是否为子节点(不是分类节点)
const selectedKey = selectedKeys[0];
if (selectedKey && !selectedKey.startsWith('classify-')) {
// 子节点的key就是identifier
onNodeSelect(selectedKey);
}
};
useEffect(() => {
fetchData();
}, []);
return (
<>
<Input
@ -23,13 +127,12 @@ const SideBar = () => {
/>
<Tree
onSelect={(value, info) => {
console.log(value, info);
}}
onExpand={(keys, info) => {
console.log(keys, info);
onSelect={handleTreeSelect}
onExpand={(keys) => {
setExpandedKeys(keys as string[]);
}}
treeData={menu}
expandedKeys={expandedKeys}
treeData={treeData}
>
</Tree>

Loading…
Cancel
Save