工作中遇到的问题特此记录一下
技术难点一: 动态列
...dateList.map(dateStr => {
const weekStr = getWeekByDate(dateStr, dateFormat);
return {
title: `${dateStr}(周${weekStr})`,
dataIndex: `${dateStr}周${weekStr}`,
align: 'center',
key: `${dateStr}周${weekStr}`,
editable: true,
width: '130px',
render: (text) => <span>{text}</span>,
};
}),
技术难点二: 合并单元格
根据 antd 官网给的 合并单元格 给的例子实现不了
如下解决的例子
const columns = [
{
title: '地区',
dataIndex: '地区',
align: 'center',
key: '地区',
editable: false,
width: '80px',
fixed: 'left',
render: (text, record, index) => {
const rowSpan = this.calculateRowSpan(this.state[dataSourceKey], text)[index];
return {
children: <span>{text}</span>,
props: {
rowSpan,
},
};
}
},
]
如下是页面的全部代码
import React, {Component} from 'react'
import style from './index.less'
import EditableCell from '../components/EditableCell'
import {Button, DatePicker, Table, Form, InputNumber, message, Modal, Radio, Checkbox} from 'antd'
import {getAllDate, getWeekByDate} from '../../../utils/utils'
import moment from 'moment'
import {CalculatorOutlined, SearchOutlined, ExperimentOutlined, FieldTimeOutlined} from '@ant-design/icons'
import ButtonBar from '../../../components/ButtonBar'
import DevideBar from '../../../components/DevideBar'
import {
getTableData,
updateTableData,
computeTableData,
initTableData,
updateSimultaneousRate,
getSimultaneousRate,
deleteTableData
} from '../../../axios/UnitManage/FutureTenDayForecastLoad'
const dateFormat = 'YYYY-MM-DD'
class FutureTenDayForecastLoad extends Component {
constructor(props) {
super(props)
this.formRef = React.createRef()
this.buttonItems = [
{
icon: <SearchOutlined/>,
name: "查询",
style: {background: 'rgb(255, 255, 255)', borderColor: 'rgb(33, 142, 147)', color: 'rgb(33, 142, 147)'},
btnFunction: () => this.onSearch(),
},
{
icon: <ExperimentOutlined/>,
name: "初始化",
style: {background: "rgb(33, 142, 147)", borderColor: "#7ccdc7"},
btnFunction: () => {
this.setState({initModalVisible: true})
},
},
{
icon: (<CalculatorOutlined style={{fontSize: '16px',}}/>),
name: '计算',
style: {background: '#FFA800', borderColor: '#FFA800'},
btnFunction: () => this.compute()
},
{
icon: <FieldTimeOutlined/>,
name: "同时率",
style: {background: "rgb(33, 142, 147)", borderColor: "#7ccdc7"},
btnFunction: () => {
this.setState({simultaneousRateModalVisible: true})
},
},
]
this.state = {
orgCode: localStorage.getItem(FN.COOKIE_KEY_PREFIX + 'deptcode'),
startDate: moment('2024-07-10').format(dateFormat),
startDate1: moment().format(dateFormat),
endDate: moment('2024-07-11').add(9, 'days').format(dateFormat),
endDate1: moment().format(dateFormat),
dataSource1: [],
dataSource2: [],
dataSource3: [],
dataSource4: [],
columns1: [],
columns2: [],
columns3: [],
columns4: [],
// 豫南
yuNan: false,
editingKey: '',
originalRecord: {},
// 初始化模态框
initModalVisible: false,
// 同时率模态框
simultaneousRateModalVisible: false,
simultaneousRate: 0.995,
simultaneousRateId: null,
// 预计划/计划
selectedOption: '预计划',
// 确认删除模态框
confirmModalVisible: false,
// 确认删除记录
deleteRecord: {},
// 为每个表单创建单独的 formRef
formRefs: {
form1: React.createRef(),
form2: React.createRef(),
form3: React.createRef(),
form4: React.createRef(),
}
}
}
componentDidMount() {
this.getSimultaneousRate()
this.createTable()
}
getSimultaneousRate = async () => {
try {
let res = await getSimultaneousRate(this.state.simultaneousRate)
if (res.code === 200 && res.data.length > 0) {
this.setState({
simultaneousRate: res.data[0]["confValue"],
simultaneousRateId: res.data[0]["id"],
})
} else {
message.error('获取同时率失败')
}
} catch (e) {
message.error('获取同时率失败')
}
}
// 初始化按钮
initTableData = async () => {
try {
this.setState({initModalVisible: false})
let res = await initTableData(this.state.startDate1, this.state.endDate1, this.state.selectedOption,)
if (res.code === 200) {
message.success('初始化成功')
} else {
message.error('初始化失败')
}
} catch (e) {
message.error('初始化失败')
}
}
handleChange = (e) => {
this.setState({selectedOption: e.target.value})
}
handleSimultaneousRate = async () => {
try {
this.setState({simultaneousRateModalVisible: false})
let res = await updateSimultaneousRate(this.state.simultaneousRateId, this.state.simultaneousRate,)
if (res.code === 200) {
message.success('同时率计算成功')
this.getTableData()
} else {
message.error('同时率计算失败')
}
} catch (e) {
message.error('同时率计算失败')
}
}
// 同时率修改事件
simultaneousRateChange = (value) => {
this.setState({
simultaneousRate: value
})
}
// 计算按钮
compute = async () => {
try {
let res = await computeTableData(this.state.startDate, this.state.endDate, this.state.orgCode,)
if (res.code === 200) {
message.success('计算成功')
} else {
message.error('计算失败')
}
} catch (e) {
message.error('计算失败')
}
}
// 查询按钮
onSearch = () => {
this.createTable()
}
calculateRowSpan = (data, region) => {
const rowSpan = []
let currentRowSpan = 1
for (let i = 0
if (i === 0 || data[i]['地区'] !== data[i - 1]['地区']) {
currentRowSpan = 1
for (let j = i + 1
if (data[j]['地区'] === data[i]['地区']) {
currentRowSpan++
} else {
break
}
}
rowSpan[i] = currentRowSpan
} else {
rowSpan[i] = 0
}
}
return rowSpan
}
createTable = () => {
const {startDate, endDate, formRefs} = this.state
const dateList = getAllDate(startDate, endDate)
const createColumns = (dataSourceKey, formRef) => [
{
title: '地区',
dataIndex: '地区',
align: 'center',
key: '地区',
editable: false,
width: '80px',
fixed: 'left',
render: (text, record, index) => {
const rowSpan = this.calculateRowSpan(this.state[dataSourceKey], text)[index]
return {
children: <span>{text}</span>,
props: {
rowSpan,
},
}
}
},
{
title: '',
dataIndex: '类型名称',
key: '类型名称',
width: 100,
fixed: 'left',
editable: false,
},
...dateList.map(dateStr => {
const weekStr = getWeekByDate(dateStr, dateFormat)
return {
title: `${dateStr}(周${weekStr})`,
dataIndex: `${dateStr}周${weekStr}`,
align: 'center',
key: `${dateStr}周${weekStr}`,
editable: true,
width: '130px',
render: (text) => <span>{text}</span>,
}
}),
{
title: '编辑',
dataIndex: 'operation',
width: "200px",
align: "center",
fixed: 'right',
render: (_, record) => {
const editable = this.isEditing(record)
return editable ? (
<span>
<a onClick={() => this.save(record, formRef)} style={{marginRight: 8}}>
<img alt="" className="img_icon" src={require('../../../images/sys/save_icon.png')}/>
</a>
<a onClick={() => this.cancel(record)}>取消</a>
</span>
) : (
<span>
<Button
size="small"
style={{
borderColor: '#2CAFE6',
color: '#2CAFE6',
display: 'inline-flex',
alignItems: 'center'
}}
icon={<img alt="" style={{width: '14px', marginRight: '6px'}}
src={require('../../../images/icon/edit.png')}/>}
disabled={this.state.editingKey !== ''}
onClick={() => this.edit(record, formRef)}
>
编辑
</Button>
<Button
size="small"
style={{
borderColor: '#EC4758',
color: '#EC4758',
display: 'inline-flex',
alignItems: 'center'
}}
icon={<img alt="" style={{width: '14px', marginRight: '6px'}}
src={require('../../../images/icon/delete.png')}/>}
disabled={this.state.editingKey !== ''}
onClick={() => this.setState({confirmModalVisible: true, deleteRecord: record})}
>
删除
</Button>
</span>
)
},
},
]
this.setState({
columns1: createColumns('dataSource1', formRefs['form1']),
columns2: createColumns('dataSource2', formRefs['form2']),
columns3: createColumns('dataSource3', formRefs['form3']),
columns4: createColumns('dataSource4', formRefs['form4'])
}, () => {
this.getTableData()
})
}
getTableData = async () => {
try {
let res = await getTableData(this.state.orgCode, this.state.startDate, this.state.endDate, this.state.yuNan)
if (res.code === 200) {
this.setState({
dataSource1: res.data['晚高峰'] || [],
dataSource2: res.data['腰荷'] || [],
dataSource3: res.data['早高峰'] || [],
dataSource4: res.data['低谷'] || [],
})
} else {
message.error('获取数据失败')
}
} catch (e) {
message.error('获取数据失败')
console.error('获取数据失败:', e)
}
}
// 豫南 checkbox
onCheckbox = (e) => {
this.setState({yuNan: e.target.checked})
}
datePickerChange = (date, dateString) => {
this.setState({startDate: dateString[0], endDate: dateString[1]}, this.createTable)
}
futureThreeDay = () => {
this.setState({
startDate1: moment().add(1, 'days').format(dateFormat),
endDate1: moment().add(3, 'days').format(dateFormat),
})
}
futureTenDay = () => {
this.setState({
startDate1: moment().add(1, 'days').format(dateFormat),
endDate1: moment().add(10, 'days').format(dateFormat),
})
}
dateStartChange = (date, dateString) => {
this.setState({startDate1: dateString})
}
dateEndChange = (date, dateString) => {
this.setState({endDate1: dateString})
}
isEditing = (record) => record.UUID === this.state.editingKey
edit = (record, formRef) => {
this.setState({editingKey: record.UUID, originalRecord: {...record}})
formRef.current.setFieldsValue(record)
}
cancel = () => {
this.setState({
editingKey: '',
originalRecord: {},
})
}
save = async (record, formRef) => {
try {
const values = await formRef.current.validateFields()
if (!values.UUID) {
values.UUID = record.UUID
}
const changes = {}
for (const key in values) {
if (values[key] !== this.state.originalRecord[key]) {
changes[key] = values[key].trim()
}
}
if (Object.keys(changes).length === 0) {
message.info('没有检测到变化。')
return
}
const dataToUpdate = {...this.state.originalRecord, ...changes}
dataToUpdate['areaId'] = this.state.orgCode
dataToUpdate['measTypeCode'] = record['类型']
let res
if (record.UUID) {
res = await updateTableData(dataToUpdate)
}
if (res.code === 200) {
message.success('修改成功')
this.getTableData()
this.cancel()
} else {
message.error('更新失败')
}
} catch (error) {
console.error("保存数据时出错:", error)
message.error('保存失败')
}
}
onDelete = async () => {
try {
this.state.deleteRecord['areaId'] = this.state.orgCode
this.state.deleteRecord['measTypeCode'] = this.state.deleteRecord['类型']
const res = await deleteTableData(this.state.deleteRecord)
if (res.code === 200) {
message.success('删除成功')
this.getTableData()
} else {
message.error('删除失败')
}
this.setState({confirmModalVisible: false})
} catch (e) {
message.error('删除失败')
}
}
renderTable = (title, columns, dataSource, formRef) => {
const mergedColumns = columns.map(col => ({
...col,
onCell: record => ({
record,
inputType: col.dataIndex === 'number' ? 'number' : 'text',
dataIndex: col.dataIndex,
title: col.title,
editing: (col.editable && (this.isEditing(record) || record.isAdd)) || (record.isAdd && col.editable),
}),
}))
return (
<div className='table-item'>
<div className='table-header'>
<DevideBar className='DevideBar' title={title}/>
</div>
<div className='table-body'>
<Form ref={formRef} component={false}>
<Table
components={{body: {cell: EditableCell}}}
columns={mergedColumns}
dataSource={dataSource}
scroll={{x: 'auto'}}
pagination={false}
rowKey='UUID'
/>
</Form>
</div>
</div>
)
}
render() {
return (
<div className={style.container}>
<div className='page-header'>
<div style={{float: 'left', width: '12%'}}>
<DevideBar className='DevideBar2' title={"同时率:" + this.state.simultaneousRate}/>
</div>
<Checkbox onChange={this.onCheckbox}
style={{float: 'right', width: '8%', color: '#218e93'}}>豫南</Checkbox>
<div className='text'>日期:</div>
<DatePicker.RangePicker
style={{marginRight: '10px'}}
allowClear={false}
defaultValue={[moment(this.state.startDate), moment(this.state.endDate)]}
onChange={this.datePickerChange}
format='YYYY-MM-DD'
/>
<ButtonBar buttonItems={this.buttonItems}/>
</div>
<div className='table-container'>
{this.renderTable('未来10天晚高峰预测(万千瓦)', this.state.columns1, this.state.dataSource1, this.state.formRefs.form1)}
{this.renderTable('未来10天腰荷预测(万千瓦)', this.state.columns2, this.state.dataSource2, this.state.formRefs.form2)}
{this.renderTable('未来10天早高峰预测(万千瓦)', this.state.columns3, this.state.dataSource3, this.state.formRefs.form3)}
{this.renderTable('未来10天低谷预测(万千瓦)', this.state.columns4, this.state.dataSource4, this.state.formRefs.form4)}
</div>
{/* 初始化弹窗 */}
<Modal
title="初始化"
onOk={this.initTableData}
visible={this.state.initModalVisible}
onCancel={() => {
this.setState({initModalVisible: false})
}}
okText="确认获取"
cancelText="取消"
>
<Radio.Group onChange={this.handleChange} value={this.state.selectedOption}>
<Radio value="预计划">预计划</Radio>
<Radio value="计划">计划</Radio>
</Radio.Group>
<Button type="primary"
onClick={this.futureThreeDay.bind(this)}>未来三天</Button>  
<Button type="primary" onClick={this.futureTenDay.bind(this)}>未来十天</Button><br/><br/>
起始日期:
<DatePicker key="start-date-datepicker"
value={moment(this.state.startDate1, dateFormat)}
format={dateFormat}
onChange={this.dateStartChange}
allowClear={false}
/><br/><br/>
结束日期:
<DatePicker key="end-date-datepicker"
value={moment(this.state.endDate1, dateFormat)}
format={dateFormat}
onChange={this.dateEndChange}
allowClear={false}
/>
</Modal>
<Modal
key="simultaneous-modal"
title="同时率数据维护"
onOk={this.handleSimultaneousRate}
visible={this.state.simultaneousRateModalVisible}
onCancel={() => {
this.setState({simultaneousRateModalVisible: false})
}}
okText="保存"
cancelText="取消"
>
<div style={{textAlign: "center"}}>
同时率:
<InputNumber value={this.state.simultaneousRate} onChange={this.simultaneousRateChange} step={0.001}></InputNumber>
</div>
</Modal>
<Modal
key="confirm-modal"
title="确认删除"
visible={this.state.confirmModalVisible}
onOk={this.onDelete}
onCancel={() => {
this.setState({confirmModalVisible: false})
}}
okText="确认"
cancelText="取消"
>
确认删除该记录吗?
</Modal>
</div>
)
}
}
export default FutureTenDayForecastLoad