一、前言

  在上篇博客中,介绍了RuoYi中如何实现文件的上传与下载,那么这篇博客讲一下如何在RuoYi中使用富文本编辑器,这部分的内容是向B站程序员青戈学习的,当然我这里就会把学到的内容做一个总结,当然也会说一下在RuoYi中使用富文本编辑器会出现的一些问题。希望对大家有所帮助!

二、准备工作

1、安装wangdietor

npm install @wangeditor/editor --save

2、导入相关的包

b641e9bfbc0d4e4e98f456cebdc7d4fa.png

三、Vue代码

  首先我想说一下使用这个wangdietor富文本编辑器的主要思路:先对需要使用wangdietor的字段绑定标签id=''editor",然后使用我们自己写的setRichText()函数对编辑器进行初始化和对一些图片、视频上传的接口进行自定义;最后要将富文本编辑器的内容存在数据中。当然数据显示的时候可能会有一些标签,此时我们可以通过按钮,出现弹窗来规避这种情况。

核心代码如下:

    setRichText(){
      this.$nextTick( () => {
        this.editor=new E('#editor')//创建对象
        this.editor.highlight = hljs//代码高亮
        this.editor.config.uploadImgServer = process.env.VUE_APP_BASE_API+'/file/editor/upload'//图片上传的接口
        this.editor.config.uploadFileName = 'file'
        this.editor.config.uploadImgParams = {
          type:"img"
        }
        this.editor.config.uploadVideoServer = process.env.VUE_APP_BASE_API+"/file/editor/upload"//视频上传的接口
        this.editor.config.uploadVideoName = 'file'
        this.editor.config.uploadVideoParams = {
          type:"video"
        }
        this.editor.create()
      })
    }

四、Java代码

后端代码主要就是在图片和视频的上传了,下面是wangdietor规定的图片和视频的上传的格式

04122df7a4e3464dae283163aebec9e4.png

d5df27bfc7354a20926dfa74794a5214.png

核心代码如下:

@RestController
@Api(tags = "文件上传与下载模块")
@RequestMapping("/file")
public class FileController extends BaseController {
    @Value("${ip:localhost}")
    String ip;//通过配置文件拿到ip

    @Value("${server.port}")
    String port;

    private static final String ROOT_PATH = System.getProperty("user.dir")+ File.separator+"files";//文件根目录

    @PostMapping("/editor/upload")
    public Dict editorUpload(MultipartFile file,@RequestParam String type) throws IOException {
        System.out.println(6767);
        String originalFilename = file.getOriginalFilename();
        String mainName = FileUtil.mainName(originalFilename);
        String extName = FileUtil.extName(originalFilename);
        if(!FileUtil.exist(ROOT_PATH)){
            FileUtil.mkdir(ROOT_PATH);
        }
        if(FileUtil.exist(ROOT_PATH+File.separator+originalFilename)){
            originalFilename=System.currentTimeMillis()+"_"+mainName+"."+extName;
        }
        File saveFile = new File(ROOT_PATH + File.separator + originalFilename);
        file.transferTo(saveFile);
        String url ="http://"+ip+":"+port+"/file/download/"+originalFilename;
        if("img".equals(type)){//上传图片
            return Dict.create().set("errno",0).set("data", CollUtil.newArrayList(Dict.create().set("url",url)));
        }else if("video".equals(type)){
            return Dict.create().set("errno",0).set("data", Dict.create().set("url",url));
        }
        return Dict.create().set("errno",0);
    }
    @ApiOperation(value = "文件下载")
    @GetMapping("/download/{fileName}")
    public void download(@PathVariable String fileName, HttpServletResponse response) throws IOException {
//        response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName,"UTF-8"));//文件下载
//        response.addHeader("Content-Disposition", "inline;filename=" + URLEncoder.encode(fileName,"UTF-8"));//文件预览
        String filePath = ROOT_PATH+File.separator+fileName;//拿到文件的路径
        if(!FileUtil.exist(filePath)){//文件不存在,就不用管,因为没有东西可写
            return;
        }
        byte[] bytes = FileUtil.readBytes(filePath);//使用FileUtil从filePath中去读取文件
        ServletOutputStream outputStream = response.getOutputStream();
        outputStream.write(bytes);//把文件的字节数写出去
        outputStream.flush();//刷新一下
        outputStream.close();//一定要关闭文件流
    }
}

后端报错

36161883564d40b294ff46ed8744776d.png

如果出现这个错误,我们改一下对应的XSS中的内容即可

96af8f9a98a14da199f58646d2906f9f.png

效果图

8037f5046f5e4c18991e6aec5cee3a95.png

630e26f6c6ce4b90b7a63b872b4830c8.png

bd893b8b814346cdbc83cecae86c687f.png

e7f720e32f88481a955aa3734da9857a.png

全部代码

  以下的代码是包含了上述效果图中的所有代码,至于一些按钮、弹窗的设计、数据的请求与显示在该代码中都有,都是一些比较小的点,我就不详细介绍了,大家感兴趣的话就在评论区评论就好了,我看到了都会回复的。

<template>
  <div class="app-container">
    <el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
      <el-form-item label="学号" prop="stuNum">
        <el-input
          v-model="queryParams.stuNum"
          placeholder="请输入学号"
          clearable
          @keyup.enter.native="handleQuery"
        />
      </el-form-item>
      <el-form-item label="姓名" prop="stuName">
        <el-input
          v-model="queryParams.stuName"
          placeholder="请输入姓名"
          clearable
          @keyup.enter.native="handleQuery"
        />
      </el-form-item>
      <el-form-item>
        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
      </el-form-item>
    </el-form>

    <el-row :gutter="10" class="mb8">
      <el-col :span="1.5">
        <el-button
          type="primary"
          plain
          icon="el-icon-plus"
          size="mini"
          @click="handleAdd"
          v-hasPermi="['system:student:add']"
        >新增</el-button>
      </el-col>
      <el-col :span="1.5">
        <el-button
          type="success"
          plain
          icon="el-icon-edit"
          size="mini"
          :disabled="single"
          @click="handleUpdate"
          v-hasPermi="['system:student:edit']"
        >修改</el-button>
      </el-col>
      <el-col :span="1.5">
        <el-button
          type="danger"
          plain
          icon="el-icon-delete"
          size="mini"
          :disabled="multiple"
          @click="handleDelete"
          v-hasPermi="['system:student:remove']"
        >删除</el-button>
      </el-col>
      <el-col :span="1.5">
        <el-button
          type="warning"
          plain
          icon="el-icon-download"
          size="mini"
          @click="handleExport"
          v-hasPermi="['system:student:export']"
        >导出</el-button>
      </el-col>
      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
    </el-row>

    <el-table v-loading="loading" :data="studentList" @selection-change="handleSelectionChange" @close="closeDialog()">
      <el-table-column type="selection" width="55" align="center" />
      <el-table-column label="表编号" align="center" prop="tableId" />
      <el-table-column label="学号" align="center" prop="stuNum" />
      <el-table-column label="姓名" align="center" prop="stuName" />
      <el-table-column label="性别" align="center" prop="stuSex" />
      <el-table-column label="爱好" align="center" prop="stuHobby" >
        <template v-slot="scope">
          <el-button type="primary" size="mini" @click="handleStuHobbyContent(scope.row)">显示</el-button>
        </template>
      </el-table-column>
      <el-table-column label="家庭信息">
        <template slot-scope="scope">
          <el-button type="success" size="mini" @click="handleJump(scope.row)">按钮</el-button>
        </template>
      </el-table-column>
      <el-table-column label="头像上传">
        <template v-slot="scope">
          <el-upload
            action="http://localhost:8080/file/upload"
            :show-file-list="false"
            :on-success="(row,file,filelist) => handleTableFileUpload(scope.row,file,filelist)"
          >
            <el-button slot="trigger" size="mini" type="primary">选取文件</el-button>
          </el-upload>
        </template>
      </el-table-column>
      <el-table-column label="头像" align="center" prop="stuAvatar" >
        <template v-slot="scope">
          <el-image v-if="scope.row.stuAvatar" :src="scope.row.stuAvatar" style="with:50px;height: 50px"></el-image>
          <div>
            <el-button type="success" size="mini" @click="preview(scope.row.stuAvatar)">预览</el-button>
          </div>
        </template>
      </el-table-column>
      <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
        <template slot-scope="scope">
          <el-button
            size="mini"
            type="text"
            icon="el-icon-edit"
            @click="handleUpdate(scope.row)"
            v-hasPermi="['system:student:edit']"
          >修改</el-button>
          <el-button
            size="mini"
            type="text"
            icon="el-icon-delete"
            @click="handleDelete(scope.row)"
            v-hasPermi="['system:student:remove']"
          >删除</el-button>
        </template>
      </el-table-column>
    </el-table>

    <pagination
      v-show="total>0"
      :total="total"
      :page.sync="queryParams.pageNum"
      :limit.sync="queryParams.pageSize"
      @pagination="getList"
    />

    <!-- 添加或修改学生信息列表对话框 -->
    <el-dialog :title="title" :visible.sync="open" width="60%" append-to-body>
      <el-form ref="form" :model="form" :rules="rules" label-width="80px">
        <el-form-item label="学号" prop="stuNum">
          <el-input v-model="form.stuNum" placeholder="请输入学号" />
        </el-form-item>
        <el-form-item label="姓名" prop="stuName">
          <el-input v-model="form.stuName" placeholder="请输入姓名" />
        </el-form-item>
        <el-form-item label="性别" prop="stuSex">
          <el-input v-model="form.stuSex" placeholder="请输入性别" />
        </el-form-item>
        <el-form-item label="爱好" prop="stuHobby" class="w-e-text">
          <div id="editor"></div>
        </el-form-item>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button type="primary" @click="submitForm">确 定</el-button>
        <el-button @click="cancel">取 消</el-button>
      </div>
    </el-dialog>
    <div style="margin:10px 0">
      <el-upload
        class="upload-demo"
        drag
        action="http://localhost:8080/file/upload"
        :on-success="handleMultipleUpload"
        multiple>
        <i class="el-icon-upload"></i>
        <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
        <div class="el-upload__tip" slot="tip">只能上传jpg/png文件,且不超过500kb</div>
      </el-upload>
    </div>

    <el-dialog title="爱好" :visible.sync="open1" width="60%" append-to-body :close-on-click-modal="false">
      <el-card class="w-e-text" >
        <div v-html="stuHobbyContent"></div>
      </el-card>
      <div slot="footer" class="dialog-footer">
        <el-button type="primary" @click="open1 = false">确 定</el-button>
      </div>
    </el-dialog>

  </div>
</template>

<script>
import { listStudent, getStudent, delStudent, addStudent, updateStudent } from "@/api/system/student";
import E from "wangeditor"
import hljs from "highlight.js"
export default {
  name: "Student",
  data() {
    return {
      // 遮罩层
      loading: true,
      // 选中数组
      ids: [],
      // 非单个禁用
      single: true,
      // 非多个禁用
      multiple: true,
      // 显示搜索条件
      showSearch: true,
      // 总条数
      total: 0,
      // 学生信息列表表格数据
      studentList: [],
      // 弹出层标题
      title: "",
      // 是否显示弹出层
      open: false,
      // 查询参数
      queryParams: {
        pageNum: 1,
        pageSize: 10,
        stuNum: null,
        stuName: null,
      },
      // 表单参数
      form: {},
      // 表单校验
      rules: {
        stuNum: [
          { required: true, message: "学号不能为空", trigger: "blur" }
        ],
        stuName: [
          { required: true, message: "姓名不能为空", trigger: "blur" }
        ],
      },
      urls:'',
      editor:null,
      open1:false,
      stuHobbyContent:''
    };
  },
  created() {
    this.getList();
  },
  methods: {
    handleStuHobbyContent(row){
      this.stuHobbyContent=row.stuHobby;
      this.open1=true;
    },
    setRichText(){
      this.$nextTick( () => {
        this.editor=new E('#editor')
        this.editor.highlight = hljs
        this.editor.config.uploadImgServer = process.env.VUE_APP_BASE_API+'/file/editor/upload'
        this.editor.config.uploadFileName = 'file'
        this.editor.config.uploadImgParams = {
          type:"img"
        }
        this.editor.config.uploadVideoServer = process.env.VUE_APP_BASE_API+"/file/editor/upload"
        this.editor.config.uploadVideoName = 'file'
        this.editor.config.uploadVideoParams = {
          type:"video"
        }
        this.editor.create()//
      })
    },
    closeDialog() {
      this.editor.destroy()
      this.editor = null
    },
    handleMultipleUpload(response,file,fileList){
      this.urls=fileList.map(v => v.response?.msg)
      console.log(this.urls)//拿到我们的图片的路径
    },
    preview(url){
      window.open(url)
    },
    handleTableFileUpload(row,file,fileList){
      console.log(row,file)
      row.stuAvatar=file.response.msg//注意我们使用的是RuoYi框架中的AjaxResult结果集,返回的数据是在response下的msg中
      //触发更新
      //使用RuoYi中封装好的updateStudent()函数,将当前对象更新
      updateStudent(row).then(response => {
        if(response.code=='200'){
          this.$message.success("上传成功")
        }else{
          this.$message.success("上传失败")
        }
        this.form = response.data;
      });
    },
    /** 查询学生信息列表列表 */
    getList() {
      this.loading = true;
      listStudent(this.queryParams).then(response => {
        this.studentList = response.rows;
        this.total = response.total;
        this.loading = false;
      });
    },
    // 取消按钮
    cancel() {
      this.open = false;
      this.reset();
    },
    // 表单重置
    reset() {
      this.form = {
        tableId: null,
        stuNum: null,
        stuName: null,
        stuSex: null,
        stuHobby: null,
        stuAvatar: null,
      };
      this.resetForm("form");
    },
    /** 搜索按钮操作 */
    handleQuery() {
      this.queryParams.pageNum = 1;
      this.getList();
    },
    /** 重置按钮操作 */
    resetQuery() {
      this.resetForm("queryForm");
      this.handleQuery();
    },
    // 多选框选中数据
    handleSelectionChange(selection) {
      this.ids = selection.map(item => item.tableId)
      this.single = selection.length!==1
      this.multiple = !selection.length
    },
    /** 新增按钮操作 */
    handleAdd() {
      this.reset();
      this.open = true;
      this.title = "添加学生信息列表";
      this.setRichText();
    },
    /** 修改按钮操作 */
    handleUpdate(row) {
      this.reset();
      this.setRichText();
      setTimeout(()=>{//延时加载
        this.editor.txt.html(row.stuHobby)
      },0);
      const tableId = row.tableId || this.ids
      getStudent(tableId).then(response => {
        this.form = response.data;
        this.open = true;
        this.title = "修改学生信息列表";
      });
    },
    /** 页面跳转按钮操作 */
    handleJump(row){
      const tableId = row.tableId || this.ids
      getStudent(tableId).then(response => {
        this.$router.push("/student/family-data/index/"+tableId)
      });
    },
    /** 提交按钮 */
    submitForm() {
      this.$refs["form"].validate(valid => {
        if (valid) {
          let content = this.editor.txt.html();
          this.form.stuHobby=content;
          if (this.form.tableId != null) {

            updateStudent(this.form).then(response => {
              this.$modal.msgSuccess("修改成功");
              this.open = false;
              this.getList();
            });
          } else {
            addStudent(this.form).then(response => {
              this.$modal.msgSuccess("新增成功");
              this.open = false;
              this.getList();
            });
          }
        }
      });
    },
    /** 删除按钮操作 */
    handleDelete(row) {
      const tableIds = row.tableId || this.ids;
      this.$modal.confirm('是否确认删除学生信息列表编号为"' + tableIds + '"的数据项?').then(function() {
        return delStudent(tableIds);
      }).then(() => {
        this.getList();
        this.$modal.msgSuccess("删除成功");
      }).catch(() => {});
    },
    /** 导出按钮操作 */
    handleExport() {
      this.download('system/student/export', {
        ...this.queryParams
      }, `student_${new Date().getTime()}.xlsx`)
    }
  }
};
</script>

五、总结

  学无止尽,大家加油哦!希望我们都能在顶峰想见!

 

点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部