uniapp 富文本框的使用

uniapp中是有富文本框组件的。

uniapp中的富文本框组件的使用官网链接:https://uniapp.dcloud.io/component/editor?id=editor

官网富文本框的展示部分:(下面红框框住的菜单含义如下:)

B 加粗
I 斜体
U 下划线
居中
靠右
字体
撤回
反撤回
添加横线
图片

结构部分:

<editor id="editor" class="ql-container" :placeholder="placeholder" @ready="onEditorReady"></editor>

1
代码部分:

export default {
    data() {
        return {
            placeholder: '开始输入...'
        }
    },
    methods: {
        onEditorReady() {
            uni.createSelectorQuery().select('#editor').context((res) => {
                this.editorCtx = res.context
            }).exec()
        },
        undo() {
            this.editorCtx.undo()
        }
    }
}

最上面图片的展示代码如下:

textarea 多行文本组件的使用——标题与副标题部分

<textarea v-model="detail.title" placeholder="添加标题" auto-height placeholder-class="grey"/>

1
v-model绑定的是多行文本的内容,placeholder是提示信息,auto-height是根据内容自动高度增加,placeholder-class是提示信息的样式类名

picker 选择组件的使用——文章分类的使用

<picker @change="bindPickerChange" :value="index" :range="array">
	<view class="uni-input">{{array[index]}}</view>
</picker>

index 文章分类数组的索引,range文章分类的数组,此数组的格式是字符串的数组,如果接口返回的数据不合适,则需要通过for循环遍历进行数组重构。

image 组件——封面图片的上传
uniapp 提供了上传图片的api;

uniapp 官网提供的上传图片的示例如下:

uni.chooseImage({
    count: 6, //默认9
    sizeType: ['original', 'compressed'], //可以指定是原图还是压缩图,默认二者都有
    sourceType: ['album'], //从相册选择
    success: function (res) {
        console.log(JSON.stringify(res.tempFilePaths));
    }
});

在上传完图片后,可以通过阿里OSS进行本地图片转化为网络图片,然后通过uniapp.uploadFile的方法将网络图片获取到。

具体代码如下所示:

async chooseImage(type) {
	let _this = this;
	uni.chooseImage({
		count: 1, //最多可以选择的图片张数,默认9
		sizeType: ['original', 'compressed'], //original 原图,compressed 压缩图,默认二者都有
		sourceType: ['album', 'camera'], //album 从相册选图,camera 使用相机,默认二者都有
		success: (respone) => {
			_this.$request.urlRequest(
				'/gate/oss/token', {},
				'GET',
				(res) => {
					if (res.code == 200) {
					let data = res.result;
					let env = {
						uploadImageUrl: 'https://58d.oss-cn-hangzhou.aliyuncs.com/', // 默认存在根目录,可根据需求改
						AccessKeySecret: data.AccessKeySecret, // AccessKeySecret 去你的阿里云上控制台上找
						OSSAccessKeyId: data.AccessKeyId, // AccessKeyId 去你的阿里云上控制台上找
						stsToken: data.SecurityToken,
						timeout: 87600 //这个是上传文件时Policy的失效时间
					}
					let dir = 'images/';
					let filePath = respone.tempFilePaths[0];
					const aliyunFileKey = dir + new Date().getTime() + Math.floor(Math.random() * 150) + '.png';
					const aliyunServerURL = env.uploadImageUrl; //OSS地址,需要https
					const accessid = env.OSSAccessKeyId;
					const policyBase64 = _this.getPolicyBase64(env);
					const signature = _this.getSignature(policyBase64, env); //获取签名
					const stsToken = env.stsToken;
	
					let param = {
						'key': aliyunFileKey,
						'policy': policyBase64,
						'OSSAccessKeyId': accessid,
						'signature': signature,
						'success_action_status': '200',
						'x-oss-security-token': stsToken,
						'stsToken': stsToken,
					};
	
					uni.uploadFile({
						url: "https://58d.oss-cn-hangzhou.aliyuncs.com/", //开发者服务器 url
						filePath: filePath, //要上传文件资源的路径
						name: 'file', //必须填file
						formData: param,
						success: (res) => {
							if(type){
								_this.detail.imageUrl = aliyunServerURL + aliyunFileKey;
								return;
							}
							_this.editorCtx.insertImage({
								src: aliyunServerURL + aliyunFileKey, // 此处需要将图片地址切换成服务器返回的真实图片地址
								alt: '图片',
								width:"320upx",// 此处可以对图片进行尺寸的限制,否则页面中的图片会展示不全
								mode:'widthFix',
								success: function(e) {}
							});
						},
						fail: (err) => {
							// _this.msg.push(JSON.stringify(err));
							// err.wxaddinfo = aliyunServerURL;
							// failc(err);
						},
					})
				}
			}
		)
	}
});

editor 富文本框组件的使用——添加图文部分`

<editor class="ql-container"  :placeholder="placeholder" :show-img-size="true" :show-img-toolbar="true"
 :show-img-resize="true" @ready="onEditorReady" id="editor" @statuschange="statuschange" @focus="editFocus" @blur="editBlur"
 ref="editot"></editor>
<view class="tool-view" >
	<view class="tool">
		<jinIcon class="single" type="" font-size="44upx" title="插入图片" @click="chooseImage"></jinIcon>
		<jinIcon class="single" type="" font-size="44upx" title="修改文字样式" @click="showMore" :color="showMoreTool ? activeColor : '#666666'"></jinIcon>
		<jinIcon class="single" type="" font-size="44upx" title="分割线" @click="insertDivider"></jinIcon>
		<jinIcon class="single" type="" font-size="44upx" title="撤销" @click="undo"></jinIcon>
		<jinIcon class="single" type="" font-size="44upx" title="重做" @click="redo"></jinIcon>
		<jinIcon class="single" type="提交" font-size="40upx" title="设置" @click="showSetting"></jinIcon>
	</view>
	<!-- 文字相关操作 -->
	<view class="font-more" v-if="showMoreTool">
		<jinIcon class="single" type="" font-size="44upx" title="加粗" @click="setBold" :color="showBold ? activeColor : '#666666'"></jinIcon>
		<jinIcon class="single" type="" font-size="44upx" title="斜体" @click="setItalic" :color="showItalic ? activeColor : '#666666'"></jinIcon>
		<jinIcon class="single" type="" font-size="44upx" title="分割线" @click="setIns" :color="showIns ? activeColor : '#666666'"></jinIcon>
		<jinIcon class="single" type="" font-size="44upx" title="标题" @click="setHeader" :color="showHeader ? activeColor : '#666666'"></jinIcon>
		<jinIcon class="single" type="" font-size="44upx" title="居中" @click="setCenter" :color="showCenter ? activeColor : '#666666'"></jinIcon>
		<jinIcon class="single" type="" font-size="44upx" title="居右" @click="setRight" :color="showRight ? activeColor : '#666666'"></jinIcon>
	</view>
</view>`

style样式如下:

.ql-container {
	line-height: 160%;
	font-size: 34upx;
	width: 100%;
	height:calc(100vh - 450upx);
	overflow-y: auto;
	margin: 20upx auto;
	// position: relative;
	// top:0;
	border:2upx solid #bbb;
	padding:20upx 30upx;
	box-sizing: border-box;
	// padding-bottom:600upx;
}

.tool-view {
	width: 100vw;
	position: fixed;
	bottom: 0;
	left: 0;
}

.tool {
	height: 100upx;
	display: flex;
	align-items: center;
	justify-content: space-around;
	width: 100%;
	background: #eee;
}

.font-more {
	position: absolute;
	left: 0;
	height:100upx;
	bottom: 100upx;
	display: flex;
	align-items: center;
	justify-content: space-around;
	width: 100%;
	background: rgb(235, 235, 235);
	overflow: hidden;
	transition: all 0.15s;
}

.setting-layer {
	position: absolute;
	bottom: 100upx;
	background: #fff;
	width: 250upx;
	right: 20upx;
	box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
	border-radius: 8upx;
}

.setting-layer .single {
	height: 80upx;
	font-size: 32upx;
	padding: 0 30upx;
	display: flex;
	align-items: center;
	line-height: 80upx;
	flex-direction: row;
	color: #666;
}

.setting-layer .single .icon {
	margin-right: 20upx;
}

.setting-layer-mask {
	position: fixed;
	left: 0;
	top: 0;
	width: 100vw;
	height: 100vh;
	background: transparent;
}

script部分代码:

data({
	return{
		showMoreTool: false,
		showBold: false,
		showItalic: false,
		showIns: false,
		showHeader: false,
		showCenter: false,
		showRight: false,
		showSettingLayer: false,
		activeColor: '#F56C6C',
		screenHeight:"",
		keyboardFlag:false,
	}
}),
onLoad(options){
	  this.iosFlag = (uni.getSystemInfoSync().platform=='ios');//判断当前环境为安卓或IOS
	 this.screenHeight = uni.getSystemInfoSync().windowHeight;//获取当前环境的高度
	 this.getList();//获取文章分类的数据
	 if(options && options.id){ // 根据链接中的id获取内容,这个是修改或展示图文的时候需要调用的接口
		 this.id = options.id;
		 this.getDetail(options.id);
	 }
},
onBackPress(options) {
	//监听页面的返回按键   
	let _this = this;
    if (options.from === 'backbutton' && !_this.id) {
		this.editorCtx.getContents({ // 通过这个方法可以获取富文本框中的内容
			success: (data)=> {
				this.richText = data;
			}
		})  
		// 下面的if判断,如果标题/副标题/封面图/富文本框内容只要是有一个有内容的,则需要进行弹窗提示是否保存为草稿
		if(_this.richText || _this.detail.title || _this.detail.secondTitle || _this.detail.imageUrl){
			uni.showModal({
			    title: '是否保存为草稿?',
			    content: '',
				confirmText: "保存草稿",
				confirmColor: "#FF7424",
			    success: res=> {
			        if (res.confirm) {
			           this.$request.urlRequest( // 调用保存草稿的接口
			               "/goods/Academy/saveDraft", 
			           	{
			           		academyCategoryId:this.dataList[this.index].id,
			           		essay:this.richText.html,
			           		imageUrl:this.detail.imageUrl,
			           		secondTitle:this.detail.secondTitle,
			           		title:this.detail.title
			           	},
			               'post',
			               (res)=> {
			           		console.log(res);
			           		if(res.code==200){
			           			uni.showToast({
			           				title:"草稿保存成功"
			           			})
			           			setTimeout(()=>{
			           				uni.redirectTo({
			           					url:"/pages/school/articleList?status=1"
			           				})
			           				// 在监听页面返回的功能中,只有遇到了return true才算是返回成功
									return true;
			           			},1500)
			           		}
			           	})
			        }else{
						uni.navigateBack();
						return true;
					}
			    }
			});
			 return true;
		}
		uni.navigateBack();
      return true;
    }
},
methods:{
	// 监听富文本框组件的准备工作
	onEditorReady(e) {
		uni.createSelectorQuery()
			.in(this)
			.select('.ql-container')
			.fields({
				size: true,
				context: true
			}, res => {
				this.editorCtx = res.context;
				this.editorCtx.setContents({
					html: this.detail.essay
				})
			})
			.exec();
	},
	// 富文本框组件的重做
	undo() {
		this.editorCtx.undo();
	},
	// 插入图片
	async chooseImage(type) {
		let _this = this;
		console.log(_this.editorCtx);
		uni.chooseImage({
			count: 1, //最多可以选择的图片张数,默认9
			sizeType: ['original', 'compressed'], //original 原图,compressed 压缩图,默认二者都有
			sourceType: ['album', 'camera'], //album 从相册选图,camera 使用相机,默认二者都有
			success: (respone) => {
				_this.$request.urlRequest(
					'/gate/oss/token', {},
					'GET',
					(res) => {
						if (res.code == 200) {
						let data = res.result;
						let env = {
							uploadImageUrl: 'https://58d.oss-cn-hangzhou.aliyuncs.com/', // 默认存在根目录,可根据需求改
							AccessKeySecret: data.AccessKeySecret, // AccessKeySecret 去你的阿里云上控制台上找
							OSSAccessKeyId: data.AccessKeyId, // AccessKeyId 去你的阿里云上控制台上找
							stsToken: data.SecurityToken,
							timeout: 87600 //这个是上传文件时Policy的失效时间
						}
						let dir = 'images/';
						let filePath = respone.tempFilePaths[0];
						const aliyunFileKey = dir + new Date().getTime() + Math.floor(Math.random() * 150) + '.png';
						const aliyunServerURL = env.uploadImageUrl; //OSS地址,需要https
						const accessid = env.OSSAccessKeyId;
						const policyBase64 = _this.getPolicyBase64(env);
						const signature = _this.getSignature(policyBase64, env); //获取签名
						const stsToken = env.stsToken;

						let param = {
							'key': aliyunFileKey,
							'policy': policyBase64,
							'OSSAccessKeyId': accessid,
							'signature': signature,
							'success_action_status': '200',
							'x-oss-security-token': stsToken,
							'stsToken': stsToken,
						};

						uni.uploadFile({
							url: "https://58d.oss-cn-hangzhou.aliyuncs.com/", //开发者服务器 url
							filePath: filePath, //要上传文件资源的路径
							name: 'file', //必须填file
							formData: param,
							success: (res) => {
								if(type){
									_this.detail.imageUrl = aliyunServerURL + aliyunFileKey;
									return;
								}
								_this.editorCtx.insertImage({
									src: aliyunServerURL + aliyunFileKey, // 此处需要将图片地址切换成服务器返回的真实图片地址
									alt: '图片',
									width:"320upx",
									mode:'widthFix',
									success: function(e) {}
								});
							},
							fail: (err) => {
								// _this.msg.push(JSON.stringify(err));
								// err.wxaddinfo = aliyunServerURL;
								// failc(err);
							},
						})
					}
				}
			)
		}
	});
},
// 阿里OSS存储的格式转化
getSignature(policyBase64, env) {
	const accesskey = env.AccessKeySecret;
	const bytes = Crypto.HMAC(Crypto.SHA1, policyBase64, accesskey, {
		asBytes: true
	});
	const signature = Crypto.util.bytesToBase64(bytes);
	return signature;
},
getPolicyBase64(env) {
	let date = new Date();
	date.setHours(date.getHours() + env.timeout);
	let srcT = date.toISOString();
	const policyText = {
		"expiration": srcT, //设置该Policy的失效时间,超过这个失效时间之后,就没有办法通过这个policy上传文件了
		"conditions": [
			["content-length-range", 0, 5 * 1024 * 1024] // 设置上传文件的大小限制,5mb
		]
	};
	const policyBase64 = base64.encode(JSON.stringify(policyText));
	return policyBase64;
},
// 富文本框组件插入分割线
insertDivider() {
	this.editorCtx.insertDivider();
},
redo() {
	this.editorCtx.redo();
},
// 富文本框组件菜单显示更多
showMore() {
	this.showMoreTool = !this.showMoreTool;
	this.editorCtx.setContents()
},
// 富文本框内容加粗
setBold() {
	this.showBold = !this.showBold;
	this.editorCtx.format('bold');
},
// 富文本框内容斜体展示
setItalic() {
	this.showItalic = !this.showItalic;
	this.editorCtx.format('italic');
},
// 富文本框组件检测状态
checkStatus(name, detail, obj) {
	if (detail.hasOwnProperty(name)) {
		this[obj] = true;
	} else {
		this[obj] = false;
	}
},
// 富文本框组件菜单的改变
statuschange(e) {
	var detail = e.detail;
	this.checkStatus('bold', detail, 'showBold');
	this.checkStatus('italic', detail, 'showItalic');
	this.checkStatus('ins', detail, 'showIns');
	this.checkStatus('header', detail, 'showHeader');
	if (detail.hasOwnProperty('align')) {
		if (detail.align == 'center') {
			this.showCenter = true;
			this.showRight = false;
		} else if (detail.align == 'right') {
			this.showCenter = false;
			this.showRight = true;
		} else {
			this.showCenter = false;
			this.showRight = false;
		}
	} else {
		this.showCenter = false;
		this.showRight = false;
	}
},
// 富文本框
setIns() {
	this.showIns = !this.showIns;
	this.editorCtx.format('ins');
},
// 富文本框组件将文字变成标题文字
setHeader() {
	this.showHeader = !this.showHeader;
	this.editorCtx.format('header', this.showHeader ? 'H2' : false);
},
// 富文本框组件内容居中展示
setCenter() {
	this.showCenter = !this.showCenter;
	this.editorCtx.format('align', this.showCenter ? 'center' : false);
},
// 富文本框组件内容靠右展示
setRight() {
	this.showRight = !this.showRight;
	this.editorCtx.format('align', this.showRight ? 'right' : false);
},
// 获取文章分类的接口调用
getList(){
	this.$request.urlRequest(
	    "/goods/AcademyCategory/list", 
		{},
	    'get',
	    (res)=> {
			if(res.code==200){
				if(!res.result){
					return;
				}
				this.dataList = res.result;
				this.dataList.forEach(item=>{
					this.array.push(item.academyCategoryName);
				})
			}
		}
	)
},
// 提交信息
showSetting() {
	if(!this.detail.title || !this.detail.imageUrl){
		uni.showToast({
			title:"请补全信息",
			icon:"none"
		})
		return;
	}
	// #ifdef APP-PLUS
	this.editorCtx.getContents({
		success: (data)=> {  
			this.richText = data;
			uni.showModal({
				title: '确定提交吗?',
				content: '提交后将进入审核阶段',
				confirmText: "确定提交",
				confirmColor: "#FF7424",
				success: res=> {
					if (res.confirm) {
						if(this.id){
							this.$request.urlRequest(
							    "/goods/Academy/reEditAcademy", 
								{
									id:this.id,
									academyCategoryId:this.dataList[this.index].id,
									essay:this.richText.html,
									imageUrl:this.detail.imageUrl,
									secondTitle:this.detail.secondTitle,
									title:this.detail.title
								},
							    'post',
							    (res)=> {
									console.log(res);
									if(res.code==200){
										uni.showToast({
											title:"提交成功"
										})
										setTimeout(()=>{
											uni.redirectTo({
												url:"/pages/school/articleList?status=2"
											})
										},1500)
									}
								}
							)
						}else{
							this.$request.urlRequest(
							    "/goods/Academy/publishAcademy", 
								{
									academyCategoryId:this.dataList[this.index].id,
									essay:this.richText.html,
									imageUrl:this.detail.imageUrl,
									secondTitle:this.detail.secondTitle,
									title:this.detail.title
								},
							    'post',
							    (res)=> {
									console.log(res);
									if(res.code==200){
										uni.showToast({
											title:"提交成功"
										})
										setTimeout(()=>{
											uni.redirectTo({
												url:"/pages/school/articleList?status=2"
											})
										},1500)
									}
								}
							)
						}
					} 
				}
			});
		}  
	})  
	// #endif
},
	// 富文本框组件添加光标
	async editFocus() {
		this.keyboardFlag = true;
	},
	// 富文本框组件移除光标
	editBlur() {
		this.keyboardFlag = false;
	},
	release(isPublic) {
		this.showSettingLayer = false;
		this.editorCtx.getContents({
			success: res => {
				Object.assign(res, {
					isPublic: isPublic
				})
				this.$emit('editOk', res);
			}
		})
	},
}
}