<Modal
title="人才特征模型"
open={modalOpen}
centered
onCancel={() => { setModalOpen(false); setEditingItemId(null); }}
footer={null}
width={800}
maskClosable={false}
classNames={{
header: styles.modalHeader,
content: styles.modal,
}}
>
{modalInitialLoading ? (
<div className={styles.initialLoading}>加载中...</div>
) : (modelDesc.length === 0 ? (
<div className={styles.emptyTip}>暂无人才特征模型数据</div>
) : (
<div id="scrollContainer" style={{ height: 700, overflow: 'auto',padding: '0 10px', }}>
<InfiniteScroll
dataLength={modelDesc.length}
next={loadMore}
hasMore={hasMore}
loader={<div className={styles.loading}>加载中...</div>}
endMessage={<div style={{ textAlign: 'center', padding: 16 }}>已加载全部数据</div>}
scrollableTarget="scrollContainer"
>
<Steps
direction="vertical"
className={styles.steps}
progressDot
style={{ width: '100%' }}
>
{modelDesc.map((item, index) => (
<Steps.Step
key={index}
status='finish'
title={
<Flex justify="space-between" align="center" className={styles.infoBar}>
<div style={{ width: 500 }}>
<Flex gap={20} align="center">
<span className={styles.infoItem} style={{ width: '45%' }}>
<img
src="https://xingge-ai.oss-cn-shenzhen.aliyuncs.com/fusi/Users Group Two Rounded.png"
style={{ marginRight: 4, verticalAlign: 'middle', marginTop: -2 }}
alt="" width={18} height={18}
/>
候选人:{item.applyName || 'null'}
</span>
<span className={styles.infoItem} style={{ width: '65%' }}>
<img
src="https://xingge-ai.oss-cn-shenzhen.aliyuncs.com/fusi/Clock Circle.png"
style={{ marginRight: 4, verticalAlign: 'middle', marginTop: -2 }}
alt="" width={18} height={18}
/>
更新时间:{item.updateTime || 'null'}
</span>
</Flex>
</div>
<div style={{ width: '30%', textAlign: 'right' }}>
{editingItemId === item.id ? (
<>
<Button
size="small"
type="text"
style={{ color: '#0057FF', fontSize: 16, fontWeight: 500 }}
onClick={() => saveEdit(item)}
loading={modalLoading}
>
保存
</Button>
<Button
size="small"
type="text"
style={{ fontSize: 16, fontWeight: 500 }}
onClick={() => cancelEdit(item.id)}
>
取消
</Button>
</>
) : (
<Button
icon={<EditOutlined style={{ color: '#0057FF' }} />}
size="small"
type="text"
style={{ color: '#0057FF', fontSize: 16, fontWeight: 500 }}
onClick={() => startEdit(item)}
>
编辑
</Button>
)}
</div>
</Flex>
}
description={
<div className={styles.timelineContentWrapper}>
{editingItemId === item.id ? (
<Input.TextArea
value={editContents[item.id] || ''}
onChange={(e) =>
setEditContents((prev) => ({
...prev,
[item.id]: e.target.value,
}))
}
autoSize={{ minRows: 10, maxRows: 10 }}
/>
) : (
<div
className={styles.viewModeContainer}
dangerouslySetInnerHTML={{ __html: md.render(item.modelContent || '')}}
/>
)}
</div>
}
/>
))}
</Steps>
</InfiniteScroll>
</div>
))
}
</Modal >
主要优化点:
-
利用 InfiniteScroll 实现滚动加载
-
modalInitialLoading 添加内容加载显示
import InfiniteScroll from 'react-infinite-scroll-component';
const [modalOpen, setModalOpen] = useState(false); // 模型弹窗 const [modelDesc, setModelDesc] = useState([]); // 模型描述 const [currentPage, setCurrentPage] = useState(1); const [pageSize, setPageSize] = useState(5); const [modalLoading, setModalLoading] = useState(false); const [editingItemId, setEditingItemId] = useState(null); const [editContents, setEditContents] = useState({}); const [hasMore, setHasMore] = useState(true); // 是否还有更多数据 const [modalInitialLoading, setModalInitialLoading] = useState(false);// 处理模型弹窗
const handleModal = async (item) => {
setModalOpen(true);
setModalInitialLoading(true);
setCurrentPage(1);
setModelDesc([]);
setHasMore(true);
setJobId(item.id);
try {
const res = await getPersonalityModel({
jobId: item.id,
pageNum: 1,
pageSize: pageSize,
});if (res.code === 200) { setModelDesc(res.rows || ''); setTotal(res.total || 0); setHasMore((res.rows || []).length < (res.total || 0)); } else { message.warning('当前职位不存在人才特征模型'); setModalOpen(false); } } catch (error) { console.log(error); } finally { setModalInitialLoading(false); } } const loadMore = async () => { try { const nextPage = currentPage + 1; const res = await getPersonalityModel({ jobId: jobId, pageNum: nextPage, pageSize: pageSize, }); if (res.code === 200) { const newData = res.rows || []; setModelDesc(prev => [...prev, ...newData]); setCurrentPage(nextPage); setHasMore([...modelDesc, ...newData].length < total); } else { message.warning('没有更多数据了'); } } catch (error) { console.log('加载更多失败:', error); } }; const startEdit = (item) => { setEditingItemId(item.id); setEditContents((prev) => ({ ...prev, [item.id]: item.modelContent, })); }; const cancelEdit = (itemId) => { setEditingItemId(null); setEditContents((prev) => { const newState = { ...prev }; delete newState[itemId]; return newState; }); }; const saveEdit = async (item) => { setModalLoading(true); try { const res = await saveFeatureModel({ id: item.id, modelContent: editContents[item.id] || '' }); if (res.code === 200) { message.success('保存成功'); } else { message.warning('保存失败'); } const updatedData = modelDesc.map((modelItem) => { if (modelItem.id === item.id) { return { ...modelItem, modelContent: editContents[item.id] || '' }; } return modelItem; }); setModelDesc(updatedData); setEditingItemId(null); } catch (error) { console.log(error); message.error('保存失败'); } finally { setModalLoading(false); } };.modal {
background-image: url('https://xingge-ai.oss-cn-shenzhen.aliyuncs.com/fusi/image 28.png');
background-size: 500px 350px;
background-repeat: no-repeat;
background-position: right top;
height: 800px;
}.initialLoading {
text-align: center;
padding: 45% 0;
font-size: 16px;
color: #666;
}.listItem {
padding: 0 !important;
border: none !important;
}.emptyTip {
color: #333;
font-size: 16px;
font-weight: 500;
text-align: center;
margin-top: 45%;
}.timelineItem {
display: flex;
margin-bottom: 30px;
position: relative;
}.steps {
margin-right: 10px;:global(.ant-steps-item-title) { color: #000000 !important; padding: 0; width: 700px; }}
.timelineContentWrapper {
margin-top: 10px;
flex: 1;
}.infoBar {
width: 100%;
flex-wrap: nowrap;
overflow: hidden;.infoItem { color: #767676; font-size: 16px; height: 30px; font-weight: 500; }}
.viewModeContainer {
min-height: 200px;
padding: 6px 12px;
border: 1px solid #d9d9d9;
border-radius: 4px;
word-break: break-all;
}.loading {
text-align: center;
padding: 20px 0;
}

