You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Vue中Tiptap富文本写入JSON遇值为空问题(含动态表格场景)

动态表格中Tiptap富文本内容无法写入JSON的解决方案

问题描述

在Vue文件中使用Tiptap富文本编辑器,动态表格内也嵌入了该编辑器,但尝试将表格中的富文本内容写入JSON时,对应值显示为undefined或null,无法正常添加。以下是原始代码:

<template>
  <v-container>
    <v-form lazy-validation ref="form">
      <v-card class="pa-5"
        >rich text editor
        <v-row> <Tiptap v-model="richText" /> </v-row>
      </v-card>

      <table class="responsive-table">
        <thead>
          <tr>
            <th scope="col">#</th>

            <th class="text-left">Sample Depth</th>

            <th class="text-left">Text</th>
          </tr>
        </thead>

        <tbody>
          <tr v-for="(imageTable, index) in imageTables" :key="index">
            <td scope="row" class="trashIconContainer">
              <i
                class="far fa-trash-alt"
                @click="deleteimageRow(index, imageTable)"
              ></i>
            </td>

            <td data-label="sampleDepth">
              <input
                v-model="imageTable.sampleDepth"
                class="form-control"
                type="number"
              />
            </td>

            <td>
              <Tiptap v-model="text" />
            </td>

            <td>
              <button @click="deleteimageRow" class="is-danger">Delete</button>
            </td>
          </tr>
        </tbody>
      </table>

      <button type="button" class="btn btn-info" @click="addimageRow">
        <i class="fas fa-plus-circle"></i>

        Add
      </button>
      <v-row>
        <v-col>
          <v-btn
            width="100%"
            class="primary"
            :loading="isPdfGenerating"
            @click="generateReport(true)"
            >Generate draft report</v-btn
          >
        </v-col>
      </v-row>

      <v-row>
        <v-col>
          <v-btn
            width="100%"
            class="primary"
            :disabled="!isPdfGenerated"
            @click="savePdf"
            >Download generated report</v-btn
          >
        </v-col>

        <v-col>
          <v-btn
            width="100%"
            class="primary"
            :disabled="!isPdfGenerated"
            @click="generateReport(false)"
            >Make report official</v-btn
          >
        </v-col>
      </v-row>
    </v-form>
  </v-container>
</template>

<script>
import Tiptap from "/src/components/TiptapView.vue";
export default {
  name: "TableView",
  components: {
    Tiptap,
  },

  data: () => ({
    field: null,
    fieldRule: [],
    fieldLines: null,
    isPdfGenerating: false,
    isPdfGenerated: false,
    pdfData: null,
    imageUploaded: false,
    imageTables: [{ sampleDepth: "", text: "" }],
  }),

  created() {},

  methods: {
    addimageRow() {
      this.imageTables.push({
        sampleDepth: "",

        sampleNumber: "",

        zoomFactor: "",

        text: "",
      });
      console.log(this.imageTables);
    },

    uppercase() {
      //pass
    },
    deleteimageRow(index, imageTables) {
      var idx = this.imageTables.indexOf(imageTables);

      console.log(idx, index);

      this.imageTables.splice(idx, 2);
    },

    generateReport: function (isDraft = true) {
      this.$refs.form.validate();

      this.$emit("pdfGenerated", null);

      this.isPdfGenerating = true;

      this.isPdfGenerated = false;

      let jsonData = {
        richText: this.richText,
        imageTables: this.imageTables,

        key: this.$store.state.key,

        isDraft: isDraft,
      };

      console.log(jsonData);

      this.$axios
        .post(this.$backendUrl + "/table", jsonData)

        .then((response) => {
          console.log(response);

          this.pdfData = { data: response.data };

          this.$emit("pdfGenerated", this.pdfData);

          this.isPdfGenerated = true;

          this.isPdfGenerating = false;
        })

        .catch((error) => {
          console.log(error);

          this.isPdfGenerating = false;

          this.isPdfGenerated = false;

          if (error.response.status == 401) {
            this.$emit("unauthorized");
          }

          if (error.response.status == 404) {
            window.alert(error.response.data.message);
          }
        })

        .then(function () {});
    },

    savePdf: function () {
      var blob = new Blob([this.pdfData.data], { type: "application/pdf" });

      var link = document.createElement("a");

      link.href = window.URL.createObjectURL(blob);

      var fileName = "Output";

      link.download = fileName;

      link.click();
    },
  },
};
</script>

问题根源

  1. 表格内Tiptap绑定错误:表格中的Tiptap组件绑定的是全局text变量,而非当前行imageTable对象的text属性,导致富文本内容无法存入imageTables数组。
  2. richText未初始化:顶部Tiptap绑定的richText未在data中定义,会导致该值为undefined。
  3. 删除行方法逻辑错误:deleteimageRow中splice(idx, 2)会一次性删除2行,应该改为splice(idx, 1)只删除当前行。

修复后的代码

<template>
  <v-container>
    <v-form lazy-validation ref="form">
      <v-card class="pa-5">
        rich text editor
        <v-row> <Tiptap v-model="richText" /> </v-row>
      </v-card>

      <table class="responsive-table">
        <thead>
          <tr>
            <th scope="col">#</th>
            <th class="text-left">Sample Depth</th>
            <th class="text-left">Text</th>
          </tr>
        </thead>

        <tbody>
          <tr v-for="(imageTable, index) in imageTables" :key="index">
            <td scope="row" class="trashIconContainer">
              <i
                class="far fa-trash-alt"
                @click="deleteimageRow(index)"
              ></i>
            </td>

            <td data-label="sampleDepth">
              <input
                v-model="imageTable.sampleDepth"
                class="form-control"
                type="number"
              />
            </td>

            <td>
              <Tiptap v-model="imageTable.text" />
            </td>

            <td>
              <button @click="deleteimageRow(index)" class="is-danger">Delete</button>
            </td>
          </tr>
        </tbody>
      </table>

      <button type="button" class="btn btn-info" @click="addimageRow">
        <i class="fas fa-plus-circle"></i> Add
      </button>
      <v-row>
        <v-col>
          <v-btn
            width="100%"
            class="primary"
            :loading="isPdfGenerating"
            @click="generateReport(true)"
          >Generate draft report</v-btn>
        </v-col>
      </v-row>

      <v-row>
        <v-col>
          <v-btn
            width="100%"
            class="primary"
            :disabled="!isPdfGenerated"
            @click="savePdf"
          >Download generated report</v-btn>
        </v-col>

        <v-col>
          <v-btn
            width="100%"
            class="primary"
            :disabled="!isPdfGenerated"
            @click="generateReport(false)"
          >Make report official</v-btn>
        </v-col>
      </v-row>
    </v-form>
  </v-container>
</template>

<script>
import Tiptap from "/src/components/TiptapView.vue";
export default {
  name: "TableView",
  components: {
    Tiptap,
  },

  data: () => ({
    field: null,
    fieldRule: [],
    fieldLines: null,
    isPdfGenerating: false,
    isPdfGenerated: false,
    pdfData: null,
    imageUploaded: false,
    richText: "",
    imageTables: [{ sampleDepth: "", text: "" }],
  }),

  created() {},

  methods: {
    addimageRow() {
      this.imageTables.push({
        sampleDepth: "",
        sampleNumber: "",
        zoomFactor: "",
        text: "",
      });
      console.log(this.imageTables);
    },

    uppercase() {
      //pass
    },
    deleteimageRow(index) {
      this.imageTables.splice(index, 1);
    },

    generateReport: function (isDraft = true) {
      this.$refs.form.validate();

      this.$emit("pdfGenerated", null);

      this.isPdfGenerating = true;
      this.isPdfGenerated = false;

      let jsonData = {
        richText: this.richText,
        imageTables: this.imageTables,
        key: this.$store.state.key,
        isDraft: isDraft,
      };

      console.log(jsonData);

      this.$axios
        .post(this.$backendUrl + "/table", jsonData)
        .then((response) => {
          console.log(response);
          this.pdfData = { data: response.data };
          this.$emit("pdfGenerated", this.pdfData);
          this.isPdfGenerated = true;
          this.isPdfGenerating = false;
        })
        .catch((error) => {
          console.log(error);
          this.isPdfGenerating = false;
          this.isPdfGenerated = false;

          if (error.response.status == 401) {
            this.$emit("unauthorized");
          }

          if (error.response.status == 404) {
            window.alert(error.response.data.message);
          }
        });
    },

    savePdf: function () {
      var blob = new Blob([this.pdfData.data], { type: "application/pdf" });
      var link = document.createElement("a");
      link.href = window.URL.createObjectURL(blob);
      var fileName = "Output";
      link.download = fileName;
      link.click();
    },
  },
};
</script>

验证

修改后点击"Generate draft report",console.log(jsonData)会显示imageTables数组中每个对象的text属性已正确存储富文本内容,richText也能正常取值。

内容的提问来源于stack exchange,提问作者Mohamed Hassan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 11:15:37