ant design 的table 每行单独添加可操作的定时器
工作中有个需求,先获取一个list列表数据展示成一个表格。每一条列表数据都可以动态查看当前数据的运行状态,就是需要不停的去做运行状态的请求。如果每条数据都开启一个定时器的话,不好好管理,所以考虑把在运行状态中的请求都放在一个定时器里。现在我简单的做个定时器模拟这一效果。
看图:刚开始是前三条数据在运行,过了10秒以后(实际工作中,可以把10秒变成运行结束的判断条件),只有第三条在运行,然后我又点击了第四条,3,4两条又重新开启了定时器。
const { Table, Divider, Tag } = antd; const data = [ { key: 1, name: 'John Brown', age: 32, address: '1', }, { key: 2, name: 'Jim Green', age: 42, address: '1', }, { key: 3, name: 'Joe Black', age: 32, address: '1', }, { key: 4, name: 'Jim Green', age: 42, address: '1', }, { key: 5, name: 'Joe Black', age: 32, address: '1', }, ]; class Sider extends React.Component { constructor(props) { super(props); this.state = { scan:[1,2,3], scunNum:[1,1,1], }; } componentDidMount(){ const {scan} = this.state; let i=1;
// 初始化开启定时器 this.timer = setInterval(()=> { i += 1; if(i>10){ const runId=scan.filter((_,n)=> n>1); const runIdTime=runId.map(()=>i); this.setState({scunNum:runIdTime,scan:runId}) }else{ const runIdTime=scan.map(()=>i); this.setState({scunNum:runIdTime}) } },1000); } componentWillUnmount() {
// 离开页面关闭定时器 clearInterval(this.timer); } start = (i) =>{ const { scunNum } = this.state; return scunNum[i] } render() { const {scan} = this.state; const columns = [ { title: 'id', dataIndex: 'key', key: 'key', }, { title: 'Name', dataIndex: 'name', key: 'name', }, { title: 'Age', dataIndex: 'age', key: 'age', }, { title: '计时', dataIndex: 'address', key: 'address', render: (address,record)=> { if(scan.indexOf(record.key)>-1){ return this.start(scan.indexOf(record.key)) } return <span>未开始</span> } }, { title: 'Action', key: 'action', render: (text, record) => ( <div> { scan.indexOf(record.key)===-1? <a onClick={()=>{ this.setState({scan:[...scan,record.key]}); clearInterval(this.timer); let i=1; this.timer = setInterval(()=> { i += 1; const runIdTime=[...scan,record.key].map(()=>i); this.setState({scunNum:runIdTime}) },1000); }} > 开始</a>: <span>开始</span> } </div> ), }, ]; return ( <Table columns={columns} dataSource={data} /> ); } } ReactDOM.render(<Sider />, mountNode);
这个场景适用于多条数据的运行,实时观测每一条的运行过程。只需要把你的轮训请求放在定时器里就可以了
我的项目文件:
import React from 'react'; import { connect } from 'dva'; import { Card, Table, Popconfirm, Divider, Input, message, Form, Row, Col, Select, Button, Progress } from 'antd'; import Link from 'umi/link'; import request from '@/utils/request'; const FormItem = Form.Item; const { Option } = Select; const bizId=["","","kdbMySql","mysql2","hive","kafka2","es"]; @connect(({ ruleList, loading }) => ({ ruleList, loading: loading.models.ruleList, })) @Form.create() class RuleList extends React.Component { constructor(props) { super(props); this.state = { bizTypes: [], allTypes:[], scan:[], }; } componentWillMount() { const { location: { query: { biz }, }, dispatch, } = this.props; request(`/api/data_marking/biz-types`, { credentials: 'same-origin', }).then(resp => { if (resp && resp.success) { const val = resp.data.filter(vals=>vals.id===Number(biz))[0]; this.setState({ bizTypes: val?val.subTypeList:[],allTypes:resp.data }); } else { message.error(`[请求失败]${JSON.stringify(resp.data)}`); } }); dispatch({ type: 'ruleList/fetchRuleList', payload: { page: 1, pageSize: 10, filter: { union: true }, biz, }, }).then((res)=>{ this.requestStatus(res,biz) }) } componentWillUnmount() { clearInterval(this.timer); } requestStatus=(res,biz)=>{ const statusId= res.map((i) => ({ id:i.id ,status:i.runInfo.ruleRunState.id})) this.setState({scan:statusId}) let runData=statusId.filter(m=>[3,4].indexOf(m.status)===-1); if(runData.length){ this.timer = setInterval(()=> { Promise.all( runData.map((run)=>{ return request(`/api/data_marking/biz/${biz}/db-column-tag-rule/${run.id}/run-state`, { credentials: 'same-origin', }).then(r=>({id:run.id,status:r.data.ruleRunState.id})) }) ).then((val)=>{ const oId=val.map(v=>v.id); const newScan=statusId.map(j=>(oId.indexOf(j.id)>-1?{id:j.id,status:val[oId.indexOf(j.id)].status}:j)); this.setState({scan:newScan}); runData=newScan.filter(m=>[3,4].indexOf(m.status)===-1); if(!runData.length){ clearInterval(this.timer); } }) },1200); } } handleTableChange = pagination => { const pageNum = pagination.current; const { pageSize } = pagination; const { dispatch, ruleList: { filter }, location: { query: {biz}, }, } = this.props; dispatch({ type: 'ruleList/fetchRuleList', payload: { page: pageNum, filter, pageSize, biz }, }).then((res)=>{ this.requestStatus(res,biz) }) }; handleSearch = (e) => { e.preventDefault(); const { dispatch, location: { query: { biz }, }, form, } = this.props; form.validateFields((err, values) => { if (err) return; dispatch({ type: 'ruleList/fetchRuleList', payload: { page: 1, pageSize: 10, filter: {...values,union: true}, biz, }, }).then((res)=>{ this.requestStatus(res,biz) }) }) }; handleFormReset = () => { const { form, dispatch, location: { query: { biz }, }, } = this.props; form.resetFields(); dispatch({ type: 'ruleList/fetchRuleList', payload: { page: 1, pageSize: 10, filter: { union: true }, biz, }, }).then((res)=>{ this.requestStatus(res,biz) }) }; deleteRuleHandler = id => { const { dispatch, location: { query: {biz}, }, } = this.props; dispatch({ type: 'ruleList/fetchDeleteRule', payload: { id, biz, filter: { union: true }, }, }) }; ddd = (i) =>{ const { scan } = this.state; const statu=scan.filter(n=>n.id===i)[0]?.status; switch (statu) { case 1: return <><Progress percent={70} strokeColor='orange' size="small" status="active" format={() => ''} /><br />等待运行</>; case 2: return <><Progress percent={50} size="small" status="active" format={() => ''} /><br />运行中</>; case 3: return <><Progress percent={100} size="small" /><br />上次执行成功</>; case 4: return <><Progress percent={70} size="small" status="exception" /><br />上次执行失败</>; case 5: return <><Progress percent={100} strokeColor='orange' size="small" status="active" format={() => ''} /><br />排队中</>; default: break; } return '' } searchForm(){ const { form: { getFieldDecorator }, } = this.props; const { bizTypes } = this.state; const formItemLayout = { labelCol: { span: 6 }, wrapperCol: { span: 16 }, }; return ( <Form {...formItemLayout} style={{ marginBottom: 20 }} onSubmit={this.handleSearch}> <Row> <Col span={8}> <FormItem label="子场景"> {getFieldDecorator('biz_sub_id')( <Select allowClear placeholder="请选择子场景" > {bizTypes.map(dn => ( <Option key={dn.name} value={`${dn.id}`}> {`${dn.name} ( ${dn.desc} )`} </Option> ))} </Select> )} </FormItem> </Col> <Col span={8}> <FormItem label="关键字"> {getFieldDecorator('key_word')( <Input placeholder='请输入关键字搜索' allowClear />, )} </FormItem> </Col> <Col span={8} align='right'> <Button type="primary" htmlType="submit"> 查询 </Button> <Button htmlType="reset" onClick={this.handleFormReset}> 重置 </Button> </Col> </Row> </Form> ); }; render() { const { ruleList, loading,location: { query: {biz}, }, } = this.props; const { allTypes, scan, } = this.state; const columns = [ { title: 'ID', dataIndex: 'id', }, { title: '子场景', dataIndex: 'bizSubTypeName', }, { title: '规则名称', dataIndex: 'name', }, { title: '规则版本', dataIndex: 'version', }, { title: '规则状态', dataIndex: 'state', render: text => { return text && text.desc ? text.desc : ''; }, }, { title: '命中总量', dataIndex:'evalInfo.hitCount', }, { title: '已评估量', key: 'total', dataIndex: 'evalInfo', render: el => el.checkSuccCount+el.checkFailCount }, { title: '正确量', dataIndex: 'evalInfo.checkSuccCount', }, { title: '错误量', dataIndex: 'evalInfo.checkFailCount', }, { title: '用户', dataIndex: 'updateUser', }, { title: '更新时间', dataIndex: 'updateTime', }, { title: '运行状态', dataIndex: 'runInfo', width: 160, render: (runInfo,record)=> this.ddd(record.id) }, { title: '操作', width: '150px', render: record => ( <div> <Link to={`/dataMarking/${bizId[biz*1]}/ruleHitList/${record.id}/${biz}?version=${record.version}`}>评估</Link> <Divider type="vertical" /> <Link to={`/dataMarking/${bizId[biz*1]}/rule/edit/${record.id}?biz=${biz}`}>编辑</Link> <br /> { [3,4].indexOf(scan.filter(m=>m.id===record.id)[0]?.status)===-1? <span>手动扫描</span>: <a onClick={()=>{ clearInterval(this.timer); request(`/api/data_marking/biz/${biz}/db-column-tag-rule/${record.id}/run-state-wait-run`, { method: 'POST', credentials: 'same-origin', }).then(resp => { if (resp && resp.success) { let newData=scan.map(n=>(n.id===record.id?{id:n.id,status:1}:n)); this.setState({scan:newData}); let runData=newData.filter(m=>[3,4].indexOf(m.status)===-1); this.timer = setInterval(()=> { console.log("Promise",runData) Promise.all( runData.map((run)=>{ return request(`/api/data_marking/biz/${biz}/db-column-tag-rule/${run.id}/run-state`, { credentials: 'same-origin', }).then(r=>({id:run.id,status:r.data.ruleRunState.id})) }) ).then((val)=>{ const oId=val.map(v=>v.id); newData=newData.map(j=>(oId.indexOf(j.id)>-1?{id:j.id,status:val[oId.indexOf(j.id)].status}:j)); runData=val.filter(m=>[3,4].indexOf(m.status)===-1); console.log("8989",runData,oId) this.setState({scan:newData}); if(!runData.length){ clearInterval(this.timer); } }) },1200); } else { message.error(`[请求失败]${JSON.stringify(resp.data)}`); } }); }} > 手动扫描 </a> } <Divider type="vertical" /> <Popconfirm title="是否删除?" onConfirm={() => this.deleteRuleHandler(record.id)}> <a>删除</a> </Popconfirm> </div> ), }, ]; const { data, page, total, pageSize } = ruleList; return ( <Card bordered={false} title="规则列表" > { this.searchForm() } <Table columns={columns} dataSource={data} rowKey="id" loading={loading} pagination={{ showSizeChanger: true, current: page, pageSize, total, }} footer={() => { return ( <tr className="ant-table-row ant-table-row-level-0"> <td>记录数: {total} </td> </tr> ); }} onChange={this.handleTableChange} /> </Card> ); } } export default RuleList;