Bootstrap

vue-springboot使用quill和quill-better-table实现富文本编辑以及上传回显

wangEditor编辑器链接:(使用更简单些~~)
https://blog.csdn.net/ziyu_nuannuan/article/details/122837550

1.安装

npm install quill ^2.0.0-dev.3 版本需要大于2.0版本 npm install
quill-better-table 处理表格

2.创建组件页面 quill-editor.vue

<template>
  <div>
    <el-upload
      ref="upload"
      class="avatar-uploader"
      :action="serverUrl"
      name="file"
      :headers="headers"
      :show-file-list="false"
      :on-success="uploadSuccess"
      :on-error="uploadError">
    </el-upload>
    <div class="editor"  style="height: calc(100% - 100px)"></div>
  </div>
</template>
<script>
  import Quill from 'quill'
  import 'quill/dist/quill.snow.css'

  import QuillBetterTable from 'quill-better-table'
  import 'quill-better-table/dist/quill-better-table.css'

  Quill.register({
    'modules/better-table': QuillBetterTable
  }, true)

  export default {
    name: 'editor',
    props: ['value'],
    data() {
      return {
        quill: null,
        content:'',
        options: {
          theme: 'snow',
          modules: {
            toolbar: {
              container: [
                ['bold', 'italic', 'underline', 'strike'],
                [{ 'list': 'ordered' }, { 'list': 'bullet' }],
                [{ 'script': 'super' }],
                [{ 'indent': '-1' }, { 'indent': '+1' }],
                [{ 'size': ['small', false, 'large', 'huge'] }],
                [{ 'header': [1, 2, 3, 4, 5, 6, false] }],
                [{ 'color': [] }, { 'background': [] }],
                [{ 'align': [] }],
                ['link', 'image'],
                [
                  { 'table': 'TD' },
                ],
              ],
              handlers: {
                'table': function () {//默认插入的表格行列数
                  this.quill.getModule('better-table').insertTable(3, 3)
                },
              },
            },
            table: false,
            'better-table': {//表格设置
              operationMenu: {
                items: {//鼠标右键菜单设置,如将某一项设置false则右键菜单不会显示 如insertColumnRight: false
                  insertColumnRight: {
                      text: '右边插入一列'
                    },
                    insertColumnLeft: {
                      text: '左边插入一列'
                    },
                    insertRowUp: {
                      text: '上边插入一行'
                    },
                    insertRowDown: {
                      text: '下边插入一行'
                    },
                    mergeCells: {
                      text: '合并单元格'
                    },
                    unmergeCells: {
                      text: '拆分单元格'
                    },
                    deleteColumn: {
                      text: '删除列'
                    },
                    deleteRow: {
                      text: '删除行'
                    },
                    deleteTable: {
                      text: '删除表格'
                    },
                },
                background: {
                  color: '#333'
                },
                color: {
                  colors: ['green', 'red', 'yellow', 'blue', 'white'],
                  text: '背景颜色:'
                }
              }
            },
            keyboard: {
              bindings: QuillBetterTable.keyboardBindings
            }
          },
          placeholder: '请输入内容 ...'
        },
        // 图片上传请求头
        headers: {
          token: localStorage.getItem('token')
        },
        // 图片上传调用的后端地址url 
        serverUrl:'/api/upload',
      }
    },
    mounted() {
      let dom = this.$el.querySelector('.editor')
      this.quill = new Quill(dom, this.options);
      this.quill.setContents(this.value)
      this.quill.on('text-change', () => {
        this.$emit('inputChange', this.quill.getContents())
      });
    },
    methods:{
      uploadSuccess(res, file) {
        let quill = this.$refs.myQuillEditor.quill
        // 上传成功
        if (res.code === 0) {
          // 获取光标所在位置
          let length = quill.getSelection.index;
          // 插入图片
          quill.insertEmbed(length, 'image', res.data);
          // 调整光标到最后
          quill.setSelection(length + 1);
        } else {
          this.$message.error("插入图片失败");
        }
      },
      uploadError() {
        this.$message.error("图片上传失败");
      },
    },
  }
</script>
<style scoped>
  /deep/ .ql-snow.ql-toolbar button, .ql-snow .ql-toolbar button {
    background: ivory;
  }
 //图标背景颜色
  /deep/ .ql-toolbar.ql-snow .ql-picker-label {
    border: 1px solid transparent;
    background: ivory;
  }
  //富文本框大小
  /deep/ .ql-editor{
    height: 500px;
    padding: 0 10px;
  }
 //图标间距设置
  /deep/ .ql-toolbar.ql-snow .ql-formats {
    margin-right: 15px;
    margin-bottom: 5px;
  }

 //表格边框颜色设置
  /deep/ table.quill-better-table td{
    border: 1px solid #fff;
  }
  //table宽度铺满
  /deep/ table.quill-better-table{
  width: 100% !important;
}

</style>

3.父组件引用

<Editor :ref="'myQuillEditor'+id"
                v-model="details"
                @inputChange="onEditorChange($event)">
</Editor>
                
<script>
import Editor from '@/components/quill-editor'
export default {
    name: "index",
    components: {
      Editor
    },
    data() {
      return {
        details:'',
        id:''
      };
    },
    created() {
      this.getInfo()
    },
    methods:{
      //将已有的数据回显到富文本框中
      getInfo(){
       //假设调用接口返回值为res.data
       //解析成json格式
        this.details = JSON.parse(res.data.details)
        //如果页面中调用多个富文本框,则ref动态
        let r = 'myQuillEditor'+id
         if (this.$refs[r]) {
           this.$refs[r].quill.setContents(this.details)
         }
        //如是单个页面调用方式如下
        //this.$refs.myQuillEditor.quill.setContents(this.details)
      },
      onEditorChange(val){
       //富文本框返回的数据格式为
       //{"ops":[{"insert":"SSSSSSSSSSSSSSSSSSSSSSSSS\n"}]}
        this.details = val
      },
      //保存需要处理数据
      save(){
      			//处理成string格式存储到数据库
      			let details = JSON.stringify(this.details)
      			...
      }
      
    }


  };
</script>

在这里插入图片描述右键出现表格操作

4.后端接口

@Slf4j
@RestController
public class FileUploadController {

		/**
		* yml中配置的存储路径
		*/
    @Value("${file.uploadFolder}")
    private String rootPath;

 /**
     * 文件上传
     * @param file
     * @param request
     * @return
     */
    @RequestMapping("/upload")
    @ResponseBody
    public JsonResult uploadFile(@RequestParam(value = "file", required = false)MultipartFile file, HttpServletRequest request) {
        String path = rootPath;
        String fileName = file.getOriginalFilename();
        // 获取文件名后缀
        String suffix = fileName.substring(file.getOriginalFilename().lastIndexOf("."));
        suffix = suffix.toLowerCase();
        fileName = UUID.randomUUID().toString() + suffix;
        File targetFile = new File(path, fileName);
        // 判断父级目录是否存在
        if (!targetFile.getParentFile().exists()) {
            targetFile.getParentFile().mkdirs();
        }
        // 保存本地
        try {
            file.transferTo(targetFile);
        } catch (IOException e) {
            e.printStackTrace();
            return JsonResult.error("上传失败!");
        }
        // 项目url
        String fileUrl = request.getContextPath() + "/api/showImg?imgUrl=";
        // 文件获取路径
        fileUrl += "/"+ fileName;
        return JsonResult.success(fileUrl);
    }

 /**
     * 文件回显
     * @param imgUrl
     * @param response
     * @throws IOException
     */
    @GetMapping("/showImg")
    public void ioReadImage(String imgUrl, HttpServletResponse response) throws IOException {
        ServletOutputStream out = null;
        FileInputStream ips = null;
        try {
            //获取图片存放路径
            String imgPath = rootPath + imgUrl;
            ips = new FileInputStream(new File(imgPath));
            String type = imgUrl.substring(imgUrl.indexOf(".") + 1);
            if ("png".equals(type)) {
                response.setContentType("image/png");
            }
            if ("jpeg".equals(type)) {
                response.setContentType("image/jpeg");
            }

            out = response.getOutputStream();
            //读取文件流
            int len = 0;
            byte[] buffer = new byte[1024 * 10];
            while ((len = ips.read(buffer)) != -1) {
                out.write(buffer, 0, len);
            }
            out.flush();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            out.close();
            ips.close();
        }
    }
   }
;