Taro使用 微信小程序 副文本编辑器

Taro使用 微信小程序 副文本编辑器_第1张图片

一、 引入 小程序富文本编辑器

Taro把一些小程序的原生组件给抽成了插件包括但不限于: editor/page-container,所以直接在代码中写的话是不会被渲染出来的,得经过下面的操作才可以。

二、文件列表

  • HgEditor.vue
  • HgEditor.less
  • iconfont.less
  • editorField.vue // 单独封装的富文本表单组件
  • addGoods.vue // 使用组件的页面文件

1. HgEditor.vue

<template>
	<view class="editor-box">
		<view
			v-if="showTabBar"
			class="editor-box-header"
		>
			<view
				class="operate-box"
				@tap="_addImage"
			>
				<text class="iconfont icon-image" />
			view>
			<view
				class="operate-box"
				@tap="_format('italic')"
			>
				<text class="iconfont icon-italic" />
			view>
			<view
				class="operate-box"
				@tap="_format('bold')"
			>
				<text class="iconfont icon-bold" />
			view>
			<view
				class="operate-box"
				@tap="_format('header', 'h1')"
			>
				<text class="iconfont icon-h1" />
			view>
			<view
				class="operate-box"
				@tap="_format('header', 'h2')"
			>
				<text class="iconfont icon-h2" />
			view>
			<view
				class="operate-box"
				@tap="_format('header', 'h3')"
			>
				<text class="iconfont icon-h3" />
			view>
			<view
				class="operate-box"
				@tap="_format('align', 'left')"
			>
				<text class="iconfont icon-alignLeft" />
			view>
			<view
				class="operate-box"
				@tap="_format('align', 'right')"
			>
				<text class="iconfont icon-alignRight" />
			view>
			<view
				class="operate-box"
				@tap="_format('list', 'ordered')"
			>
				<text class="iconfont icon-orderedList" />
			view>
			<view
				class="operate-box"
				@tap="_format('list', 'bullet')"
			>
				<text class="iconfont icon-unorderedList" />
			view>
			<view
				class="operate-box"
				@tap="_undo"
			>
				<text class="iconfont icon-undo" />
			view>
		view>
		<view class="editor-box-content">
			<Editor
				id="editor"
				class="fz-28 editor"
				:name="name"
				:value="editorValue"
				:placeholder="placeholder"
				:showImgResize="true"
				:show-img-toolbar="true"
				:show-img-resize="true"
				@Blur="blur"
				@Ready="_onEditorReady"
				@Input="_onInputting"
			/>
		view>
	view>
template>
<script>
import Taro from '@tarojs/taro';
import { Editor } from '@tarojs/components';
import request from '@/apis/public';
import './HgEditor.less';
export default {
	name: 'HgEditor',
	components: { Editor },
	props: {
		/** 是否显示工具栏 */
		showTabBar: {
			type: Boolean,
			value: true
		},
		placeholder: {
			type: String,
			value: '请输入图文详情'
		},
		name: {
			type: String,
			value: ''
		},
		editorValue: {
			type: String,
			value: ''
		}
	},
	data () {
		return {
			editorCtx: null
		};
	},
	methods: {
		blur () {
			this.editorCtx.blur();
		},
		_onEditorReady () {
			Taro.createSelectorQuery().select('#editor').context(res => {
				this.editorCtx = res.context;
				const that = this;
				setTimeout(() => {
					if (that.editorValue) {
						res.context.setContents({
							html: that.editorValue
						});
					} else {
						res.context.setContents({
							html: ''
						});
					}
					that.editorCtx.blur();
					Taro.pageScrollTo({
						scrollTop: 0,
						duration: 0
					});
				}, 100);
			}).exec();
		},
		// 插入图片
		_addImage (event) {
			Taro.chooseImage({
				count: 1,
				sizeType: ['compressed'],
				sourceType: ['album'],
				success: (res) => {
					Taro.showLoading({
						title: '上传中',
						mask: true
					});
					this._uploadImage(res.tempFilePaths[0]);
				}
			});
		},
		_uploadImage (tempFilePath) {
			request.uploadFileCommon('/common/img', tempFilePath, 'imgs', this.wxToken)
				.then(res => {
					if (res.result_code === 1) {
						this.editorCtx.insertImage({
							src: res.data[0]
						});
					} else {
						Taro.showToast({
							icon: 'error',
							title: '上传失败',
							mask: true
						});
					}
					Taro.hideLoading();
				})
				.catch(e => {
					Taro.showToast({
						title: '上传失败',
						icon: 'none',
						duration: 1000 // 持续的时间
					});
					Taro.hideLoading();
				});
		},
		_format (type, val) {
			this.editorCtx.format(type, val);
		},
		// 撤销
		_undo () {
			this.editorCtx.undo();
		},
		// 监控输入
		_onInputting (e) {
			const html = e.detail.html;
			// const text = e.detail.text;
			this.$emit('input', html);
		}
	}
};
script>

2. HgEditor.less

/* components/hg-editor/hg-editor.wxss */
@import "./iconfont.less";

.editor-box {
	width: 100%;
	padding: 0;
	box-sizing: border-box;
	background-color: #fff;
	background: #F7F7F7;
}

.editor-box-header,
.editor-box-content {
  	width: 100%;
}

.editor-box-header {
	display: flex;
	flex-flow: row nowrap;
	align-items: center;
	justify-content: space-between;
	padding: 20rpx 20rpx;
	box-sizing: border-box;
	border-bottom: 2rpx solid #E6E6E6;
	background-color: #F6F6F6;
}

.editor-box-header>.operate-box {
	width: 40rpx;
	height: 40rpx;
	overflow: hidden;
	color: gray;
}

.editor-box-content {
	padding: 20rpx;
	box-sizing: border-box;
	background: #F7F7F7;
	.editor {
		height: auto;
		margin-top: 0;
	}
}

3. iconfont.less

@font-face {
  font-family: 'iconfont';  /* Project id 2549449 */
  src: url('//at.alicdn.com/t/font_2549449_hxmflg4qsr6.woff2?t=1621002720450') format('woff2'),
       url('//at.alicdn.com/t/font_2549449_hxmflg4qsr6.woff?t=1621002720450') format('woff'),
       url('//at.alicdn.com/t/font_2549449_hxmflg4qsr6.ttf?t=1621002720450') format('truetype');
}

.iconfont {
  font-family: "iconfont" !important;
  font-size: 38rpx;
  font-style: normal;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

.icon-undo:before {
  content: "\e609";
}

.icon-hr:before {
  content: "\e60a";
}

.icon-h3:before {
  content: "\e60b";
}

.icon-quote:before {
  content: "\e60c";
}

.icon-bold:before {
  content: "\e60e";
}

.icon-orderedList:before {
  content: "\e612";
}

.icon-h2:before {
  content: "\e61a";
}

.icon-italic:before {
  content: "\e61c";
}

.icon-unorderedList:before {
  content: "\e620";
}

.icon-alignLeft:before {
  content: "\e621";
}

.icon-alignRight:before {
  content: "\e622";
}

.icon-h1:before {
  content: "\e623";
}

.icon-image:before {
  content: "\e629";
}

4. editorField.vue

<template>
	<view class="form-item  no-border">
		<text class="label">
			{{ label }}
		text>
		<view class="ql-container">
			<HgEditor
				ref="hgEditor"
				:showTabBar="true"
				:editorValue="value"
				:placeholder="placeholder"
				:name="prop"
				@input="handleInput"
			/>
		view>
	view>
template>
<script>
import HgEditor from '../HgEditor.vue';
import './editorField.less';

export default {
	name: 'EditorField',
	components: { HgEditor },
	props: {
		value: [String, Number],
		prop: String,
		label: String,
		placeholder: String,
		type: {
			type: String,
			default: 'text'
		},
		uploadImageURL: {
			type: String,
			value: ''
		},
		maxlength: {
			type: Number,
			default: 30
		},
		disabled: {
			type: Boolean,
			default: false
		}
	},
	methods: {
		handleInput (content) {
			this.$emit('fieldChange', this.prop, content);
		},
		initEditor () {
			this.$refs.hgEditor._onEditorReady();
		}
	}
};
script>

5. editorField.less


.form-item {
	position: relative;
	min-height: 167rpx;
    padding-top: 60rpx;
    padding-bottom: 23rpx;
    box-sizing: border-box;
    border-bottom: 1rpx solid #F0F0F0;
	&.no-border {
		border: none !important;
	}
	.label {
		font-size: 30rpx;
		font-family: PingFangSC-Medium;
		font-weight: 500;
		color: #333333;
	}
	.sub-label {
		margin-left: 10rpx;
		font-size: 22rpx;
		font-family: PingFang SC;
		font-weight: 400;
		color: #CCCCCC;
	}
	.input-text {
		margin-top: 28rpx;
		font-size: 28rpx;
	}
	.textarea {
		width: 100%;
		height: 286px;
		margin-top: 34rpx;
		padding: 35rpx 30rpx;
		box-sizing: border-box;
		font-size: 28rpx;
		background: #F5F6F8;
		border-radius: 20rpx;
	}
	.counter {
		position: absolute;
		bottom: 30rpx;
		right: 35rpx;
		font-size: 22rpx;
		font-family: PingFang SC;
		font-weight: 400;
		color: #CCCCCC;
	}
	.placeholder-class {
		font-size: 28rpx;
		font-family: PingFangSC-Regular;
		font-weight: 400;
		color: #CCCCCC;
	}
	.picker {
		position: relative;
		.arrow {
			width: 15rpx;
			height: 26rpx;
			position: absolute;
			right: 0;
			bottom: 14rpx;
		}
	}
	.upload-container {
		margin-top: 30rpx;
		flex-wrap: wrap;
		.ui_uploader_item {
			position: relative;
			width: 196rpx;
			height: 196rpx;
			margin-right: 20rpx;
			margin-bottom: 20rpx;
			border-radius: 10rpx;
			.ui_uploader_item_icon {
				position: absolute;
				right: -20rpx;
				top: -20rpx;
				background: #fff;
				border-radius: 50%;
			}
			image {
				width: 196rpx;
				height: 196rpx;
			}
		}
		.ui_uploader {
			position: relative;
			margin: 20rpx 0;
			margin-top: 0;
			margin-right: 20rpx;
			width: 196rpx;
			height: 196rpx;
			box-sizing: border-box;
			background: #F7F7F7;
			border-radius: 10rpx;
			image {
				width: 57rpx;
				height: 57rpx;
			}
		}
	}
	.ql-container {
		height: auto;
		margin-top: 34rpx;
		border-radius: 20rpx;
	}
}

6. addGoods.vue

<template>
	<EditorField
		ref="editorField"
		:value="formData.content"
		prop="content"
		label="商品详情"
		placeholder="请输入商品详情"
		@fieldChange="fieldChangeHandle"
	/>
template>
<script>
	export default {
		data () {
			return {
				formData: {
					id: '',
					content: ''
				}
			};
		},
		methods: {
			fieldChangeHandle (field, val) {
				this.formData[field] = val;
			}
		}
	}
script>

你可能感兴趣的:(H5,微信小程序,taro)