富文本编辑器使用md-editor-v3
官方链接:imzbf.github.io/md-editor-v…
# 使用npm安装
npm install md-editor-v3
setup模板:
<template>
<ContentWrap title="Article">
<MdEditor v-model="text" v-on:on-upload-img="onUploadImg" v-on:save="onSave" />
</ContentWrap>
</template>
<script setup lang="ts">
import { ContentWrap } from '@/components/ContentWrap'
import { ref } from 'vue';
import { MdEditor } from 'md-editor-v3';
import 'md-editor-v3/lib/style.css';
import { uploadImageApi } from '@/api/image';
const text = ref('Hello Editor!');
const emit = defineEmits(['update:content', "onSave"])
const onUploadImg = async (files, callback) => {
const res = await Promise.all(
files.map((file) => {
console.log("file: ", file)
return uploadImageApi(file)
})
);
callback(res.map((item) => {
console.log("item: ", item)
return item.data.data[0]
}));
};
const onSave = (md, h) => {
emit("onSave", md)
}
</script>
files和callback是固定写法,编辑器使用callback中的返回值进行图片的反显。
uploadImageApi实际上是调用了axios向后端发起请求。
export const uploadImageApi = (file) => {
return new Promise((resolve, reject) => {
const form = new FormData();
form.append('image', file);
axios.post("/api/image/insert", form, {
headers: {
'Content-Type': 'multipart/form-data',
'Authorization': localStorage.getItem('Authorization')
}
}).then((res) => resolve(res))
.catch((error) => reject(error));
})
}
后端基于golang实现,具体实现可以有很多种,我使用了minIO对象存储进行图片存储,也可以直接存储到服务器的某个文件夹下。存储到minIO后,直接返回该图片url。
package image_api
import (
"UTABackend/global"
"UTABackend/models/res"
"UTABackend/utils/jwts"
"context"
"fmt"
"net/http"
"time"
"github.com/gin-gonic/gin"
"github.com/minio/minio-go/v7"
)
func (ImageApi) ImageInsert(c *gin.Context) {
fmt.Println("调用ImageInsert")
// 读取图片
form, err := c.MultipartForm()
if err != nil {
c.JSON(500, gin.H{"ImageInsert c.MultipartForm() error": err.Error()})
return
}
// 获取UserId
token := c.GetHeader("Authorization")
claim, err := jwts.ParseToken(token)
if err != nil {
// token验证不通过
c.JSON(http.StatusBadRequest, gin.H{
"error": err.Error(),
})
}
if claim.Role != 1 {
// 权限不足
res.ErrorResult(400, "", "权限不足", c)
return
}
// 获取文件
files := form.File["image"]
resList := []string{}
for _, file := range files {
uniqueFileName := fmt.Sprintf("%d_%s", time.Now().UnixNano(), file.Filename)
// fmt.Println(file.Filename)
bucketName := "uta-images"
objectName := fmt.Sprintf("%d/%s", claim.UserID, uniqueFileName)
src, err := file.Open()
if err != nil {
res.ErrorResult(500, "", err.Error(), c)
return
}
defer src.Close()
ctx := context.Background()
_, err = global.MinIO.PutObject(ctx, bucketName, objectName, src, file.Size, minio.PutObjectOptions{})
if err != nil {
res.ErrorResult(500, "", err.Error(), c)
return
}
// 返回图片地址url
imageUrl := "http://" + global.CONFIG.Minio.Endpoint + ":" + global.CONFIG.Minio.Port + "/" + bucketName + "/" + objectName
resList = append(resList, imageUrl)
}
// 返回结果
res.OkWithDataAndMessage(resList, "插入图片成功", c)
}