上一篇文章链接
富文本编辑器 vue-quill-editor入门(使用,显示,表格支持)

之前由于自己也只是粗浅的使用,以为没有太多人和我一样用到它
有一些地方,就没有展开讲
没想到大家用到的还挺多的,于是再展开讲解一下
先看一下效果

NewsAdd.vue 新增页面,可以添加附件
在这里插入图片描述

NewsDetail.vue 展示页面
在这里插入图片描述

NewsUpdate.vue 修改页面 获取富文本内容,然后展示,以供修改
在这里插入图片描述

项目代码放github不太合适, 就直接粘一下了
简单讲解一下逻辑

  • editor.vue 富文本编辑器组件
    1.activated组件的时候,会请求接口,加载富文本内容this.quill.root.innerHTML = this.content2,展示相应的内容。
    2.在mounted中使用this.quill.on(‘text-change’, () => {this.$emit(‘contentData’, this.quill.root.innerHTML, this.enclosureIDs)})来监听修改,将html格式的富文本,传递给父组件。
    3.使用el-upload实现附件上传,自定义了它的httpRequest

  • NewsAdd.vue 新增页面
    1.使用@contentData="change(arguments,userDefined)"获取子组件传来的内容
    2.请求接口,存储数据

  • NewsUpdate 修改页面
    1.使用@contentData=“change($event)” 获取子组件传来的,修改后的数据
    2.请求接口,修改数据

  • NewsDetail 展示页面
    1.无需引入editor组件,只需要从接口中取出富文本的html,放在

<el-col :span="20">
          <div class="ql-container ql-snow">
            <div class="ql-editor" v-html="form.content"></div>
          </div>
          <!-- <editor v-html="form.content" style="background-color:white"></editor> -->
        </el-col>

即可显示内容。

以下为代码,大家可以取出自己需要的部分

editor.vue

<template>
  <keep-alive>
    <div>
      <div class="editor"></div>
      <el-upload
        class="upload-demo"
        action="#"
        :http-request="httpRequest"
        :on-preview="handlePreview"
        :on-remove="handleRemove"
        :before-remove="beforeRemove"
        multiple
        :limit="50"
        :on-exceed="handleExceed"
        :ref="upload"
        :file-list="fileList">
        <el-button
          size="small"
          type="primary"
          @click="uploadBtn"
          class="my-upload"
          v-show="false"
          style="height:0px">点击上传</el-button>
        <div slot="tip" class="el-upload__tip" style="margin-top:0px">已选择附件:</div>
      </el-upload>
    </div>
  </keep-alive>
</template>

<script>
import api from '@/api/index'
import Quill from 'quill'
import 'quill/dist/quill.snow.css'
const titleConfig = {
  'ql-bold': '加粗',
  'ql-color': '颜色',
  'ql-font': '字体',
  'ql-code': '插入代码',
  'ql-italic': '斜体',
  'ql-link': '添加链接',
  'ql-background': '颜色',
  'ql-size': '字体大小',
  'ql-strike': '删除线',
  'ql-script': '上标/下标',
  'ql-underline': '下划线',
  'ql-blockquote': '引用',
  'ql-header': '标题',
  'ql-indent': '缩进',
  'ql-list': '列表',
  'ql-align': '文本对齐',
  'ql-direction': '文本方向',
  'ql-code-block': '代码块',
  'ql-formula': '公式',
  'ql-image': '图片',
  'ql-video': '视频',
  'ql-clean': '清除字体样式',
  'ql-upload': '文件',
  'ql-table': '插入表格',
  'ql-table-insert-row': '插入行',
  'ql-table-insert-column': '插入列',
  'ql-table-delete-row': '删除行',
  'ql-table-delete-column': '删除列'
}
export default {
  name: 'Editor',
  props: {
  },
  data () {
    return {
      upload: '',
      enclosureIDs: [],
      fileReader: null,
      fileList: [],
      content2: '',
      loadData: (date = this.date) => {
            return this.$http
            .get(api.NewsDetail, {
                params: { nid: this.$route.query.nid }
            })
            .then(res => {
                var resData = res.result.data[0]
                if (resData) {
                this.content2 = resData.content
                this.quill.root.innerHTML = this.content2
                } else {
                this.content2 = ''
                }
            })
        },
      quill: null,
      uploadAPI: api.Enclosure,
      options: {
        theme: 'snow',
        modules: {
          toolbar: {
            container: [
              ['bold', 'italic', 'underline', 'strike'],
              [{ header: 1 }, { header: 2 }],
              [{ list: 'ordered' }, { list: 'bullet' }],
              [{ indent: '-1' }, { indent: '+1' }],
              [{ color: [] }, { background: [] }],
              [{ font: [] }],
              [{ align: [] }],
              ['clean'],
              [
                { table: 'TD' },
                { 'table-insert-row': 'TIR' },
                { 'table-insert-column': 'TIC' },
                { 'table-delete-row': 'TDR' },
                { 'table-delete-column': 'TDC' }
              ],
              ['sourceEditor']
            ],
            handlers: {
              table: function (val) {
                this.quill.getModule('table').insertTable(2, 3)
              },
              'table-insert-row': function () {
                this.quill.getModule('table').insertRowBelow()
              },
              'table-insert-column': function () {
                this.quill.getModule('table').insertColumnRight()
              },
              'table-delete-row': function () {
                this.quill.getModule('table').deleteRow()
              },
              'table-delete-column': function () {
                this.quill.getModule('table').deleteColumn()
              },
              sourceEditor: function () { // 添加工具方法
                  document.getElementsByClassName('my-upload')[0].click()
              }

            }
          },
          table: true
        },
        placeholder: ''
      }
    }
  },
  methods: {

    httpRequest (options) {
          var file = options.file
          var filename = file.name
          const reader = new FileReader()
          reader.readAsDataURL(file)
          const that = this
          reader.onload = function () {
            var base64Str = this.result
            if (base64Str === 'data:') {
              // 为空txt时下载的时候会显示网络错误,这里进行处理转换为有一个空格的txt
               base64Str = 'data:text/plain;base64,IA=='
            }
            that.$http
            .post(api.Enclosure, {
                params: { base64Str: base64Str, name: filename, nid: that.$route.query.nid }
              })
            .then(res => {
              that.enclosureIDs.push({ 'en_id': res.enclosure_id, 'name': filename })
              that.$emit('contentData', that.quill.root.innerHTML, that.enclosureIDs)
            })
          }
    },
    uploadBtn () {
    },
    handleRemove (file, fileList) {
        var deleteId = 0
        if (file.id) {
          for (var i = 0; i < this.fileList.length; i++) {
            if (this.fileList[i].name === file.name) {
              this.fileList.splice(i, 1)
                deleteId = file.id
                break
            }
          }
        } else if (file.uid) {
          for (var j = 0; j < this.enclosureIDs.length; j++) {
            if (this.enclosureIDs[j].name === file.name) {
                deleteId = this.enclosureIDs[j]['en_id']
                this.enclosureIDs.splice(j, 1)
                break
            }
          }
        }
        // for (var i = this.fileList.length - 1; i > 0; i--) {
        //   console.log(this.fileList[i])
        //   }
        // this.$emit('contentData', this.quill.root.innerHTML, this.enclosureIDs)
        // // 请求删除附件接口
        this.$http
          .delete(api.Enclosure, {
            params: { eid: deleteId }
          })
          .then(res => {
            this.$emit('contentData', this.quill.root.innerHTML, this.enclosureIDs)
            this.$message({
            type: 'success',
            message: '删除成功!'
          })
          })
      },
    handlePreview (file) {
    },
    handleExceed (files, fileList) {
      this.$message.warning(`当前限制选择 3 个文件,本次选择了 ${files.length} 个文件,共选择了 ${files.length + fileList.length} 个文件`)
    },
    beforeRemove (file, fileList) {
      return this.$confirm(`确定移除 ${ file.name }?`)
    },
    addQuillTitle () {
      const oToolBar = document.querySelector('.ql-toolbar')
      const aButton = oToolBar.querySelectorAll('button')
      const aSelect = oToolBar.querySelectorAll('select')
      aButton.forEach(function (item) {
        if (item.className === 'ql-script') {
          item.value === 'sub' ? (item.title = '下标') : (item.title = '上标')
        } else if (item.className === 'ql-indent') {
          item.value === '+1' ? (item.title = '向右缩进') : (item.title = '向左缩进')
        } else {
          item.title = titleConfig[item.classList[0]]
        }
      })
      aSelect.forEach(function (item) {
        item.parentNode.title = titleConfig[item.classList[0]]
      })
    },
    getContentData () {
      return this.quill.getContents()
    }
  },
  mounted () {
    this.fileReader = new FileReader()
    const dom = this.$el.querySelector('.editor')
    this.quill = new Quill(dom, this.options)
    this.myEl = this.$el.querySelector('.ql-sourceEditor')
    // this.quill.setContents(this.value)
    this.quill.on('text-change', () => {
    //   console.log(this.quill.getContents())
    //   this.$emit('contentData', this.quill.getContents())
    //   console.log(this.quill.root.innerHTML)
      this.$emit('contentData', this.quill.root.innerHTML, this.enclosureIDs)
    })
    this.$el.querySelector(
      '.ql-table-insert-row'
    ).innerHTML = `<svg t="1591862376726" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6306" width="18" height="200"><path d="M500.8 604.779L267.307 371.392l-45.227 45.27 278.741 278.613L779.307 416.66l-45.248-45.248z" p-id="6307"></path></svg>`
    this.$el.querySelector(
      '.ql-table-insert-column'
    ).innerHTML = `<svg t="1591862238963" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6509" width="18" height="200"><path d="M593.450667 512.128L360.064 278.613333l45.290667-45.226666 278.613333 278.762666L405.333333 790.613333l-45.226666-45.269333z" p-id="6510"></path></svg>`
    this.$el.querySelector(
      '.ql-table-delete-row'
    ).innerHTML = `<svg t="1591862253524" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6632" width="18" height="200"><path d="M500.8 461.909333L267.306667 695.296l-45.226667-45.269333 278.741333-278.613334L779.306667 650.026667l-45.248 45.226666z" p-id="6633"></path></svg>`
    this.$el.querySelector(
      '.ql-table-delete-column'
    ).innerHTML = `<svg t="1591862261059" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6755" width="18" height="200"><path d="M641.28 278.613333l-45.226667-45.226666-278.634666 278.762666 278.613333 278.485334 45.248-45.269334-233.365333-233.237333z" p-id="6756"></path></svg>`
    this.addQuillTitle()
    this.$el.querySelector(
      '.ql-sourceEditor'
    ).innerHTML = `<svg t="1592278063482" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6358" width="18" height="200"><path d="M704 341.333333h64a64 64 0 0 1 64 64v362.666667a64 64 0 0 1-64 64H256a64 64 0 0 1-64-64V405.333333a64 64 0 0 1 64-64h64v64h-64v362.666667h512V405.333333h-64v-64zM517.653333 124.629333l150.826667 150.826667-45.226667 45.269333-74.026666-74.005333v304.768h-64V247.616l-73.173334 73.130667-45.248-45.248 150.826667-150.848z" p-id="6359"></path></svg>`
  },
  activated () {
    if (this.$route.query.nid) {
      this.loadData()
    } else {
      this.quill.root.innerHTML = ''
    }
  }
}
</script>
<style>
.el-upload-list__item {
      transition: none !important;
    }
</style>

NewsAdd.vue

<template>
  <keep-alive>
    <div :style="widthStyle">
      <el-row :gutter="24">
        <el-col :span="24">
          <el-form ref="form" :model="form" label-width="80px">
            <el-form-item label>
              <h1 style="font-size:30px">发布消息</h1>
            </el-form-item>
            <el-form-item label="类型">
              <el-select v-model="select_value" filterable placeholder="类型" @change="selectChanged">
                <el-option
                  v-for="item in SelectOptions"
                  :key="item.id"
                  :label="item.name"
                  :value="item.id"
                ></el-option>
              </el-select>
            </el-form-item>
            <el-form-item label="标题">
              <el-input placeholder="标题" v-model="title"></el-input>
            </el-form-item>
            <el-form-item label="内容">
              <editor @contentData="change(arguments,userDefined)" ref="son"></editor>
            </el-form-item>
            <el-form-item>
              <el-button type="primary" :disabled="isAble" @click="onSubmit">立即创建</el-button>
            </el-form-item>
            <el-form-item>
            </el-form-item>
          </el-form>
        </el-col>
      </el-row>
      <!-- <el-row :gutter="24">
        <el-col :span="18">
          <div class="my-upload">
            <el-upload
              multiple
              ref="upload"
              action=""
              accept=".pdf"
              :before-remove="handleBeforeRemove"
              :file-list="fileList"></el-upload>
          </div>
        </el-col>
      </el-row> -->
    </div>
  </keep-alive>
</template>
<script>
import api from '@/api/index'
import editor from '@/views/news/editor.vue'
export default {
  components: {
    editor
  },
  name: 'Editor',
  computed: {
      widthStyle: function () {
        // console.log(document.body.clientWidth)
        var result = ''
        var screenWidth = document.body.clientWidth
        if (screenWidth <= 1280) {
          result = '100%'
        } else if (screenWidth > 1280 && screenWidth <= 1440) {
          result = '90%'
        } else {
          result = '80%'
        }
        return {
          width: result
        }
      }
    },
  data: function () {
    return {
      screenWidth: document.body.clientWidth,
      userDefined: '',
      enclosureIDs: [],
        loadSelectData: (date = this.date) => {
        return this.$http
          .get(api.NewsType, {
            params: { 'select': true }
          })
          .then(res => {
            // this.SelectOptions = res.result.data
            // var result = []currentPage
            // res.result.data.forEach((item) => {
            //     result.push({ 'id': item.tid, 'name': item.news_type })
            // }
            // )
            // console.log(result)
            this.SelectOptions = res.result.data
          })
      },
      bg: {},
      plan: {},
      content: {},
      contentHtml: '',
      SelectOptions: [],
      select_value: '',
      value: '<h1>123</h1>',
      title: '发布消息',
      isAble: false,
      form: {
        name: '',
        region: '',
        date1: '',
        date2: '',
        delivery: false,
        type: [],
        resource: '',
        desc: '',
        SelectOptions: []
      },
      quill: null
    }
  },
  methods: {
    change (data) {
      this.contentHtml = data[0]
      this.enclosureIDs = data[1]
    },
    onSubmit () {
        this.isAble = true
        // console.log(this.$refs.childMethod.getContentData())
        this.$http
          .post(api.NewsDetail, {
            params: { type_id: this.select_value, title: this.title, content: this.contentHtml, enclosure_ids: this.enclosureIDs }
          })
          .then(res => {
            this.isAble = false
            this.$refs.son.fileList = []
            this.$refs.son.enclosureIDs = []
            this.SelectOptions = res.result.data
            this.$router.push({
                  path: '/news/'
              })
          })
    },
    selectChanged (value) {}
  },
  created: function () {},
  mounted () {
    this.loadSelectData()
  },
  activated () {
    this.title = ''
    this.content = {}
    this.contentHtml = ''
    this.loadSelectData()
  }
}
</script>
<style>
.ql-editor {
  min-height:400px; height:auto!important; height:400px;
}
</style>

NewsUpdate.vue

<template>
  <keep-alive>
    <el-row :gutter="24">
      <el-col :span="18">
        <el-form label-width="80px">
          <el-form-item label>
            <h1 style="font-size:30px">修改</h1>
          </el-form-item>
          <el-form-item label="标题">
            <el-input placeholder="标题" v-model="title"></el-input>
          </el-form-item>
          <el-form-item label="内容">
            <editor @contentData="change($event)" ref="son"></editor>
          </el-form-item>
          <el-form-item>
            <el-button type="primary" @click="onSubmit" >确认修改</el-button>
          </el-form-item>
        </el-form>
      </el-col>
    </el-row>
  </keep-alive>
</template>
<script>
import api from '@/api/index'
import editor from '@/views/news/editor.vue'
export default {
  components: {
    editor
  },
  name: 'Editor',
  data: function () {
    return {
        aaa: 'aaa',
        loadData: (date = this.date) => {
            return this.$http
            .get(api.NewsDetail, {
                params: { nid: this.$route.query.nid }
            })
            .then(res => {
                var resData = res.result.data[0]
                if (resData) {
                this.title = resData.title
                this.content = resData.content
                // this.$refs.son.fileList = [{ name: '空.txt', url: '#' }, { name: 'food2.jpeg', url: '#' }]
                // this.quill.root.innerHTML = resData.content
                } else {
                this.title = ''
                this.content = ''
                }
            })
        },
        loadEnclosureData: () => {
        return this.$http
          .get(api.Enclosure, {
            params: { nid: this.$route.query.nid }
          })
          .then(res => {
            this.$refs.son.fileList = res
          })
      },
        bg: {},
        plan: {},
        content: '',
        contentHtml: '',
        SelectOptions: [],
        select_value: '',
        value: '<h1>123</h1>',
        title: '',
        quill: null
    }
  },
  methods: {
    change (data) {
      this.contentHtml = data
    },
    onSubmit () {
    //   console.log(this.$refs.childMethod.getContentData())
      this.$http
        .patch(api.NewsDetail, {
          params: { nid: this.$route.query.nid, title: this.title, content: this.contentHtml }
        })
        .then(res => {
            this.$message({
            type: 'success',
            message: '修改成功!'
          })
          this.$router.push({
                path: '/news/detail/',
                query: {
                nid: this.$route.query.nid
                }
            })
        })
        }
    },
    created: function () {
        // this.loadData()
    },
    mounted: function () {
        // this.loadData()
        // this.loadEnclosureData()
        },
    activated () {
        this.loadData()
        this.loadEnclosureData()
    }
}
</script>
<style>
.ql-editor {
  min-height:400px; height:auto!important; height:400px;
}
</style>

NewsDetail.vue

<template>
  <keep-alive>
    <div>
      <el-row :gutter="24" type="flex" justify="center">
        <el-col :span="10" >
          <div>
            <h3 style="font-size:30px">{{ form.news_type }} - {{ form.title }}</h3>
          </div>
        </el-col>
      </el-row>
      <el-row :gutter="24" type="flex" justify="space-between">
        <el-col :span="6">
          <div>
            <h3>{{ form.department }} - {{ form.nickname }}</h3>
          </div>
        </el-col>
        <el-col :offset="8" :span="6">
          <div>
            <h3>{{ form.date }}</h3>
          </div>
        </el-col>
      </el-row>
      <el-row v-show="EnclosureList.length">
        <el-col :span="10">
          <h3>附件:</h3>
          <div v-for="en in EnclosureList" :key="en.id">
            <a :href="en.content" :download="en.name">{{ en.name }}</a>
            <!-- <a :href="en.content">{{ en.name }}</a> -->
            <a style="margin-left:20px" @click="preView(en.content)">预览</a>
          </div>
        </el-col>
      </el-row>
      <el-row :gutter="24">
        <el-col :span="20">
          <div class="ql-container ql-snow">
            <div class="ql-editor" v-html="form.content"></div>
          </div>
          <!-- <editor v-html="form.content" style="background-color:white"></editor> -->
        </el-col>
      </el-row>
      <el-row>
        <el-col :span="10">
          <el-button type="primary" plain style="margin-bottom:20px" @click="updateNews" v-show="showUpdateBtn">修改</el-button>
          <el-button type="danger" plain style="margin-bottom:20px" @click="deleteNews" v-show="showDeleteBtn">删除</el-button>
        </el-col>
      </el-row>
      <el-dialog
        title="提示"
        :visible.sync="dialogVisible"
        width="30%"
        :before-close="handleClose">
        <span>确定要删除该消息么 ?</span>
        <span slot="footer" class="dialog-footer">
          <el-button @click="dialogVisible = false">取 消</el-button>
          <el-button type="primary" @click="confirmDelete">确 定</el-button>
        </span>
      </el-dialog>
    </div>
  </keep-alive>

</template>
<script>
import api from '@/api/index'
import editor from '@/views/news/editor.vue'

export default {
  components: {
    editor
  },
  name: 'Detail',
  data: function () {
    return {
      dialogVisible: false,
      showDeleteBtn: false,
      showUpdateBtn: false,
      EnclosureList: [],
      content: {},
      loadData: (date = this.date) => {
        return this.$http
          .get(api.NewsDetail, {
            params: { nid: this.$route.query.nid, tid: this.$route.query.tid }
          })
          .then(res => {
            var resData = res.result.data[0]
            if (resData) {
              this.form.news_id = resData.id
              this.form.news_type = resData.news_type
              this.form.department = resData.department
              this.form.nickname = resData.nickname
              this.form.date = resData.date
              this.form.title = resData.title
              this.form.content = resData.content
              if (resData.deleteable) {
                  this.showDeleteBtn = true
              }
              if (resData.updateable) {
                  this.showUpdateBtn = true
              }
            } else {
              this.form.news_id = ''
              this.form.news_type = ''
              this.form.department = ''
              this.form.nickname = ''
              this.form.date = ''
              this.form.title = ''
              this.form.content = ''
            }
          })
      },
      loadEnclosureData: () => {
        return this.$http
          .get(api.Enclosure, {
            params: { nid: this.$route.query.nid }
          })
          .then(res => {
            this.EnclosureList = res
          })
      },
      form: {
        news_id: '',
        news_type: '',
        department: '',
        nickname: '',
        date: '',
        title: '',
        content: {}
      }
    }
  },
  methods: {
    preView (content) {
      var string = content
      var iframe = "<iframe width='100%' height='100%' src='" + string + "'></iframe>"
      var x = window.open()
      x.document.open()
      x.document.write(iframe)
      x.document.close()
    },
    confirmDelete () {
        this.$http
          .delete(api.NewsDetail, {
            params: { nid: this.$route.query.nid }
          })
          .then(res => {
            this.$message({
            type: 'success',
            message: '删除成功!'
          })
            this.$router.push({
            path: '/news/',
            query: {}
            })
          })
      this.dialogVisible = false
    },
    handleClose (done) {
          done()
      },
    onSubmit () {},
    updateNews () {
          this.$router.push({
          path: '/news/update/',
          query: { nid: this.$route.query.nid }
          })
      },
    deleteNews () {
      this.dialogVisible = true
    }
    },
    created: function () {},
    mounted: function () {
        this.loadData()
        this.loadEnclosureData()
    },
    activated () {
        this.showUpdateBtn = false
        this.showDeleteBtn = false
        this.loadData()
        this.loadEnclosureData()
    }
}
</script>
<style>
    .ql-editor {
    min-height:400px; height:auto!important; height:400px;
    }
 .el-row {
    margin-bottom: 20px;
  }
  .el-col {
    border-radius: 4px;
  }
  .bg-purple-dark {
    background: #99a9bf;
  }
  .bg-purple {
    background: #d3dce6;
  }
  .bg-purple-light {
    background: #e5e9f2;
  }
  .grid-content {
    border-radius: 4px;
    min-height: 36px;
  }
  .row-bg {
    padding: 10px 0;
    background-color: #f9fafc;
  }
</style>

Logo

瓜分20万奖金 获得内推名额 丰厚实物奖励 易参与易上手

更多推荐