Ant Design 使用Tabs和Form.List 实现二维数组结构

本文介绍了一种使用React和Ant Design实现可编辑二维数组结构的方法,该结构支持音频文件的上传、播放及动态增删。通过Form.List组件与Tabs标签页组件组合使用,实现了数据的有效组织和提交。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

使用场景与实现效果

需求的数据结构为可编辑的二维数组结构,在Form表单中提交数据到服务端,下面为数据结构的例子:

[
	[{
		"text": "",
		"audioUrl": "",
		"emotion": ""
	}, {
		"text": "",
		"audioUrl": "",
		"emotion": ""
	}],
	[{
		"text": "",
		"audioUrl": "",
		"emotion": ""
	}, {
		"text": "",
		"audioUrl": "",
		"emotion": ""
	}]
]

实现效果:实现元素的动态添加,包含音频的上传与播放

实现思路

在Form表单组件中使用Tabs标签页组件(数据为第一层数组),然后再Tabs选项卡组件中使用Form.List组件(数据为第二层数组),最终提交Form表单获取想要的数据结构。

实现代码

import React from "react";
import ReactDOM from "react-dom";
import "antd/dist/antd.css";
import "./index.css";
import {
  Tabs,
  Form,
  Row,
  Col,
  Divider,
  Input,
  Select,
  Button,
  Upload,
  message
} from "antd";
import {
  MinusCircleOutlined,
  PlusOutlined,
  UploadOutlined,
  SoundTwoTone
} from "@ant-design/icons";

const { TabPane } = Tabs;

const formItemLayout = {
  labelCol: { span: 4 },
  wrapperCol: { span: 14 }
};

// 音频样式
const formItemYin = {
  labelCol: { span: 4 },
  wrapperCol: { span: 20 }
};

const initialPanes = [
  { title: "方案1", key: "1" },
  { title: "方案2", key: "2" },
  { title: "方案3", key: "3" }
];

const initialForm = {
  data1: [
    {
      audioUrl: "",
      text:
        "因为怕水痘乘着风就跑到别人身上去了呀#哈哈大笑#这样我就不痒了,别人痒。",
      emotion: "Default"
    }
  ],
  data2: [
    { audioUrl: "", text: "哈哈大笑#这样我就不痒了,别人痒。", emotion: "sad" }
  ],
  data3: [{ audioUrl: "", text: "因为怕水痘乘着风就跑到", emotion: "angry" }]
};

class Demo extends React.Component {
  newTabIndex = 0;

  state = {
    activeKey: initialPanes[0].key,
    panes: initialPanes,
    formValues: initialForm
  };

  formRef = React.createRef();

  // tab 改变触发
  onChange = (activeKey) => {
    this.setState({ activeKey });
  };

  // tab 修改触发
  onEdit = (targetKey, action) => {
    this[action](targetKey);
  };

  // tab 新增触发
  add = () => {
    const { panes } = this.state;
    const activeKey = panes.length + 1;
    const newPanes = [...panes];
    newPanes.push({
      title: "方案" + activeKey,
      key: activeKey.toString()
    });
    this.setState({
      panes: newPanes,
      activeKey: activeKey.toString()
    });
  };

  // tab 移除触发
  remove = (targetKey) => {
    const { panes, activeKey } = this.state;
    let newActiveKey = activeKey;
    let lastIndex = 0;
    panes.forEach((pane, i) => {
      if (pane.key === targetKey) {
        lastIndex = i - 1;
      }
    });
    const newPanes = panes.filter((pane) => pane.key !== targetKey);
    if (newPanes.length && newActiveKey === targetKey) {
      if (lastIndex >= 0) {
        newActiveKey = newPanes[lastIndex].key;
      } else {
        newActiveKey = newPanes[0].key;
      }
    }
    this.setState({
      panes: newPanes,
      activeKey: newActiveKey
    });
  };

  // 提交form 表单
  onFinish = (values) => {
    console.log(values);
  };

  // 上传组件触发
  handleChange(info, index) {
    if (info.file.status === "uploading") {
      console.log("正在上传");
    }
    if (info.file.status !== "uploading") {
      console.log(info.file, info.fileList);
    }
    if (info.file.status === "done") {
      if (info.file.response.result === true) {
        console.log(info.file.response.message);
        this.formRef.current.setFields([
          { name: index, value: info.file.response.message }
        ]);
      } else {
        message.error(`上传失败:${info.file.response.message}.`);
      }
      message.success(`${info.file.name} 上传成功`);
    } else if (info.file.status === "error") {
      message.error(`${info.file.name}上传失败.`);
    }
  }

  // 多媒体播放/暂停
  handleAudio = (index) => {
    var audio = document.getElementById("audio");
    if (audio && audio.src) {
      if (audio.src === this.formRef.current.getFieldValue(index)) {
        // 当前的资源
        if (audio.paused) {
          audio.play(); // 这个就是播放
        } else {
          audio.pause(); // 这个就是暂停
        }
      } else {
        // 存在src 但不是当前点击的资源,则播放当前资源
        audio.src = this.formRef.current.getFieldValue(index);
        audio.play();
      }
    } else {
      // 没有音频路径 则是播放
      audio && (audio.src = this.formRef.current.getFieldValue(index));
      audio && audio.play();
    }
  };

  // 手动录入音频地址   拆分出音频名称
  handleSelf = (e) => {
    if (e.target.value) {
      const a = e.target.value.split("/");
      if (a && a.length > 0) {
        console.log("name", a[a.length - 1]); // 上传音频后更新节点标题名称
      } else {
        message.warning("请输入正确的音频地址");
      }
    }
  };

  render() {
    const { panes, activeKey, formValues } = this.state;
    return (
      <>
        <audio controls src="" id="audio" hidden>
          {" "}
        </audio>
        <Form
          {...formItemLayout}
          onFinish={this.onFinish}
          initialValues={formValues}
          ref={this.formRef}
        >
          <Tabs
            type="editable-card"
            onChange={this.onChange}
            activeKey={activeKey}
            onEdit={this.onEdit}
          >
            {panes.map((pane) => (
              <TabPane
                tab={pane.title}
                key={pane.key}
                closable={pane.closable}
                forceRender
              >
                <Form.List name={`data${pane.key}`}>
                  {(fields, { add, remove }) => {
                    return (
                      <div>
                        {fields.map((field, index) => (
                          <>
                            <Divider>
                              简单对话{index + 1} &nbsp;&nbsp;
                              <MinusCircleOutlined
                                onClick={() => {
                                  remove(field.name);
                                }}
                              />
                            </Divider>
                            <Row gutter={24}>
                              <Col span={24}>
                                <Form.Item
                                  {...field}
                                  name={[field.name, "text"]}
                                  label="文本"
                                  fieldKey={[field.fieldKey, "text"]}
                                  initialValue=""
                                >
                                  <Input placeholder="请输入文本" />
                                </Form.Item>
                              </Col>
                            </Row>
                            <Row gutter={24}>
                              <Col span={17} offset={1}>
                                <Form.Item
                                  {...field}
                                  label="音频地址"
                                  {...formItemYin}
                                  name={[field.name, "audioUrl"]}
                                  fieldKey={[field.fieldKey, "audioUrl"]}
                                  initialValue=""
                                >
                                  <Input
                                    placeholder="请输入音频地址"
                                    id={`audioUrl${index}`}
                                    onChange={(e) => this.handleSelf(e)}
                                  />
                                </Form.Item>
                              </Col>

                              <Col span={3}>
                                <Upload
                                  name="file"
                                  accept="audio/mp3"
                                  showUploadList={false}
                                  method="POST"
                                  action={`/flowChart/upload_audio.do`}
                                  headers={{
                                    authorization: "authorization-text"
                                  }} // `audioUrl${index}`
                                  onChange={(info) =>
                                    this.handleChange(info, [
                                      `data${pane.key}`,
                                      index,
                                      "audioUrl"
                                    ])
                                  }
                                >
                                  <Button size="small">
                                    {" "}
                                    <UploadOutlined />
                                    上传
                                  </Button>
                                </Upload>
                              </Col>
                              <Col span={3} style={{ paddingLeft: "30px" }}>
                                <SoundTwoTone
                                  onClick={() =>
                                    this.handleAudio([
                                      `data${pane.key}`,
                                      index,
                                      "audioUrl"
                                    ])
                                  }
                                />
                              </Col>
                            </Row>
                            <Row gutter={24}>
                              <Col span={24}>
                                <Form.Item
                                  {...field}
                                  name={[field.name, "emotion"]}
                                  label="表情"
                                  fieldKey={[field.fieldKey, "emotion"]}
                                  initialValue="Default"
                                >
                                  <Select placeholder="请选择表情">
                                    <Select.Option value="Default">
                                      开心
                                    </Select.Option>
                                    <Select.Option value="sad">
                                      难过
                                    </Select.Option>
                                    <Select.Option value="angry">
                                      生气
                                    </Select.Option>
                                  </Select>
                                </Form.Item>
                              </Col>
                            </Row>
                          </>
                        ))}
                        <Row gutter={24}>
                          <Col span={15} offset={6}>
                            <Form.Item>
                              <Button
                                type="dashed"
                                onClick={() => {
                                  add();
                                }}
                                block
                              >
                                <PlusOutlined /> 添加简单对话
                              </Button>
                            </Form.Item>
                          </Col>
                        </Row>
                      </div>
                    );
                  }}
                </Form.List>
              </TabPane>
            ))}
          </Tabs>
          <Form.Item wrapperCol={{ offset: 9, span: 14 }}>
            <Button type="primary" htmlType="submit">
              {" "}
              提交{" "}
            </Button>
            <Button
              style={{ marginLeft: "5px" }}
              htmlType="button"
              onClick={() => this.props.changeTalkVisible(false)}
            >
              取消
            </Button>
          </Form.Item>
        </Form>
      </>
    );
  }
}

ReactDOM.render(<Demo />, document.getElementById("container"));

部分功能记录

1、Form.List中的音频上传成功后,回显到对应的输入框中

<Upload name='file' accept="audio/mp3" showUploadList={false} method="POST"
                                                      action={`${PATH}/flowChart/upload_audio.do`}
                                                      headers= {{ authorization: 'authorization-text', }} // `audioUrl${index}`
                                                      onChange={(info)=>this.handleChange(info,[`data${pane.key}`,index, 'audioUrl'])}
                                                    >



// 上传组件触发
    handleChange (info:any,  index:any){
        if (info.file.status === 'uploading') {
          console.log("正在上传")
        }
        if (info.file.status !== 'uploading') {
          console.log(info.file, info.fileList);
        }
        if (info.file.status === 'done') {
          if(info.file.response.result === true){
            console.log(info.file.response.message);
            this.formRef.current.setFields([{name:index,value:info.file.response.message}])
          }else{
            message.error(`上传失败:${info.file.response.message}.`);
          }
          message.success(`${info.file.name} 上传成功`);
        } else if (info.file.status === 'error') {
          message.error(`${info.file.name}上传失败.`);
        }
    }

this.formRef.current.setFields([{name:index,value:info.file.response.message}])   实现上传后的回显

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值