Vue富文本插件vue-quill-editor

282 阅读2分钟

参考: segmentfault.com/a/119000002…

 

 

自定义HTML模块[tableEditor.js]


// 【测评报告模板】 单月报告表格

export const monthReportForm = `<div class="quill-better-table-wrapper" id="oneMonth">

  <table class="quill-better-table" style="width: 930px;">

    <colgroup>

      <col width="90"></col>

        <col width="180"></col>

          <col width="260"></col>

            <col width="200"></col>

              <col width="200"></col>

    </colgroup>

    <tbody>

    <tr data-row="row-awp1">

      <td data-row="row-awp1" rowSpan="1" colSpan="1" data-cell-bg="#E7E6E6"

        style="background-color: rgb(231, 230, 230);border: 1px solid rgb(231, 230, 230);"

      ><p class="qlbt-cell-line" data-row="row-awp1" data-cell="cell-6vfu" data-rowspan="1" data-colspan="1" style="

    color: #000;

" data-cell-bg="#E7E6E6"

      ><strong>序号</strong></p></td>

      <td data-row="row-awp1" rowSpan="1" colSpan="1" data-cell-bg="#E7E6E6"

        style="background-color: rgb(231, 230, 230);border: 1px solid rgb(231, 230, 230);"

      ><p class="qlbt-cell-line" data-row="row-awp1" data-cell="cell-67kz" data-rowspan="1" data-colspan="1" style="

    color: #000;

" data-cell-bg="#E7E6E6"

      ><strong>指标名称</strong></p></td>

      <td data-row="row-awp1" rowSpan="1" colSpan="1" data-cell-bg="#E7E6E6"

        style="background-color: rgb(231, 230, 230);border: 1px solid rgb(231, 230, 230);"

      ><p class="qlbt-cell-line" data-row="row-awp1" data-cell="cell-c4kr" data-rowspan="1" data-colspan="1" style="

    color: #000;

" data-cell-bg="#E7E6E6"

      ><strong>问题情况</strong></p></td>

      <td data-row="row-awp1" rowSpan="1" colSpan="1" data-cell-bg="#E7E6E6"

        style="background-color: rgb(231, 230, 230);border: 1px solid rgb(231, 230, 230);"

      ><p class="qlbt-cell-line" data-row="row-awp1" data-cell="cell-nmhr" data-rowspan="1" data-colspan="1" style="

    color: #000;

" data-cell-bg="#E7E6E6"

      ><strong>业务处室</strong></p></td>

      <td data-row="row-awp1" rowSpan="1" colSpan="1" data-cell-bg="#E7E6E6"

        style="background-color: rgb(231, 230, 230);border: 1px solid rgb(231, 230, 230);"

      ><p class="qlbt-cell-line" data-row="row-awp1" data-cell="cell-ndwn" data-rowspan="1" data-colspan="1" style="

    color: #000;

" data-cell-bg="#E7E6E6"

      ><strong>整改期限</strong></p></td>

    </tr>

    <tr data-row="row-jqho">

      <td data-row="row-jqho" rowSpan="1" colSpan="1" style="border: 1px solid rgb(231, 230, 230);"><p class="qlbt-cell-line" data-row="row-jqho"

        data-cell="cell-i4pg" data-rowspan="1" data-colspan="1"

      ><em style="background-color: rgb(255, 255, 255); color: rgb(199, 199, 204);">根据问题指标数自增</em></p></td>

      <td data-row="row-jqho" rowSpan="1" colSpan="1" style="border: 1px solid rgb(231, 230, 230);"><p class="qlbt-cell-line" data-row="row-jqho"

        data-cell="cell-f8nj" data-rowspan="1" data-colspan="1"

      ><em style="background-color: rgb(255, 255, 255); color: rgb(199, 199, 204);">有问题的指标名称</em></p></td>

      <td data-row="row-jqho" rowSpan="1" colSpan="1" style="border: 1px solid rgb(231, 230, 230);"><p class="qlbt-cell-line" data-row="row-jqho"

        data-cell="cell-3zbu" data-rowspan="1" data-colspan="1"

      ><em style="background-color: rgb(255, 255, 255); color: rgb(199, 199, 204);">根据不同情况显示问题内容</em></p></td>

      <td data-row="row-jqho" rowSpan="1" colSpan="1" style="border: 1px solid rgb(231, 230, 230);"><p class="qlbt-cell-line" data-row="row-jqho"

        data-cell="cell-kfpu" data-rowspan="1" data-colspan="1"

      ><em style="background-color: rgb(255, 255, 255); color: rgb(199, 199, 204);">读取指标体系内的业务处室</em></p></td>

      <td data-row="row-jqho" rowSpan="1" colSpan="1" style="border: 1px solid rgb(231, 230, 230);"><p class="qlbt-cell-line" data-row="row-jqho"

        data-cell="cell-nejd" data-rowspan="1" data-colspan="1"

      ><em style="background-color: rgb(255, 255, 255); color: rgb(199, 199, 204);">根据选择的月报期限延后一个月</em></p></td>

    </tr>

    </tbody>

  </table>

</div>`

 

// 【测评报告模板】 多月报告表格

export const monthReportForms = `<div class="quill-better-table-wrapper" id="moreMonth"><table class="quill-better-table" style="width: 1330px;"><colgroup><col width="90"></col><col width="180"></col><col width="200"></col><col width="260"></col><col width="200"></col><col width="200"></col><col width="200"></col></colgroup><tbody><tr data-row="row-mw36"><td data-row="row-mw36" rowspan="1" colspan="1" data-cell-bg="#E7E6E6" style="background-color: rgb(231, 230, 230);border: 1px solid rgb(231, 230, 230);"><p class="qlbt-cell-line" data-row="row-mw36" data-cell="cell-mvab" data-rowspan="1" data-colspan="1" style="

    color: #000;

" data-cell-bg="#E7E6E6"><strong>序号</strong></p></td><td data-row="row-mw36" rowspan="1" colspan="1" data-cell-bg="#E7E6E6" style="background-color: rgb(231, 230, 230);border: 1px solid rgb(231, 230, 230);"><p class="qlbt-cell-line" data-row="row-mw36" data-cell="cell-qkd7" data-rowspan="1" data-colspan="1" data-cell-bg="#E7E6E6"><strong style="

    color: #000;

">指标名称</strong></p></td><td data-row="row-mw36" rowspan="1" colspan="1" data-cell-bg="#E7E6E6" style="background-color: rgb(231, 230, 230);border: 1px solid rgb(231, 230, 230);"><p class="qlbt-cell-line" data-row="row-mw36" data-cell="cell-p8kr" data-rowspan="1" data-colspan="1" style="

    color: #000;

" data-cell-bg="#E7E6E6"><strong>月份</strong></p></td><td data-row="row-mw36" rowspan="1" colspan="1" data-cell-bg="#E7E6E6" style="background-color: rgb(231, 230, 230);border: 1px solid rgb(231, 230, 230);"><p class="qlbt-cell-line" data-row="row-mw36" data-cell="cell-ejla" data-rowspan="1" data-colspan="1" style="

    color: #000;

" data-cell-bg="#E7E6E6"><strong>问题情况</strong></p></td><td data-row="row-mw36" rowspan="1" colspan="1" data-cell-bg="#E7E6E6" style="background-color: rgb(231, 230, 230);border: 1px solid rgb(231, 230, 230);"><p class="qlbt-cell-line" data-row="row-mw36" data-cell="cell-s854" data-rowspan="1" data-colspan="1" style="

    color: #000;

" data-cell-bg="#E7E6E6"><strong>业务处室</strong></p></td><td data-row="row-mw36" rowspan="1" colspan="1" data-cell-bg="#E7E6E6" style="background-color: rgb(231, 230, 230);border: 1px solid rgb(231, 230, 230);"><p class="qlbt-cell-line" data-row="row-mw36" data-cell="cell-eac3" data-rowspan="1" data-colspan="1" data-cell-bg="#E7E6E6"><strong style="

    color: #000;

">指标现状(x月)</strong></p></td><td data-row="row-mw36" rowspan="1" colspan="1" data-cell-bg="#E7E6E6" style="background-color: rgb(231, 230, 230);border: 1px solid rgb(231, 230, 230);"><p class="qlbt-cell-line" data-row="row-mw36" data-cell="cell-eac3" data-rowspan="1" data-colspan="1" data-cell-bg="#E7E6E6"><strong style="

    color: #000;

">整改期限</strong></p></td></tr><tr data-row="row-jrvh"><td data-row="row-jrvh" rowspan="1" colspan="1" style="border: 1px solid rgb(231, 230, 230);"><p class="qlbt-cell-line" data-row="row-jrvh" data-cell="cell-zyqt" data-rowspan="1" data-colspan="1"><em style="background-color: rgb(255, 255, 255); color: rgb(199, 199, 204);">根据问题指标数自增(按指标数非问题数)</em></p></td><td data-row="row-jrvh" rowspan="1" colspan="1" style="border: 1px solid rgb(231, 230, 230);"><p class="qlbt-cell-line" data-row="row-jrvh" data-cell="cell-zlb1" data-rowspan="1" data-colspan="1"><em style="background-color: rgb(255, 255, 255); color: rgb(199, 199, 204);">有问题的指标名称(若同一指标多月出现问题,则合并单元格)</em></p></td><td data-row="row-jrvh" rowspan="1" colspan="1" style="border: 1px solid rgb(231, 230, 230);"><p class="qlbt-cell-line" data-row="row-jrvh" data-cell="cell-9ba5" data-rowspan="1" data-colspan="1"><em style="background-color: rgb(255, 255, 255); color: rgb(199, 199, 204);">当前指标有问题的月份</em></p></td><td data-row="row-jrvh" rowspan="1" colspan="1" style="border: 1px solid rgb(231, 230, 230);"><p class="qlbt-cell-line" data-row="row-jrvh" data-cell="cell-jxpj" data-rowspan="1" data-colspan="1"><em style="background-color: rgb(255, 255, 255); color: rgb(199, 199, 204);">根据不同情况显示问题内容</em></p></td><td data-row="row-jrvh" rowspan="1" colspan="1" style="border: 1px solid rgb(231, 230, 230);"><p class="qlbt-cell-line" data-row="row-jrvh" data-cell="cell-lu35" data-rowspan="1" data-colspan="1"><em style="background-color: rgb(255, 255, 255); color: rgb(199, 199, 204);">读取指标体系内的业务处室</em></p></td><td data-row="row-jrvh" rowspan="1" colspan="1" style="border: 1px solid rgb(231, 230, 230);"><p class="qlbt-cell-line" data-row="row-jrvh" data-cell="cell-02yp" data-rowspan="1" data-colspan="1"><em style="background-color: rgb(255, 255, 255); color: rgb(199, 199, 204);">当前指标出现问题的月份</em></p></td><td data-row="row-jrvh" rowspan="1" colspan="1" style="border: 1px solid rgb(231, 230, 230);"><p class="qlbt-cell-line" data-row="row-jrvh" data-cell="cell-jyvd" data-rowspan="1" data-colspan="1"><em style="background-color: rgb(255, 255, 255); color: rgb(199, 199, 204);">当前有问题的指标月份期限延后一个月</em></p></td></tr></tbody></table></div>`

 

 

引入--注册--添加模板表格


 

import { monthReportForm, monthReportForms } from '@/components/tableEditor.js'

 

 

 

// 插入自定义HTML块

const BlockEmbed = Quill.import('blots/block/embed')

// 定义新的blot类型

class AppPanelEmbed extends BlockEmbed {

  static create(value) {

    const node = super.create(value)

    node.setAttribute('contenteditable', 'false')

    node.setAttribute('width', '100%')

    //   设置自定义html

    node.innerHTML = this.transformValue(value)

    return node

  }

  static transformValue(value) {

    let handleArr = value.split('\n')

    handleArr = handleArr.map(e => e.replace(/^[\s]+/, '')

      .replace(/[\s]+$/, ''))

    return handleArr.join('')

  }

  // 返回节点自身的value值 用于撤销操作

  static value(node) {

    return node.innerHTML

  }

}

// blotName

AppPanelEmbed.blotName = 'AppPanelEmbed'

// class名将用于匹配blot名称

AppPanelEmbed.className = 'embed-innerApp'

// 标签类型自定义

AppPanelEmbed.tagName = 'div'

 

 

const link = Quill.import('formats/link')

class Link extends link {

  static create(value) {

    const node = super.create(value) // 调用父类的方法生成 node 节点

    value = this.sanitize(value)// 使用 sanitize 取出 value

    // 判断是否拥有协议前缀无则添加默认前缀

    if (!(value.indexOf('http://') === 0 || value.indexOf('https://') === 0)) {

      value = `http://${value}`

    }

    // 设置 href 属性

    node.setAttribute('href', value)

    return node

  }

}

 

Quill.register(AppPanelEmbed, true)

 

 


 

<!-- 插入元素 -->

<div class="addElemnt" v-if="selectModel">

  <p class="bg-gray">插入模板元素</p>

  <p

    class="tree_item"

    v-for="(item, index) in arr"

    :key="index"

    v-html="item"

    @click="addCondition(item)"

  ></p>

</div>

 

 

 


mounted() {

  let dom = this.$el.querySelector('.editor')

  this.quill = new Quill(dom, this.options)

  // 编辑时,渲染文本

  this.quillRender(this.value)

  this.quill.on('text-change', () => {

        this.$emit('contentData', { text: this.quill.root.innerText, rHtml:        this.quill.root.innerHTML })

  })

  this.quill.on('editor-change', () => {

    let toolDom = document.querySelectorAll('.ql-tooltip')

    if (toolDom.length !== 0) {

      toolDom.forEach(element => {

        if (element.style.left.indexOf('-') === 0 || element.style.left === '0px') element.style.left = '0px'

      })

    }

  })

},

methods: {

  // 插入元素

  addCondition(data) {

    let _this = this

    let num = _this.quill.selection.savedRange.index

    // _this.quill.insertText(num, data, { color: 'rgba(0, 0, 0)' })

 

    let table = {}

    table.oneMonth = document.getElementById('oneMonth')

    table.moreMonth = document.getElementById('moreMonth')

    if (data === '单月报告表格') {

      if (table.moreMonth) {

        table.moreMonth.parentNode.nextSibling.remove()

        table.moreMonth.parentNode.remove()

        table.moreMonth.remove()

      } else if (table.oneMonth) {

        return

      }

      _this.quill.insertEmbed(num || 0, 'AppPanelEmbed', monthReportForm)

    } else if (data === '多月报告表格') {

      if (table.oneMonth) {

        table.oneMonth.parentNode.nextSibling.remove()

        table.oneMonth.parentNode.remove()

        table.oneMonth.remove()

      } else if (table.moreMonth) {

        return

      }

      _this.quill.insertEmbed(num || 0, 'AppPanelEmbed', monthReportForms)

    } else {

      data = `{${data}}`

      _this.quill.insertText(num, data)

    }

 

    // 加空格,防止后面文字颜色一致

    _this.quill.insertText(num + data.length, ' ', { color: 'rgba(0, 0, 0, 0.65)' })

    _this.quill.selection.savedRange.index = num + data.length + 1

    // 别的页面回显的时候 直接字符串查找 比如'<span style="color: rgb(0, 0, 0);">{处理结果}</span>' 然后替换成对应的数据

  },

 

  quillRender(value) {

    if (document.querySelector('.qlbt-col-tool')) document.querySelector('.qlbt-col-tool')

      .remove()

    this.quill.root.innerHTML = value

  },

 

  // // 防止xss攻击【目前做了AES加密就不需要了】

  // handleWithXss(content) {

  //   const options = {

  //     whiteList: {

  //       ...

  //         div: ['class', 'style', 'data-id','contenteditable'],

  //       ...

  //     },

  //     css: {

  //       whiteList: {

  //         color: true,

  //         'background-color': true,

  //         'max-width': true,

  //       },

  //     },

  //     stripIgnoreTag: true,

  //     onTagAttr: (tag, name, value, isWhiteAttr) => {

  //       // 针对div的contenteditable 处理

  //       if (isWhiteAttr && tag === 'div' && name === 'contenteditable') {

  //         return 'contenteditable="false"'

  //       },

  //     },

  //   } // 自定义规则

  //   const myxss = new xss.FilterXSS(options)

  //   return myxss.process(content)

  // },

 

模板表格--渲染表格数据


async created() {

  if (this.isEditting) {

    const res = await api.prebiewItem(

      this.$route.params.id,

      this.$route.params.rid,

    )

 

    // 解密

    let aseKey = '2022tianxrda0620'

    let decrypt = CryptoJS.AES.decrypt(

      res.reportContent,

      CryptoJS.enc.Utf8.parse(aseKey),

      {

        mode: CryptoJS.mode.ECB,

        padding: CryptoJS.pad.Pkcs7,

      },

    ).toString(CryptoJS.enc.Utf8)

    res.reportContent = decrypt

 

    if (res.saveStatus !== 1) {

      if (res.monthlyVos.length > 0) {

        this.monthlyVosData = res.monthlyVos

 

        if (res.reportContent.indexOf('oneMonth') > -1) {

          let loveStr = ''

          let index = 0

          this.monthlyVosData.forEach((item) => {

            let listStr = ''

            let trRow = ''

            let tdRow = this.getEngNum()

            index += 1

            for (let k in item) {

              trRow = tdRow

              let onetd = `<td data-row="row-${tdRow}" rowSpan="1" colSpan="1" style="border: 1px solid rgb(231, 230, 230);"><p class="qlbt-cell-line" data-row="row-${tdRow}" data-cell="cell-${this.getEngNum()}" data-rowspan="1" data-colspan="1"><em style="background-color: rgb(255, 255, 255); color: rgb(0, 0, 0);">${

                index

              }</em></p></td>`

              let twotd = `<td data-row="row-${tdRow}" rowSpan="1" colSpan="1" style="border: 1px solid rgb(231, 230, 230);"><p class="qlbt-cell-line" data-row="row-${tdRow}" data-cell="cell-${this.getEngNum()}" data-rowspan="1" data-colspan="1"><em style="background-color: rgb(255, 255, 255); color: rgb(0, 0, 0);">${

                item.indicatorName

              }</em></p></td>`

              let threetd = `<td data-row="row-${tdRow}" rowSpan="1" colSpan="1" style="border: 1px solid rgb(231, 230, 230);"><p class="qlbt-cell-line" data-row="row-${tdRow}" data-cell="cell-${this.getEngNum()}" data-rowspan="1" data-colspan="1"><em style="background-color: rgb(255, 255, 255); color: rgb(0, 0, 0);">${

                item.problemSituation

              }</em></p></td>`

              let fourtd = `<td data-row="row-${tdRow}" rowSpan="1" colSpan="1" style="border: 1px solid rgb(231, 230, 230);"><p class="qlbt-cell-line" data-row="row-${tdRow}" data-cell="cell-${this.getEngNum()}" data-rowspan="1" data-colspan="1"><em style="background-color: rgb(255, 255, 255); color: rgb(0, 0, 0);">${

                item.businessDepartment

              }</em></p></td>`

              let fivetd = `<td data-row="row-${tdRow}" rowSpan="1" colSpan="1" style="border: 1px solid rgb(231, 230, 230);"><p class="qlbt-cell-line" data-row="row-${tdRow}" data-cell="cell-${this.getEngNum()}" data-rowspan="1" data-colspan="1"><em style="background-color: rgb(255, 255, 255); color: rgb(0, 0, 0);">${

                item.rectificationDate

              }</em></p></td>`

              listStr = onetd + twotd + threetd + fourtd + fivetd

              console.info(k)

            }

            loveStr += `<tr data-row="row-${trRow}">${listStr}</tr>`

          })

          this.monthTrStr = loveStr

        }

        if (res.reportContent.indexOf('moreMonth') > -1) {

          if (this.monthlyVosData[0].indicatorStatusMonth) res.reportContent = res.reportContent.replace(/指标现状(x月)/g, `指标现状(${this.monthlyVosData[0].indicatorStatusMonth}月)`)

 

          let loveStr = ''

          let index = 0

          this.monthlyVosData.forEach((item) => {

            let listStr = ''

            let trRow = ''

            let tdRow = this.getEngNum()

            index += 1

            for (let k in item) {

              trRow = tdRow

              let onetd = `<td data-row="row-${tdRow}" rowSpan="1" colSpan="1" style="border: 1px solid rgb(231, 230, 230);"><p class="qlbt-cell-line" data-row="row-${tdRow}" data-cell="cell-${this.getEngNum()}" data-rowspan="1" data-colspan="1"><em style="background-color: rgb(255, 255, 255); color: rgb(0, 0, 0);">${

                index

              }</em></p></td>`

              let twotd = `<td data-row="row-${tdRow}" rowSpan="1" colSpan="1" style="border: 1px solid rgb(231, 230, 230);"><p class="qlbt-cell-line" data-row="row-${tdRow}" data-cell="cell-${this.getEngNum()}" data-rowspan="1" data-colspan="1"><em style="background-color: rgb(255, 255, 255); color: rgb(0, 0, 0);">${

                item.indicatorName

              }</em></p></td>`

              let threetd = `<td data-row="row-${tdRow}" rowSpan="1" colSpan="1" style="border: 1px solid rgb(231, 230, 230);"><p class="qlbt-cell-line" data-row="row-${tdRow}" data-cell="cell-${this.getEngNum()}" data-rowspan="1" data-colspan="1"><em style="background-color: rgb(255, 255, 255); color: rgb(0, 0, 0);">${

                item.month

              }</em></p></td>`

              let fourtd = `<td data-row="row-${tdRow}" rowSpan="1" colSpan="1" style="border: 1px solid rgb(231, 230, 230);"><p class="qlbt-cell-line" data-row="row-${tdRow}" data-cell="cell-${this.getEngNum()}" data-rowspan="1" data-colspan="1"><em style="background-color: rgb(255, 255, 255); color: rgb(0, 0, 0);">${

                item.problemSituation

              }</em></p></td>`

              let fivetd = `<td data-row="row-${tdRow}" rowSpan="1" colSpan="1" style="border: 1px solid rgb(231, 230, 230);"><p class="qlbt-cell-line" data-row="row-${tdRow}" data-cell="cell-${this.getEngNum()}" data-rowspan="1" data-colspan="1"><em style="background-color: rgb(255, 255, 255); color: rgb(0, 0, 0);">${

                item.businessDepartment

              }</em></p></td>`

              let sixtd = `<td data-row="row-${tdRow}" rowSpan="1" colSpan="1" style="border: 1px solid rgb(231, 230, 230);"><p class="qlbt-cell-line" data-row="row-${tdRow}" data-cell="cell-${this.getEngNum()}" data-rowspan="1" data-colspan="1"><em style="background-color: rgb(255, 255, 255); color: rgb(0, 0, 0);">${

                item.indicatorStatus

              }</em></p></td>`

              let seventd = `<td data-row="row-${tdRow}" rowSpan="1" colSpan="1" style="border: 1px solid rgb(231, 230, 230);"><p class="qlbt-cell-line" data-row="row-${tdRow}" data-cell="cell-${this.getEngNum()}" data-rowspan="1" data-colspan="1"><em style="background-color: rgb(255, 255, 255); color: rgb(0, 0, 0);">${

                item.rectificationDate

              }</em></p></td>`

              listStr

                  = onetd + twotd + threetd + fourtd + fivetd + sixtd + seventd

              console.info(k)

            }

            loveStr += `<tr data-row="row-${trRow}">${listStr}</tr>`

          })

          this.monthTrStr = loveStr

        }

      }

 

      if (this.monthTrStr) {

        const div = document.createElement('div')

        div.innerHTML = res.reportContent

        let app = div.querySelector('.embed-innerApp')

        let trDatas = []

        if (res.reportContent.indexOf('oneMonth') > -1) {

          trDatas = div.querySelector('#oneMonth')

          div.appendChild(trDatas)

          div.removeChild(app)

        }

        if (res.reportContent.indexOf('moreMonth') > -1) {

          trDatas = div.querySelector('#moreMonth')

          div.appendChild(trDatas)

          div.removeChild(app)

        }

        const dom = trDatas.querySelectorAll('tr')

        const parentElement = dom[0].parentNode

        dom.forEach((ele, index) => {

          if (index > 0) parentElement.removeChild(ele)

        })

        const a = document.createElement('tbody')

        a.innerHTML = this.monthTrStr

        const trList = a.querySelectorAll('tr')

        trList.forEach((item) => {

          parentElement.appendChild(item)

        })

        res.reportContent = div.innerHTML

      }

    }

 

    this.$nextTick(() => {

      this.model = {

        ...res,

        checkedItems: res.regionOrgVos?.map((i) => ({

          ...i,

          key: i.code,

          title: i.name,

        })),

        fileList: res.fileList

          ? res.fileList.map((i) => ({

            ...i,

            uid: i.id,

            name: i.fileName,

            status: 'done',

            response: { data: i.id },

          }))

          : [],

      }

    })

  }

}