springboot+vue小白升级之路17-集成wangeditor富文本编辑器实现发贴、发博客功能

661 篇文章 4 订阅
112 篇文章 0 订阅

我们还是接着之前的内容,把新增的代码贴出来。

此功能包含在了之前的图书功能中,代码有变化,各位自己对照一下。

数据库

drop table if exists an_book;
create table an_book(
	id int not null auto_increment primary key comment '主键id',
	name varchar(100) not null comment '图书名称',
	price decimal(6,2) default null comment '价格',
	author varchar(50) default null comment '作者',
	express varchar(200) default null comment '出版社',
	img varchar(255) default null comment '图书封面',
	type_id int not null comment '图书分类id',
	content longtext default null comment '图书介绍'
) comment '图书表';
insert into an_book values(1,'三国演义',999.99,'罗贯中','商务出版社', 'http:',1,'');
//富文本编辑器
npm install wangeditor --save
import E from 'wangeditor';

package.json

{
  "name": "vueweb",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "serve": "vue-cli-service serve",
    "build": "vue-cli-service build"
  },
  "dependencies": {
    "axios": "^1.6.1",
    "core-js": "^3.8.3",
    "echarts": "^5.1.2",
    "element-ui": "^2.15.14",
    "mockjs": "^1.1.0",
    "vue": "^2.6.14",
    "vue-router": "^3.5.1",
    "vuex": "^3.6.2",
    "wangeditor": "^4.7.15"
  },
  "devDependencies": {
    "@vue/cli-plugin-babel": "^5.0.0",
    "@vue/cli-service": "^5.0.0",
    "sass": "^1.32.7",
    "sass-loader": "^12.0.0",
    "vue-template-compiler": "^2.6.14"
  }
}

bookview.vue

<template>
    <div>
        <!-- 搜索区域       -->
        <div style="margin-bottom:15px;">
            <el-input
                    v-model="searchForm.name"
                    style="width:200px;"
                    placeholder="请输入图书名"
                    @clear="doSearch"
                    @keypress.native.enter="doSearch"
                    clearable>
            </el-input>
            <el-input
                    v-model="searchForm.author"
                    style="width:200px;margin-left: 10px;"
                    placeholder="请输入作者"
                    @clear="doSearch"
                    @keypress.native.enter="doSearch"
                    clearable>
            </el-input>
            <el-input
                    v-model="searchForm.express"
                    style="width:200px;margin-left: 10px;"
                    placeholder="请输入出版社"
                    @clear="doSearch"
                    @keypress.native.enter="doSearch"
                    clearable>
            </el-input>
            <el-button
                    type="warning"
                    style="margin-left: 10px;"
                    icon="el-icon-search"
                    @click="doSearch">查询</el-button>
            <el-button
                    type="primary"
                    style="margin-left: 10px;"
                    icon="el-icon-toilet-paper"
                    @click="clearSearch">清空</el-button>
            <el-button
                    type="primary"
                    style="margin-left: 10px;"
                    icon="el-icon-plus" @click="addBtn"
                    v-if="admin.role !== 'ROLE_STUDENT'">新增</el-button>
        </div>

        <!-- 表格区域       -->
        <el-table
                :data="tableData"
                style="width: 100%">
            <el-table-column
                    prop="id"
                    label="ID">
            </el-table-column>
            <el-table-column
                    prop="name"
                    label="图书名称">
            </el-table-column>
            <el-table-column
                    prop="author"
                    label="作者">
            </el-table-column>
            <el-table-column
                    prop="express"
                    label="出版社">
            </el-table-column>
            <el-table-column
                    prop="price"
                    label="价格">
            </el-table-column>
            <el-table-column
                    prop="img"
                    label="图书封面">
                <template slot-scope="scope">
                    <el-image
                            :src="scope.row.img"
                            :preview-src-list="[scope.row.img]"
                            style="width:60px;height:60px;border-radius: 50%;"></el-image>
                </template>
            </el-table-column>
            <el-table-column
                    prop="typeId"
                    label="分类id">
                <template slot-scope="scope">
                    <span> {{ bookTypeList.find((item) => item.id === scope.row.typeId).name }}</span>
                </template>
            </el-table-column>
            <el-table-column label="图书介绍">
                <template slot-scope="scope">
                    <el-button type="text" @click="openEditorDialog(scope.row.content)">详情</el-button>
                </template>
            </el-table-column>
            <el-table-column label="操作" width="260px">
                <template slot-scope="scope">
                    <el-button
                            type="primary"
                            icon="el-icon-edit"
                            @click="editBtn(scope.row)"
                            v-if="admin.role === 'ROLE_ADMIN'">编辑</el-button>
                    <el-button
                            type="danger"
                            icon="el-icon-delete"
                            @click="deleteBtn(scope.row)"
                            v-if="admin.role === 'ROLE_ADMIN'">删除</el-button>
                    <el-button
                            icon="el-icon-download"
                            @click="downloadBtn(scope.row)">下载</el-button>
                </template>
            </el-table-column>
        </el-table>
        <!-- 分页区域       -->
        <div style="margin-top:15px;">
            <el-pagination
                    @size-change="handleSizeChange"
                    @current-change="handleCurrentChange"
                    :current-page="searchForm.currentPage"
                    :page-sizes="[2, 5, 10, 20]"
                    :page-size="searchForm.pageSize"
                    layout="total, sizes, prev, pager, next, jumper"
                    :total="total">
            </el-pagination>
        </div>
        <!--  对话框      -->
        <div>
            <el-dialog
                    :title="dialogTitle"
                    :visible.sync="dialogFormVisible"
                    :close-on-click-modal="false"
                    @close="closeDialog"
                    width="50%">
                <el-form
                        :model="addForm"
                        :rules="rules"
                        ref="addForm"
                        :label-width="formLabelWidth"
                        label-postion="left">
                    <el-form-item label="图书名称" prop="name">
                        <el-input v-model="addForm.name" clearable></el-input>
                    </el-form-item>
                    <el-form-item label="作者" prop="author">
                        <el-input v-model="addForm.author" clearable></el-input>
                    </el-form-item>
                    <el-form-item label="出版社" prop="express">
                        <el-input v-model="addForm.express" clearable></el-input>
                    </el-form-item>
                    <el-form-item label="价格" prop="price">
                        <el-input-number v-model="addForm.price" :max="9999.9" :min="0.1" :step="0.5"></el-input-number>
                    </el-form-item>
                    <el-form-item label="图片封面" prop="img">
<!--                        <el-input v-model="addForm.img" clearable></el-input>-->
                        <el-upload :action="uploadPath" :on-success="successUpload">
                            <el-button size="small" type="primary">点击上传</el-button>
                        </el-upload>
                    </el-form-item>
                    <el-form-item label="图书分类" prop="typeId">
                        <el-select v-model="addForm.typeId" clearable>
                            <el-option
                                    v-for="item in bookTypeList"
                                    :key="item.id"
                                    :label="item.name"
                                    :value="item.id"></el-option>
                        </el-select>
                    </el-form-item>
                    <el-form-item label="图书介绍">
                        <div id="editor"></div>
                    </el-form-item>
                </el-form>
                <div slot="footer" class="dialog-footer">
                    <el-button @click="resetBtn" v-show="dialogTitle === '新增图书'">重 置</el-button>
                    <el-button type="primary" @click="submitBtn">确 定</el-button>
                </div>
            </el-dialog>
        </div>
        <!-- 图书介绍详情       -->
        <div>
            <el-dialog
                    title="图书介绍"
                    :visible.sync="editorVisible"
                    :close-on-click-modal="false"
                    @close="editorVisible = false"
                    width="50%">
                <div v-html="editorContent"></div>
            </el-dialog>
        </div>
    </div>
</template>

<script>
    import request from "@/utils/request";
    import E from 'wangeditor';

    let editor;
    function initWangEditor(content){
        setTimeout(()=>{
            if(!editor){
                editor = new E('#editor');
                editor.config.placeholder = '请输入内容';
                editor.config.uploadFileName = 'file';
                editor.config.uploadImgServer = 'http://localhost:8089/api/files/wang/upload';
                editor.create();
            }
            editor.txt.html(content);
        },0);
    }

    export default {
        name: "BookView",
        computed: {
            admin(){
                return JSON.parse(window.localStorage.getItem('access-admin')) || { name: '未登录'};
            }
        },
        data() {
            return {
                //对话框显示内容
                editorContent:'',
                //图书介绍对话框
                editorVisible: false,
                //图书分类
                bookTypeList:[],
                //上传路径
                uploadPath:'http://localhost:8089/api/files/upload/',
                //下载路径
                downloadPath:'http://localhost:8089/api/files/down/',
                //添加表单
                addForm:{
                    name:'',
                    author:'',
                    express:'',
                    price:'',
                    img:'',
                    typeId:'',
                    content:'',
                },
                rules:{
                    name:[{required: true, message: '请输入图书名称', trigger: 'blur'}],
                    author:[{required: true, message: '请输入作者', trigger: 'blur'}],
                    express:[{required: true, message: '请输入出版社', trigger: 'blur'}],
                    price:[{required: true, message: '请输入价格', trigger: 'blur'}],
                    img:[{required: true, message: '请输入封面地址', trigger: 'blur'}],
                    typeId:[{required: true, message: '请选择图书分类', trigger: 'change'}],
                },
                //表单标题宽度
                formLabelWidth:'80px',
                //对话框标题
                dialogTitle:'',
                //对话框
                dialogFormVisible: false,
                //搜索条件
                searchForm:{
                    name: '',
                    author: '',
                    express:'',
                    currentPage: 1,
                    pageSize: 5
                },
                tableData: [],
                total:0
            }
        },
        methods: {
            //图书详情打开事件
            openEditorDialog(data){
                this.editorContent = data;
                this.editorVisible = true;
            },
            //获取全部的图书分类
            getBookTypeList(){
                request.get('/bookType/list').then(res=>{
                    if(res.code === '200'){
                        this.bookTypeList = res.data;
                    }
                })
            },
            //下载按钮
            downloadBtn(row){
                window.location.href = row.img;
            },
            //文件上传成功的回调
            successUpload(res){
                this.addForm.img = this.downloadPath + res.data;
            },
            //删除
            deleteBtn(row){
                this.$confirm(`您确定要删除【${row.name}】吗`,'删除提示',{
                    confirmButtonText:'删除',
                    cancelButtonText:'取消',
                    type:'warning',
                }).then(()=>{
                    request.delete('/book/delete',{
                        params:{ id : row.id}
                    }).then(res => {
                        if(res.code === '200'){
                            this.$message.success(res.data);
                            this.doSearch();
                        }
                    })
                }).catch(_=>{
                    this.$message.warning('已取消删除');
                })
            },
            //编辑
            editBtn(row){
                let obj = JSON.parse(JSON.stringify(row));
                this.addForm = obj;
                this.dialogTitle = "编辑图书";
                this.dialogFormVisible = true;
                initWangEditor(obj.content);
            },
            //关闭对话框
            closeDialog(){
                this.resetBtn();
                this.dialogFormVisible = false;
            },
            //新增保存
            submitBtn(){
                this.$refs.addForm.validate((valid)=>{
                    if(valid){
                        //校验通过
                        //富文本的内容,存入
                        this.addForm.content = editor.txt.html();
                        //有id,编辑,没有id是新增
                        request.post(this.addForm.id ? '/book/update':'/book/save',this.addForm)
                            .then(res=>{
                                if(res.code === '200'){
                                    this.$message.success(res.data);
                                    this.resetBtn();
                                    this.dialogFormVisible = false;
                                    this.doSearch();
                                }else {
                                    this.$message.error(res.msg);
                                }
                            })
                    }
                })
            },
            //新增重置
            resetBtn(){
                this.$refs.addForm.resetFields();
                //修复bug
                this.addForm = {};
            },
            addBtn(){
                this.dialogTitle = '新增图书';
                this.dialogFormVisible = true;
                initWangEditor("");
            },
            clearSearch(){
                this.searchForm.name = '';
                this.searchForm.author = '';
                this.searchForm.express = '';
                this.doSearch();
            },
            //搜索
            doSearch(){
                //修复bug
                this.searchForm.currentPage = 1;
                this.getData();

            },
            handleSizeChange(val) {
                this.searchForm.pageSize = val;
                this.searchForm.currentPage = 1;
                this.getData();
            },
            handleCurrentChange(val) {
                this.searchForm.currentPage = val;
                this.getData();
            },
            //获取数据
            getData(){
                request.get('/book/searchPage',{
                    params: this.searchForm
                }).then(res=>{
                    if(res.code === '200'){
                        this.tableData = res.data.records; //数据
                        this.searchForm.currentPage = res.data.current; //当前页
                        this.searchForm.pageSize = res.data.size; //页条数
                        this.total = res.data.total; //总条数
                    }else {
                        this.$message.error(res.msg);
                    }
                });
            }
        },
        created(){
            //获取数据
            this.getData();
            //获取图书分类
            this.getBookTypeList();
        }
    }
</script>

<style scoped>

</style>

fileController.java

package com.shrimpking.controller;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.util.StrUtil;
import com.shrimpking.res.Result;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Created by IntelliJ IDEA.
 *
 * @Author : Shrimpking
 * @create 2023/11/13 11:07
 */
@RestController
@RequestMapping("/files")
public class FileController
{
    /**
     * 文件上传存储路径
     */
    private static final String filePath = System.getProperty("user.dir") + "/file/";

    /**
     * 上传文件
     * @param file
     * @return
     */
    @PostMapping("/upload")
    public Result upload(MultipartFile file){

        synchronized (FileController.class){
            //获取当前时间戳
            String flag = System.currentTimeMillis() + "";
            //获取原始文件名
            String fileName = file.getOriginalFilename();
            try
            {
                //如果文件夹不存在,在项目的根目录中创建文件夹
                if(!FileUtil.isDirectory(filePath)){
                    FileUtil.mkdir(filePath);
                }
                //文件存储形式: 时间戳 + 文件名
                FileUtil.writeBytes(file.getBytes(),filePath + flag + "-" + fileName);
                System.out.println(fileName + "--上传成功");
                Thread.sleep(1L);
            }catch (Exception e){
                System.err.println(fileName + "--上传失败");
            }
            return Result.success(flag);
        }
    }


    /**
     * 下载文件
     * @param flag
     * @param response
     */
    @GetMapping("/down/{flag}")
    public void avatarPath(@PathVariable String flag, HttpServletResponse response){
        //创建目录
        if(!FileUtil.isDirectory(filePath)){
            FileUtil.mkdir(filePath);
        }

        OutputStream os;
        //获取目录下全部文件列表
        List<String> fileNames = FileUtil.listFileNames(filePath);
        //逐一核对,获取该文件名称
        String avatar = fileNames.stream().filter(item -> item.contains(flag)).findAny().orElse("");

        try
        {
            if(StrUtil.isNotEmpty(avatar)){
                response.addHeader("Content-Disposition","attachment;filename=" + URLEncoder.encode(avatar,"UTF-8"));
                response.setContentType("application/octet-stream");
                byte[] bytes = FileUtil.readBytes(filePath + avatar);
                os = response.getOutputStream();
                os.write(bytes);
                os.flush();
                os.close();
            }
        }catch (Exception e){
            System.out.println("文件下载失败");
        }
    }

    /**
     * wangeditor编辑器上传文件接口
     * @param file
     * @return
     */
    @PostMapping("/wang/upload")
    public Map<String,Object> wangEditorUpload(MultipartFile file){

        synchronized (FileController.class){
            //获取当前时间戳
            String flag = System.currentTimeMillis() + "";
            //获取原始文件名
            String fileName = file.getOriginalFilename();
            try
            {
                //如果文件夹不存在,在项目的根目录中创建文件夹
                if(!FileUtil.isDirectory(filePath)){
                    FileUtil.mkdir(filePath);
                }
                //文件存储形式: 时间戳 + 文件名
                FileUtil.writeBytes(file.getBytes(),filePath + flag + "-" + fileName);
                System.out.println(fileName + "--上传成功");
                Thread.sleep(1L);
            }catch (Exception e){
                System.err.println(fileName + "--上传失败");
            }
            Map<String,Object> resultMap = new HashMap<>();
            resultMap.put("errno",0);
            resultMap.put("data", CollUtil.newArrayList(Dict.create().set("url","http://localhost:8089/api/files/down/" + flag)));
            return resultMap;
        }
    }
}

测试

编辑图书

图书介绍

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

虾米大王

有你的支持,我会更有动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值