需求
富文本编辑器
使用vue3+Setup
做一个微信上的富文本编辑器,包含简单的文本样式设置以及图片上传等功能。
查阅资料发现uni-app的组件中自带editor。官方文档链接
但是官方文档没有太多细节,建议参考:#微信小程序实战之实现富文本编辑器 讲的非常仔细
效果图展示
直接上代码
这里只展示富文本有关代码
<template>
<!-- 工具栏:包含格式化按钮 -->
<view class="toolbar" @tap="format">
<!-- 加粗按钮,.bold是否为true -->
<view :class="formats.bold ? 'ql-active' : ''" class="iconfont icon-zitijiacu" data-name="bold"></view>
<!-- 斜体按钮 -->
<view :class="formats.italic ? 'ql-active' : ''" class="iconfont icon-zitixieti" data-name="italic"></view>
<!-- 下划线按钮 -->
<view :class="formats.underline ? 'ql-active' : ''" class="iconfont icon-zitixiahuaxian" data-name="underline">
</view>
<!-- 文本对齐按钮:左对齐,center,justify排除百度小程序兼容性 -->
<!-- 左对齐按钮,判断是否选中 -->
<view :class="formats.align === 'left' ? 'ql-active' : ''" class="iconfont icon-zuoduiqi" data-name="align"
data-value="left"></view>
<!-- 中心对齐按钮 -->
<view :class="formats.align === 'center' ? 'ql-active' : ''" class="iconfont icon-juzhongduiqi" data-name="align"
data-value="center"></view>
<!-- 右对齐按钮 -->
<view :class="formats.align === 'right' ? 'ql-active' : ''" class="iconfont icon-youduiqi" data-name="align"
data-value="right"></view>
<!-- 两端对齐按钮 -->
<view :class="formats.align === 'justify' ? 'ql-active' : ''" class="iconfont icon-zuoyouduiqi" data-name="align"
data-value="justify"></view>
<!-- 撤销和重做按钮,分别调用undo和redo函数 -->
<view class="iconfont icon-undo" @tap="undo"></view>
<view class="iconfont icon-redo" @tap="redo"></view>
<!-- 缩进和增加缩进,分别设置data-value="-1"和"+1" -->
<view class="iconfont icon-outdent" data-name="indent" data-value="-1"></view>
<view class="iconfont icon-indent" data-name="indent" data-value="+1"></view>
<!-- 标题按钮,设置为标题1格式 -->
<view :class="formats.header === 1 ? 'ql-active' : ''" class="iconfont icon-format-header-1" data-name="header"
:data-value="1"></view>
<!-- 图片插入按钮,调用insertImage函数 -->
<view class="iconfont icon-charutupian" @tap="insertImage"></view>
<!-- 清除内容按钮,调用clear函数 -->
<view class="iconfont icon-shanchu" @tap="clear"></view>
</view>
<!-- 富文本编辑器 -->
<editor id="editor" class="ql-container" placeholder="{{placeholder}}" showImgSize showImgToolbar showImgResize
bindstatuschange="onStatusChange" read-only="{{readOnly}}" @ready="onEditorReady" @input="updateContent">
</editor>
</template>
script部分
注意1:uni的Editor不可以使用v-model进行双向绑定,但是官方给的办法是使用@input,当内容发生改变的时候就会记录。
详情参考官方文档
<script lang="ts" setup>
import { ref } from 'vue'
const editorCtx = ref(null) // 编辑器上下文
const formats = ref({}) // 格式化状态存储
const placeholder = ref('请输入信息...')
const form = ref({
xiangQing: '' // 编辑器内容存储字段 我这里只显示xiangQing
})
</script>
下面是初始化部分,这里留个数据回显的问题放在最后。
<script lang="ts" setup>
// 编辑器初始化
const onEditorReady = () => {
// 选择编辑器组件并获取上下文
uni.createSelectorQuery().select('#editor').context((res) => {
editorCtx.value = res.context
if (form.value.xiangQing) {
editorCtx.value.setContents({ html: form.value.xiangQing }) // 如果有值,则回显给富文本编辑区
}
}).exec()
}
// 设置富文本格式化功能(如加粗、斜体等)
const format = (e) => {
const { name, value } = e.target.dataset
if (!name) return
editorCtx.value.format(name, value) // 根据name和value应用格式
}
// 更新格式化状态
const onStatusChange = (e) => {
const formats = e.detail // 获取格式化状态
formats.value = formats // 将状态更新到formats变量
}
// 撤销操作
const undo = () => {
editorCtx.value.undo()
}
// 重做操作
const redo = () => {
editorCtx.value.redo()
}
// 清空编辑器内容
const clear = () => {
uni.showModal({
title: '清空编辑器',
content: '确定清空编辑器全部内容?',
success: (res) => {
if (res.confirm) {
editorCtx.value.clear({
success: () => console.log('clear success'),
})
}
},
})
}
// 获取编辑器内容
const updateContent = (e) => {
const content = e.detail // 获取编辑器内容
form.value.xiangQing = content.html // 更新到 form.xiangQing
}
</script>
下面是图片部分,这里我的图片不需要存储到云端,只需要转换成base64和编辑区内容一起传给后端就可以了。
// 插入图片
const insertImage = () => {
uni.chooseImage({
count: 1,
success: (res) => {
const filePath = res.tempFilePaths[0]
uni.getFileSystemManager().readFile({
filePath,
encoding: 'base64',
success: (result) => {
const base64Data = 'data:image/png;base64,' + result.data
editorCtx.value.insertImage({
src: base64Data,
alt: '图像',
success: () => console.log('insert image success with base64'),
})
},
fail: (err) => {
console.error('Failed to convert image to base64:', err)
},
})
},
})
}
样式部分
<script lang="scss" scoped>
.toolbar {
display: flex;
flex-wrap: wrap; // 允许换行
justify-content: space-around; // 平均分配空间
align-items: center; // 垂直居中
background-color: #f9f9f9; // 背景颜色
padding: 10px; // 内边距
border-radius: 10px; // 圆角
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); // 阴影效果
margin-top: 20rpx; // 顶部边距
.iconfont {
text-align: center;
flex: 0 0 10%; // 设置每个图标占据 10% 宽度
margin: 5px; // 添加间距
transition: color 0.3s, transform 0.3s; // 添加过渡效果
cursor: pointer; // 手型光标
}
.ql-active {
color: #497749; // 激活状态的颜色
font-weight: bold; // 激活时加粗
}
}
.ql-container {
box-sizing: border-box;
padding: 12px 15px;
width: 100%;
min-height: 30vh;
height: 750rpx;
background: #fff;
margin-top: 20rpx;
font-size: 16px;
line-height: 1.5;
border: 1rpx solid #f2f2f2;
border-radius: 15rpx;
}
</script>
最后
前面留的小问题 修改的时候会发生
我的的Id是需要从主页面链接过来,当我拿到Id后再去请求form的所有数据并赋值。 在这个时候如果赋值过来的xiangQing的数据太大比如有base64图片,就会加载不出来,但是很玄学有的时候可以 我的做法是设置个await,先让数据赋值过来 再去执行onEditorReady就没问题了。
<script lang="scss" scoped>
onLoad(async (options) => {
id.value = options.id
name.value = options.name
form.value.stationId = options.id
if (options.analysisId) {
await open(options.analysisId)
uni.setNavigationBarTitle({
title: '修改分析报告',
})
onEditorReady()
}
})
</script>