Steps + Input.TextArea + InfiniteScroll 联调优化
<Modaltitle="人才特征模型"open={modalOpen}centeredonCancel={() => { 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', }}><InfiniteScrolldataLength={modelDesc.length}next={loadMore}hasMore={hasMore}loader={<div className={styles.loading}>加载中...</div>}endMessage={<div style={{ textAlign: 'center', padding: 16 }}>已加载全部数据</div>}scrollableTarget="scrollContainer"><Stepsdirection="vertical"className={styles.steps}progressDotstyle={{ width: '100%' }}>{modelDesc.map((item, index) => (<Steps.Stepkey={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%' }}><imgsrc="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%' }}><imgsrc="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 ? (<><Buttonsize="small"type="text"style={{ color: '#0057FF', fontSize: 16, fontWeight: 500 }}onClick={() => saveEdit(item)}loading={modalLoading}>保存</Button><Buttonsize="small"type="text"style={{ fontSize: 16, fontWeight: 500 }}onClick={() => cancelEdit(item.id)}>取消</Button></>) : (<Buttonicon={<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.TextAreavalue={editContents[item.id] || ''}onChange={(e) =>setEditContents((prev) => ({...prev,[item.id]: e.target.value,}))}autoSize={{ minRows: 10, maxRows: 10 }}/>) : (<divclassName={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;
}