一、组件属性分析:
- 使用name绑定到表单上, 虽然提供了value以及onChange但操作数据及表单联动比较麻烦。
- 可以使用formItemProps配置该录入组件Form.Item的属性,比如getValueProps,getValueFromEvent等,
参照antd的Form.Item属性。 - 使用fieldProps配置表单内录入组件属性,比如onChange,onBlur等,具体录入组件可用属性参考antd官方文档
二、案例展示:
1.案例一:
import { EditableProTable } from '@ant-design/pro-components';
import { Button, Form } from 'antd';
import React, { useEffect } from 'react';
export default function index() {
const [form] = Form.useForm();
const columns = [
// 数据展示
// readonly: true 的render处理级别为 renderFormItem > renderText 不会显示render
{
title: '活动名称',
dataIndex: 'title',
readonly: true,
// renderFormItem: () => 'renderFormItem', // 优先处理
// renderText: () => 'renderText',
// render: () => 'render', // 不会显示
},
// 对象数组类型的数据无法使用readonly: true展示
// 数据无法转化会抛出异常
// Error : Objects are not valid as a React child (found: object with keys {name}). If you meant to render a collection of children, use an array instead.
// 此时可以使用 editable: false,
{
title: '活动步骤',
dataIndex: 'steps',
editable: false,
render: (dom, config) => {
console.log(dom); // 对象数组
return config.steps?.map((step, i) => <p key={i}>{step.name}</p>);
},
},
// 数据录入
// input
{
title: '活动名称',
dataIndex: 'title',
// 必填校验
formItemProps: {
rules: [{ required: true, message: '请输入!' }],
},
},
// select
{
title: '活动类型',
dataIndex: 'type',
valueType: 'select',
// 选项推荐写法 数据录入组件有options属性的都可以用
fieldProps: {
options: [
{ value: 1, label: '类型1' },
{ value: 2, label: '类型2' },
],
},
// 官网实例写法(不是很推荐 写法复杂且限制字符串数据类型):
// valueEnum: {
// one: { text: '类型1', status: '1' },
// two: { text: '类型2', status: '2' },
// three: { text: '类型3', status: '3' },
// },
},
// switch
{
title: '状态',
dataIndex: 'state',
initialValue: 'up',
valueType: 'switch',
fieldProps: {
checkedChildren: '上架',
unCheckedChildren: '下架',
},
// switch绑定boolean值只有true/false 可以使用getValueProps和getValueFromEvent再加工
// 其他录入组件都同理
formItemProps: {
getValueProps: (value) => ({ value: value == 'up' }),
getValueFromEvent: (value) => (value ? 'up' : 'down'),
},
},
// 联动
{
title: '活动描述模板',
dataIndex: 'descTemp',
// valueType: 'select',
fieldProps: (form, config) => ({
// options: [
// { value: 1, label: '模板1' },
// { value: 2, label: '模板2' },
// ],
onChange: (changeValue) => {
console.log('switch onchange changeValue =', changeValue);
// {type, isEditable, rowKey, rowIndex, entity}
console.log(config);
// 可以直接拿到数据
const descTemp = form.getFieldValue(['editableData', config.rowIndex, 'descTemp']);
// 联动数据
form.setFieldValue(['editableData', config.rowIndex, 'decs'], '描述模板' + descTemp);
},
}),
},
{
title: '描述',
dataIndex: 'decs',
readonly: true,
},
// 不展示列
{
title: '描述',
hideInTable: true,
dataIndex: 'decs',
readonly: true,
},
// valueType: 'option'
// 这里配置非编辑状态的操作
// 编辑状态的操作 在表格editable属性的actionRender属性配置
{
title: '操作',
key: 'action',
valueType: 'option',
align: 'center',
render: (_, record, index, action) => {
return [
<a
key="eidit"
onClick={() => {
action?.startEditable(record.key);
}}
>
编辑
</a>,
];
},
},
];
return (
<Form form={form}>
<EditableProTable
recordCreatorProps={{
record: () => {
return {
key: `0${Date.now()}`,
};
},
}}
name="editableData" // 绑定到表单上方便管理
columns={columns}
rowKey="key"
/>
</Form>
);
}
2. 案例二:
点击 EditableProTable的编辑按钮,输入讲师编码失去焦点调取接口填充当前行讲师姓名,如若先输入讲师姓名失去焦点调取接口弹出讲师列表:
点击某一个列表前单选按钮,点击确定使用该讲师,按钮弹窗关闭,讲师编码赋值成功,点击 EditableProTable组件 保存 按钮即可。
代码如下:
import React, { useRef, useState, Fragment, useEffect } from 'react';
import { Modal, Button, Table, Form, Collapse, Spin, message, Tooltip } from 'antd';
import { EditableProTable, ProForm, ProTable } from '@ant-design/pro-components';
import { CaretRightOutlined, ExportOutlined } from '@ant-design/icons';
import BasicInfo from '../basicInfo';
import {
getCourseListByClassCode,
getTeacherInfoByStaffCode,
updateClassSchedule,
courseTeacherExportExcel,
} from '@/services/train/train';
const { Panel } = Collapse;
export default function Teacher({
current,
setCurrent,
handleModalVisible,
currentRow,
yesOrNo,
orgList,
idtypeArray,
sexArray,
teacherTypeArray,
yesOrNoObj,
}) {
const [form] = Form.useForm(); // 创建表单
const [saveLoading, setSaveLoading] = useState(false); // 保存讲师按钮
const [teacherListVisible, handleTeacherListVisible] = useState(false); // 讲师姓名列表 的弹窗
const [teacherList, setTeacherList] = useState([]); // 讲师列表
const [curConfigAndForm, setCurConfigAndForm] = useState({}); // 讲师姓名失去焦点时当前行信息
const [curTeacherInfo, setTeacherInfo] = useState({}); // 讲师弹窗列表点击确定时讲师个人信息
const [spinLoading, setSpinLoading] = useState(false); // loading
const [courseData, setCourseData] = useState([]); // 课表讲师列表数据
const [editableKeys, setEditableRowKeys] = useState([]); // 课程讲师信息列表编辑
const [downloadParams, setDownloadParams] = useState(''); //导出参数
useEffect(() => {
setSpinLoading(true);
if (currentRow && currentRow.classCode) {
setDownloadParams(currentRow.classCode);
getCourseList(currentRow.classCode);
} else {
setSpinLoading(false);
}
}, []);
const getCourseList = (classCode) => {
getCourseListByClassCode(classCode).then((res) => {
if (res && res.success && res.data) {
const { data } = res;
form.setFieldsValue({
teacherDetails: data,
});
setSpinLoading(false);
}
});
setSpinLoading(false);
};
// 导出讲师信息
const genExtra = () => (
<Tooltip placement="topLeft" title="点击导出讲师课表信息">
<Button
type="default"
key="export"
loading={spinLoading}
onClick={() => {
courseTeacherExportExcel({
downloadParams,
setSpinLoading,
yesOrNoObj,
});
}}
>
<ExportOutlined /> 导出数据
</Button>
</Tooltip>
);
// 保存课表讲师信息
const handleSubmit = (value) => {
const { teacherDetails } = value;
setSaveLoading(true);
let flag = teacherDetails.every((item, index) => {
if (!item.staffCode) {
message.error(`第${index + 1}行, 讲师编码或者讲师姓名不能为空!`);
return false;
}
return item.staffCode != null || item.staffCode != '';
});
if (flag) {
updateClassSchedule(teacherDetails).then((res) => {
if (res && res.success && res?.data?.code == '200') {
setSaveLoading(false);
message.success('保存讲师信息成功!');
} else {
message.error(res?.data?.message);
}
setSaveLoading(false);
});
} else {
setSaveLoading(false);
}
};
// 讲师列表点击选择讲师信息
const rowSelectOnChange = (selectedRowKeys, selectedRows) => {
if (selectedRows && selectedRows.length > 0) {
console.log('selectedRows', selectedRows);
console.log('curConfigAndForm', curConfigAndForm);
setTeacherInfo(selectedRows[0]);
}
};
//确定使用该讲师
const confirmCurTeacher = () => {
// 联动数据赋值助教姓名
curConfigAndForm.form.setFieldValue(
['teacherDetails', curConfigAndForm.config.rowIndex, 'staffCode'],
curTeacherInfo.staffCode,
);
handleTeacherListVisible(false);
};
// 课程列表
const columns2 = [
{
title: '课程名称',
dataIndex: 'courseName',
editable: false,
},
{
title: '授课日期',
dataIndex: 'lectureDate',
valueType: 'date',
editable: false,
},
{
title: '课时',
dataIndex: 'courseHours',
editable: false,
},
{
title: '开始时间',
dataIndex: 'beginTime',
editable: false,
},
{
title: '结束时间',
dataIndex: 'endTime',
editable: false,
},
{
title: '是否计入讲师课时',
dataIndex: 'isTakeIntoHours',
editable: false,
valueType: 'select',
fieldProps: {
options: yesOrNo,
fieldNames: {
// 树属性重命名
label: 'codeName',
value: 'code',
},
},
},
{
title: '是否支付课时津贴',
dataIndex: 'isPayAllowance',
editable: false,
valueType: 'select',
fieldProps: {
options: yesOrNo,
fieldNames: {
// 树属性重命名
label: 'codeName',
value: 'code',
},
},
},
{
title: '讲师编码',
dataIndex: 'staffCode',
formItemProps: {
rules: [
{
required: true,
message: '此项为必填项',
},
],
},
fieldProps: (form, config) => ({
onBlur: (changeValue) => {
// 可以直接拿到当前行数据
const staffCode = form.getFieldValue(['teacherDetails', config.rowIndex, 'staffCode']);
if (staffCode) {
getTeacherInfoByStaffCode({ staffCode: staffCode }).then((res) => {
if (res && res.success && res?.data?.code == '200') {
// 联动数据赋值姓名
form.setFieldValue(
['teacherDetails', config.rowIndex, 'teacherName'],
res?.data?.data[0].name,
);
}
});
} else {
form.setFieldValue(['teacherDetails', config.rowIndex, 'teacherName'], '');
}
},
}),
},
{
title: '讲师姓名',
dataIndex: 'teacherName',
formItemProps: {
rules: [
{
required: false,
message: '此项为必填项',
},
],
},
fieldProps: (form, config) => ({
onBlur: (changeValue) => {
// 可以直接拿到当前行数据
const teacherName = form.getFieldValue([
'teacherDetails',
config.rowIndex,
'teacherName',
]);
if (teacherName) {
getTeacherInfoByStaffCode({ name: teacherName }).then((res) => {
if (res && res.success && res?.data?.code == '200') {
if (res?.data?.data.length > 0) {
res?.data?.data.map((item, index) => {
item.listKey = index + 1;
});
}
setCurConfigAndForm({
form,
config,
});
setTeacherList(res?.data?.data);
// 联动数据赋值助教姓名
// form.setFieldValue(
// ['teacherDetails', config.rowIndex, 'staffCode'],
// res?.data?.data?.name,
// );
}
});
handleTeacherListVisible(true);
} else {
form.setFieldValue(['teacherDetails', config.rowIndex, 'staffCode'], '');
}
},
}),
},
{
title: '操作',
key: 'action',
valueType: 'option',
render: (_, record, index, action) => {
return [
<a
key="editable"
onClick={() => {
action?.startEditable(record.seriesNo);
}}
>
编辑
</a>,
];
},
},
];
// 讲师列表
const columns3 = [
{
title: '管理机构',
dataIndex: 'manageCom',
width: 180,
valueType: 'select',
fieldProps: {
options: orgList,
fieldNames: {
label: 'name',
value: 'comCode',
},
},
},
{
title: '营销员编码(工号)',
dataIndex: 'staffCode',
width: 150,
},
{
title: '姓名',
dataIndex: 'name',
width: 180,
},
{
title: '性别',
dataIndex: 'sex',
width: 150,
valueType: 'select',
fieldProps: {
options: sexArray,
fieldNames: {
// 树属性重命名
label: 'codeName',
value: 'code',
},
},
},
{
title: '证件类型',
dataIndex: 'idNoType',
width: 150,
valueType: 'select',
fieldProps: {
options: idtypeArray,
fieldNames: {
// 树属性重命名
label: 'codeName',
value: 'code',
},
},
},
{
title: '证件号码',
dataIndex: 'idNo',
width: 180,
},
{
title: '讲师类型',
dataIndex: 'teacherType',
width: 150,
valueType: 'select',
fieldProps: {
options: teacherTypeArray,
fieldNames: {
// 树属性重命名
label: 'codeName',
value: 'code',
},
},
},
];
return (
<Spin spinning={spinLoading}>
<BasicInfo basicInfo={currentRow} />
<Collapse
defaultActiveKey={['12']}
bordered={false}
ghost
collapsible="header"
expandIcon={({ isActive }) => <CaretRightOutlined rotate={isActive ? 90 : 0} />}
>
<Panel
header="课程讲师信息"
key="12"
style={{ fontWeight: 700, width: '90%' }}
extra={genExtra()}
>
<ProForm
// layout="horizontal"
form={form}
style={{ fontWeight: 500 }}
submitter={{
// 完全自定义整个区域
render: (props, doms) => {
return [
<Button
type="primary"
key="save"
loading={saveLoading}
onClick={() => {
// props.form?.resetFields();
props.form?.submit?.();
}}
>
保存讲师信息
</Button>,
];
},
}}
// initialValues={{ teacherDetails: tableData }}
onFinish={handleSubmit}
>
<ProForm.Item>
<EditableProTable
name="teacherDetails"
columns={columns2}
recordCreatorProps={{
style: {
display: 'none',
}, // 隐藏添加一行
}}
rowKey="seriesNo"
editable={{
type: 'single',
actionRender: (row, config, defaultDom) => [defaultDom?.save],
editableKeys,
onChange: setEditableRowKeys,
}}
/>
{/* <EditableProTable
recordCreatorProps={{
record: () => {
return {
key: `0${Date.now()}`,
};
},
}}
columns={columns2}
rowKey="seriesNo"
/> */}
</ProForm.Item>
</ProForm>
</Panel>
</Collapse>
<div style={{ position: 'fixed', bottom: '30px' }}>
<Button
type="primary"
key="back2"
onClick={() => {
// handleModalVisible(false);
setCurrent(current - 1);
}}
style={{ marginRight: 10 }}
>
上一步
</Button>
<Button
type="primary"
key="next2"
onClick={() => {
setCurrent(current + 1);
}}
>
下一步
</Button>
</div>
{teacherListVisible ? (
<Modal
title="讲师列表"
visible={teacherListVisible}
destroyOnClose
width="60%"
onCancel={() => handleTeacherListVisible(false)}
footer={[
<Button key="confirm" type="primary" onClick={() => confirmCurTeacher()}>
确定使用该讲师
</Button>,
<Button key="close" onClick={() => handleTeacherListVisible(false)}>
关闭
</Button>,
]}
maskClosable={false}
>
<ProTable
style={{ marginLeft: '-20px' }}
headerTitle=""
rowKey="listKey"
options={false}
search={false}
pagination={false}
dataSource={teacherList}
revalidateOnFocus={false}
columns={columns3}
rowSelection={{
selections: [Table.SELECTION_ALL, Table.SELECTION_INVERT],
defaultSelectedRowKeys: [],
type: 'radio',
onChange: rowSelectOnChange,
}}
tableAlertOptionRender={false}
tableAlertRender={false}
/>
</Modal>
) : null}
</Spin>
);
}