vite vue-quill 构建基本案例

示例图

示例图

注意事项:如果没有使用后端接口时,插入图片的时候总是会插在最前面,使用了就不会了

Vue 前端

安装版本

  "dependencies": {
    
    
  	...
    "@ssumo/quill-resize-module": "^1.0.1",
    "@vueup/vue-quill": "^1.0.1",
     "quill-image-paste-module": "^1.0.6",
    ...
  },

安装Quill相关

> npm install @vueup/[email protected]
> npm install [email protected]
> npm install @ssumo/[email protected]

之所以使用 @vueup/vue-quill ,是因为使用 vue-quill-editor 老是有一堆报错
imports of undefinedundefined (reading 'register')之类的

参考文档

vue-quill 基本格式案例
https://blog.csdn.net/weixin_44892327/article/details/127625100
https://blog.csdn.net/qq_33674679/article/details/128128291

自定义上传图片到服务器、粘贴图片上传至服务器、拖拽图片上传至服务器的功能
https://www.npmjs.com/package/quill-image-paste-module

图片、视频缩放大小
https://github.com/scrapooo/quill-resize-module

具体使用方法见参考文档,写的很详细了

main.js

import {
    
     createApp } from 'vue';
import App from './App.vue';

// 导入vue富文本编辑器
import {
    
     QuillEditor } from '@vueup/vue-quill'
import '@vueup/vue-quill/dist/vue-quill.snow.css'
import '@vueup/vue-quill/dist/vue-quill.bubble.css'

const app = createApp(App);
app.component('QuillEditor', QuillEditor)
app.mount("#app");

components/RichTextEditor.vue

<template>
  <div>
    <input v-model="title" class="title" type="text" />
    <input
      class="saveDraft"
      type="button"
      value="保存草稿"
      @click="saveDraft"
    />
    <input class="saveDiary" type="button" value="发布文章" />
    <quill-editor
      v-model:content="content"
      :options="editorOption"
      contentType="html"
      style="height: 600px"
    />
  </div>
</template>
    
<script>
import {
    
     QuillEditor, Quill } from "@vueup/vue-quill";

import {
    
     container, ImageExtend, QuillWatch } from "quill-image-paste-module";
Quill.register("modules/ImageExtend", ImageExtend);

import ResizeModule from "@ssumo/quill-resize-module";

Quill.register("modules/resize", ResizeModule);
// Quill.register("modules/ImageExtend", ImageExtend);

export default {
    
    
  // eslint-disable-next-line vue/multi-word-component-names
  name: "RichTextEditor",
  components: {
    
     QuillEditor },
  data() {
    
    
    return {
    
    
      title: "【无标题】",
      example: "test",
      content: "",
      // 富文本框参数设置
      // Rich text box parameter settings
      editorOption: {
    
    
        modules: {
    
    
          ImageExtend: {
    
    
            // 可选参数 是否显示上传进度和提示语
            // Optional parameters. Whether to display upload progress and prompt
            loading: true,
            // 图片参数名
            // Picture parameter name
            name: "img",
            // 可选参数 图片大小,单位为M,1M = 1024kb
            // Optional parameters. Image size, Unit is M
            size: 10,
            // 服务器地址, 如果action为空,则采用base64插入图片
            // Server address, if action is empty, use base64 to insert picture
            action: "/upload",
            // 可选 可上传的图片格式
            // Optional, uploadable image format
            accept:
              "image/jpg, image/png, image/gif, image/jpeg, image/bmp, image/x-icon",
            // response 为一个函数用来获取服务器返回的具体图片地址
            // response is a function to get the specific image address returned by the server
            // 例如服务器返回 {code: 200; data:{ url: 'baidu.com'}}
            // For example, the server returns {code: 200; data:{ url: 'baidu.com'}}
            // 则 return res.data.url
            response: (res) => {
    
    
              console.log(res);
              return res.data.url;
            },
            // 可选参数 设置请求头部
            // Optional parameter. Set request header
            headers: (xhr) => {
    
    
              // 比如添加 csrf-token
              // For example, add csrf-token
              xhr.setRequestHeader("X-CSRFToken", "TestToken");
            },
            // 图片超过大小的回调
            // Callback when the image exceeds the size
            sizeError: () => {
    
    
              alert("图片大小超过 10 M");
            },
            // 可选参数 自定义开始上传触发事件
            // Optional parameters. Custom start upload trigger event
            start: () => {
    
    },
            // 可选参数 自定义上传结束触发的事件,无论成功或者失败
            // Optional parameters. Customize the event triggered by the end of upload, regardless of success or failure
            end: () => {
    
    },
            // 可选参数 上传失败触发的事件
            // Optional parameter. The event triggered by upload failure
            error: () => {
    
    },
            // 可选参数  上传成功触发的事件
            // Optional parameter. The event triggered by a successful upload
            success: () => {
    
    },
            // 可选参数 选择图片触发,也可用来设置头部,但比headers多了一个参数,可设置formData
            // Optional parameters. Select the picture to trigger. It can also be used to set the header, but there is one more parameter than headers, which can be set formData
            change: (xhr, formData) => {
    
    
              formData.append("example", "test");
            },
          },
          resize: {
    
    
            locale: {
    
    
              altTip: "按住alt键比例缩放",
              inputTip: "回车键确认",
              floatLeft: "靠左",
              floatRight: "靠右",
              center: "居中",
              restore: "还原",
            },
          },
          toolbar: {
    
    
            // container为工具栏,此次引入了全部工具栏,也可自行配置
            // container is a toolbar, all toolbars are introduced this time, and they can also be configured by themselves
            
            // container: container,
            container: [
              ["bold", "italic", "underline", "strike"], // 加粗 斜体 下划线 删除线
              ["blockquote", "code-block"], // 引用  代码块
              [{
    
     list: "ordered" }, {
    
     list: "bullet" }], // 有序、无序列表
              [{
    
     indent: "-1" }, {
    
     indent: "+1" }], // 缩进
              [{
    
     size: ["small", false, "large", "huge"] }], // 字体大小
              [{
    
     header: [1, 2, 3, 4, 5, 6, false] }], // 标题
              [{
    
     color: [] }, {
    
     background: [] }], // 字体颜色、字体背景颜色
              [{
    
     align: [] }], // 对齐方式
              ["clean"], // 清除文本格式
              ["link", "image", "video", "docx"], // 链接、图片、视频、文档//重要
            ],
            handlers: {
    
    
              image: function () {
    
    
                // 劫持原来的图片点击按钮事件
                // Hijack the original picture click button event
                QuillWatch.emit(this.quill.id);
              },
            },
          },
        },
      },
    };
  },
  methods: {
    
    
    saveDraft() {
    
    
      console.log(this.content);
    },
  },
};
</script>
    
<style scoped>
.saveDraft {
    
    
  height: 41px;
  margin-right: 16px;
  padding: 0 16px;
  font-size: 16px;
  color: #c7c9cb;
  border: 1px solid rgb(178, 62, 255);
  border-radius: 18px;
  white-space: nowrap;
  background: none;
}
.saveDraft:hover {
    
    
  background-color: rgba(203, 203, 203, 20%);
}
.saveDiary {
    
    
  height: 41px;
  margin-right: 16px;
  padding: 0 16px;
  font-size: 16px;
  color: #c7c9cb;
  border: 1px solid rgb(178, 62, 255);
  border-radius: 18px;
  white-space: nowrap;
  background-color: rgb(178, 62, 255);
}
.saveDiary:hover {
    
    
  background-color: rgb(178, 62, 255, 70%);
}
.title {
    
    
  width: 20%;
  border-radius: 18px;
  padding: 8px;
  padding-right: 88px;
  font-size: 18px;
  line-height: 24px;
  background: none;
  margin: 10px;
}
/* ql开头表示富文本样式设计 */
.ql-toolbar.ql-snow {
    
    
  border: 1px solid #d1d5db;
  box-sizing: border-box;
  font-family: "Helvetica Neue", "Helvetica", "Arial", sans-serif;
  padding: 8px;
  background-color: rgb(178, 62, 255, 70%);
}
</style>    

跨域请求相关

> npm install @vitejs/plugin-vue

vite.config.js

我是使用vite构建的项目,
使用VueCLI构建的,麻烦自行搜索方法

import {
    
     defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

// https://vitejs.dev/config/
export default defineConfig({
    
    
  plugins: [vue(),],
  server: {
    
    
    proxy: {
    
    
      '/upload': {
    
    
        target: 'http://127.0.0.1:3000',
        changeOrigin: true,
      },
    }
  }
})

Node 后端

版本

  "dependencies": {
    
    
    "body-parser": "^1.20.1",
    "cors": "^2.8.5",
    "express-fileupload": "^1.4.0",
    "express.js": "^1.0.0",
    "nodemon": "^2.0.20"
  },

安装

> npm install [email protected]
> npm install [email protected]
> npm install [email protected]
> npm install [email protected]
> npm install [email protected]

后端 express 代码 server.js

const express = require('express');
const path = require('path');
// 跨域
const cors = require("cors");

// 上传文件
const fileupload = require('express-fileupload');
let initial_path = path.join(__dirname, "public");

// post请求解析
const bodyParser = require("body-parser");
// 解析以 application/json 和 application/x-www-form-urlencoded 提交的数据
var jsonParser = bodyParser.json();
var urlencodedParser = bodyParser.urlencoded({
    
     extended: false });

const app = express();

app.use(express.static(initial_path));
app.use(fileupload());
// 跨域问题
app.use(cors());
app.use(urlencodedParser);
app.use(jsonParser)

// 开启 cors 不包含的跨域请求
app.use(function (req, res, next) {
    
    
    // 请求头部分
    res.header("Access-Control-Allow-Origin", "*");
    res.header("Access-Control-Allow-Headers", "Access-Control-Allow-Credentials", "Origin, X-Requested-With, Content-Type, Accept");

    // 定义了 next 表示会继续执行
    // 不写则到这里就停止了, 后面不会执行
    next();
});


// upload link
app.post('/upload', (req, res) => {
    
    
    let file = req.files.img;
    let date = new Date();
    // image name
    let imagename = date.getDate() + date.getTime() + file.name;
    // image upload path
    let path = 'public/uploads/' + imagename;

    // create upload
    file.mv(path, (err, result) => {
    
    
        if (err) {
    
    
            throw err;
        } else {
    
    
            // our image upload path
            res.json({
    
    
                data: {
    
    
                    url: `uploads/${
      
      imagename}`
                }
            })
        }
    })
})



app.listen("3000", () => {
    
    
    console.log('listening...')
})

猜你喜欢

转载自blog.csdn.net/m0_46262108/article/details/128855156