百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程网 > 正文

用来用去还是用回了ueditor-Vue富文本编辑器二次扩展

yuyutoo 2024-10-12 01:13 1 浏览 0 评论

用来用去还是用回了ueditor-Vue富文本编辑器二次扩展。我们使用用过UEditor、TinyMCE、CKEditor、wangEditor、Tiptap、Quill项目经历过多次富文本的编辑器的选型使用,发现现在新的富文本编辑总感觉还是没达到我们的要求,果然改回了ueditor。




UEditor 是由百度WEB前端研发部开发的一款所见即所得的开源富文本编辑器。它具有轻量、可定制、用户体验优秀等特点,并且基于MIT开源协议(也有说法是基于BSD协议),所有源代码在协议允许范围内可自由修改和使用。UEditor的推出,有效降低了网站开发者的开发成本,节约了他们在开发富文本编辑器方面所需的大量时间。

一、系统特点

分层架构设计:UEditor在设计上采用了经典的分层架构设计理念,包括核心层、命令插件层和UI层,以实现功能层次之间的轻度耦合。

核心层:提供编辑器底层的方法和概念,如DOM树操作、Selection、Range等。

命令插件层:所有的功能型实现都通过这一层中的命令和插件来完成,各个命令和插件之间基本互不耦合,方便定制与扩展。

UI层:与核心层和命令插件层几乎完全解耦,简单的配置即可为编辑器在界面上添加额外的UI元素和功能。

二、优点

体积小巧,性能优良:UEditor在保证功能全面的同时,尽量减少了体积,提升了性能。

使用简单:对于开发者来说,UEditor易于上手,可以快速集成到项目中。

多浏览器支持:支持Mozilla、MSIE、FireFox、Maxthon、Safari和Chrome等多种浏览器。

丰富完善的中文文档:为开发者提供了详尽的中文文档,方便学习和使用。

vue-ueditor-wrap 是一个Vue组件,它封装了UEditor(百度富文本编辑器)以便更容易地在Vue项目中集成和使用。UEditor是一个功能强大的富文本编辑器,但直接将其集成到Vue项目中可能会遇到一些兼容性和维护性的问题,而vue-ueditor-wrap正是为了解决这些问题而设计的。

如何使用 vue-ueditor-wrap

安装

首先,你需要通过npm或yarn来安装vue-ueditor-wrap。打开你的终端或命令提示符,然后运行以下命令之一:

npm install vue-ueditor-wrap --save

yarn add vue-ueditor-wrap

扩展组件

接下来,在你的Vue组件中引入vue-ueditor-wrap。

<template>
	<div style="border: 1px solid #ccc">
		<vue-ueditor-wrap
			v-model="data"
			:config="editorConfig"
			:editorDependencies="['ueditor.config.js', 'ueditor.all.js']"
			ref="editorRef"
		></vue-ueditor-wrap>
		<diy-storage ref="storageImg" style="display: none" @confirm="getAttachmentFileList"></diy-storage>
		<diy-storage
			ref="storageVideo"
			type="video"
			accept=".rm,.rmvb,.wmv,.avi,.mpg,.mpeg,.mp4"
			style="display: none"
			@confirm="getAttachmentVideoFileList"
		></diy-storage>
	</div>
</template>

<script setup name="DiyEditor">
import { ref } from 'vue';
import { VueUeditorWrap } from 'vue-ueditor-wrap';
import { useVModel } from '@vueuse/core';
import DiyStorage from '@/components/upload/storage.vue';
import { Session } from '@/utils/storage';
const editorRef = ref();
const storageImg = ref(null);
const storageVideo = ref(null);

const props = defineProps({
	modelValue: {
		type: String,
		default: '',
	},
	height: {
		type: Number,
		default: 300,
	},
});

const emit = defineEmits(['update:modelValue', 'handleBlur']);
const data = useVModel(props, 'modelValue', emit);

const serverHeaders = {
	Authorization: Session.get('token'),
};
const editorConfig = ref({
	debug: false,
	UEDITOR_HOME_URL: import.meta.env.MODE == 'development' ? '/public/ueditor/' : import.meta.env.VITE_BUILD_DIR + '/ueditor/',
	serverUrl: (process.env.NODE_ENV === 'production' ? '' : '/api') + '/sys/storage/upload',
	serverHeaders,
	// 编辑器不自动被内容撑高
	autoHeightEnabled: false,
	// 初始容器高度
	initialFrameHeight: props.height,
	// 初始容器宽度
	initialFrameWidth: '100%',
	toolbarCallback: function (cmd, editor) {
		editorRef.value = editor;
		if (cmd == 'insertimage') {
			storageImg.value.handleStorageDlg('', '上传图片');
			return true;
		} else if (cmd == 'insertvideo') {
			storageVideo.value.handleStorageDlg('', '上传视频');
			return true;
		}
	},
});

const getAttachmentFileList = (files) => {
	if (!files.length) {
		return;
	}
	files.forEach((element) => {
		editorRef.value?.execCommand('insertHtml', "<img src='" + element.url + "'/>");
	});
};

const getAttachmentVideoFileList = (files) => {
	if (!files.length) {
		return;
	}
	files.forEach((element) => {
		editorRef.value?.execCommand('insertHtml', "<video src='" + element.url + "'/>");
	});
};
</script>

通过使用vue-ueditor-wrap,你可以轻松地将UEditor集成到你的Vue项目中,从而为用户提供一个功能丰富的文本编辑体验。

<template>
	<div class="container">
		<div class="el-card is-always-shadow diygw-col-24">
			<div class="el-card__body">
				<div class="mb8">
					<el-button type="primary" plain @click="onOpenAddModule"> <SvgIcon name="ele-Plus" />新增 </el-button> <el-button type="danger" plain :disabled="multiple" @click="onTabelRowDel"> <SvgIcon name="ele-Delete" />删除 </el-button>
				</div>
				<el-table @selection-change="handleSelectionChange" v-loading="loading" :data="tableData" stripe border current-row-key="id" empty-text="没有数据" style="width: 100%">
					<el-table-column type="selection" width="55" align="center"></el-table-column>
					<el-table-column label="姓名" prop="name" align="center"> </el-table-column>
					<el-table-column label="性别" prop="sex" align="center"> </el-table-column>
					<el-table-column label="邮箱" prop="email" align="center"> </el-table-column>
					<el-table-column label="操作" align="center" fixed="right" width="180">
						<template #default="scope">
							<el-button type="primary" plain size="small" @click="onOpenEditModule(scope.row)"> <SvgIcon name="ele-Edit" />修改 </el-button> <el-button v-if="scope.row.id != 0" type="danger" plain size="small" @click="onTabelRowDel(scope.row)"> <SvgIcon name="ele-Delete" />删除 </el-button>
						</template>
					</el-table-column>
				</el-table>
				<!-- 分页设置-->
				<div v-show="total > 0"><el-divider></el-divider> <el-pagination background :total="total" :current-page="queryParams.pageNum" :page-size="queryParams.pageSize" layout="total, sizes, prev, pager, next, jumper" @size-change="handleSizeChange" @current-change="handleCurrentChange" /></div>
			</div>
			<!-- 添加或修改参数配置对话框 -->
			<el-dialog :fullscreen="isFullscreen" width="800px" v-model="isShowDialog" destroy-on-close title="CRUD表格" draggable center>
				<template #header="{ close, titleId, titleClass }">
					<h4 :id="titleId" :class="titleClass">CRUD表格</h4>
					<el-tooltip v-if="!isFullscreen" content="最大化" placement="bottom">
						<el-button class="diygw-max-icon el-dialog__headerbtn">
							<el-icon @click="isFullscreen = !isFullscreen">
								<FullScreen />
							</el-icon>
						</el-button>
					</el-tooltip>
					<el-tooltip v-if="isFullscreen" content="返回" placement="bottom">
						<el-button class="diygw-max-icon el-dialog__headerbtn">
							<el-icon @click="isFullscreen = !isFullscreen">
								<Remove />
							</el-icon>
						</el-button>
					</el-tooltip>
				</template>
				<el-form class="flex flex-direction-row flex-wrap" :model="editForm" :rules="editFormRules" ref="editFormRef" label-width="80px">
					<div class="flex diygw-col-24">
						<el-form-item prop="editor" class="diygw-el-rate" label="多行输入">
							<diy-editor v-model="editForm.editor"></diy-editor>
						</el-form-item>
					</div>
				</el-form>
				<template #footer>
					<div class="dialog-footer flex justify-center"><el-button @click="closeDialog"> 取 消 </el-button> <el-button type="primary" @click="onSubmit" :loading="saveloading"> 保 存 </el-button></div>
				</template>
			</el-dialog>
		</div>
		<div class="clearfix"></div>
	</div>
</template>

<script>
	import { useRouter, useRoute } from 'vue-router';
	import { storeToRefs } from 'pinia';
	import { useUserInfo } from '@/stores/userInfo';

	import { ElMessageBox, ElMessage } from 'element-plus';
	import { deepClone, changeRowToForm } from '@/utils/other';
	import { addData, updateData, delData, listData } from '@/api';

	import DiyEditor from '@/components/editor/index.vue';

	export default {
		components: {
			DiyEditor
		},
		data() {
			return {
				globalOption: {},
				userInfo: {},
				// 遮罩层
				loading: true,
				// 选中数组
				ids: [],
				// 非单个禁用
				single: true,
				// 非多个禁用
				multiple: true,
				// 弹出层标题
				title: '',
				// 总条数
				total: 0,
				tableData: [],
				// 是否显示弹出层
				isFullscreen: false,
				isShowDialog: false,
				saveloading: false,
				editFormInit: {},
				queryParamsInit: {},

				queryParams: {
					pageNum: 1,
					pageSize: 10
				},

				queryParamsRules: {},

				editForm: {
					id: undefined,
					editor: ''
				},

				editFormRules: {}
			};
		},
		methods: {
			getParamData(e, row) {
				row = row || {};
				let dataset = e.currentTarget ? e.currentTarget.dataset : e;
				if (row) {
					dataset = Object.assign(dataset, row);
				}
				return dataset;
			},
			navigateTo(e, row) {
				let dataset = this.getParamData(e, row);
				let { type } = dataset;
				if (type == 'page' || type == 'inner' || type == 'href') {
					this.router.push({
						path: dataset.url,
						query: dataset
					});
				} else if (typeof this[type] == 'function') {
					this[type](dataset);
				} else {
					ElMessage.error('待实现点击事件');
				}
			},
			async init() {}, // 打开弹窗
			openDialog(row) {
				if (row.id && row.id != undefined && row.id != 0) {
					this.editForm = changeRowToForm(row, this.editForm);
				} else {
					this.initForm();
				}
				this.isShowDialog = true;
				this.saveloading = false;
			},

			// 关闭弹窗
			closeDialog(row) {
				this.mittBus.emit('onEditAdmintableModule', row);
				this.isShowDialog = false;
			},

			// 保存
			onSubmit() {
				const formWrap = this.$refs.editFormRef;
				if (!formWrap) return;
				formWrap.validate((valid) => {
					if (valid) {
						this.saveloading = true;
						if (this.editForm.id != undefined && this.editForm.id != 0) {
							updateData('/sys/user', this.editForm)
								.then(() => {
									ElMessage.success('修改成功');
									this.saveloading = false;
									this.closeDialog(this.editForm); // 关闭弹窗
								})
								.finally(() => {
									this.saveloading = false;
								});
						} else {
							addData('/sys/user', this.editForm)
								.then(() => {
									ElMessage.success('新增成功');
									this.saveloading = false;
									this.closeDialog(this.editForm); // 关闭弹窗
								})
								.finally(() => {
									this.saveloading = false;
								});
						}
					} else {
						ElMessage.error('验证未通过!');
					}
				});
			},
			// 表单初始化,方法:`resetFields()` 无法使用
			initForm() {
				this.editForm = deepClone(this.editFormInit);
			},
			/** 查询CRUD表格列表 */
			handleQuery() {
				this.loading = true;
				listData('/sys/user', this.queryParams).then((response) => {
					this.tableData = response.rows;
					this.total = response.total;
					this.loading = false;
				});
			},
			/** 重置按钮操作 */
			resetQuery() {
				this.queryParams = deepClone(this.queryParamsInit);
				this.handleQuery();
			},

			// 打开新增CRUD表格弹窗
			onOpenAddModule(row) {
				row = [];
				this.title = '添加CRUD表格';
				this.openDialog(row);
			},
			// 打开编辑CRUD表格弹窗
			onOpenEditModule(row) {
				this.title = '修改CRUD表格';
				this.initForm();
				this.openDialog(row);
			},
			/** 删除按钮操作 */
			onTabelRowDel(row) {
				let thiz = this;
				const id = row.id || this.ids;
				ElMessageBox({
					message: '是否确认删除选中的CRUD表格?',
					title: '警告',
					showCancelButton: true,
					confirmButtonText: '确定',
					cancelButtonText: '取消',
					type: 'warning'
				}).then(function () {
					return delData('/sys/user', id).then(() => {
						thiz.handleQuery();
						ElMessage.success('删除成功');
					});
				});
			},
			// 多选框选中数据
			handleSelectionChange(selection) {
				this.ids = selection.map((item) => item.id);
				this.single = selection.length != 1;
				this.multiple = !selection.length;
			},

			//分页页面大小发生变化
			handleSizeChange(val) {
				this.queryParams.pageSize = val;
				this.handleQuery();
			},
			//当前页码发生变化
			handleCurrentChange(val) {
				this.queryParams.pageNum = val;
				this.handleQuery();
			}
		},
		async mounted() {
			this.router = useRouter();
			this.globalOption = useRoute().query;
			const stores = useUserInfo();
			const { userInfos } = storeToRefs(stores);
			this.userInfo = userInfos;
			await this.init();
			this.editFormInit = deepClone(this.editForm);
			this.queryParamsInit = deepClone(this.queryParams);
			this.handleQuery();
			this.mittBus.on('onEditAdmintableModule', () => {
				this.handleQuery();
			});
		},
		beforeUnmount() {
			this.mittBus.off('onEditAdmintableModule');
		}
	};
</script>

<style lang="scss" scoped>
	.container {
		font-size: 12px;
	}
</style>

扩展组件代码已开源至https://gitee.com/diygw/diygw-ui-admin

大家可以前往下载

相关推荐

jQuery VS AngularJS 你更钟爱哪个?

在这一次的Web开发教程中,我会尽力解答有关于jQuery和AngularJS的两个非常常见的问题,即jQuery和AngularJS之间的区别是什么?也就是说jQueryVSAngularJS?...

Jquery实时校验,指定长度的「负小数」,小数位未满末尾补0

在可以输入【负小数】的输入框获取到焦点时,移除千位分隔符,在输入数据时,实时校验输入内容是否正确,失去焦点后,添加千位分隔符格式化数字。同时小数位未满时末尾补0。HTML代码...

如何在pbootCMS前台调用自定义表单?pbootCMS自定义调用代码示例

要在pbootCMS前台调用自定义表单,您需要在后台创建表单并为其添加字段,然后在前台模板文件中添加相关代码,如提交按钮和表单验证代码。您还可以自定义表单数据的存储位置、添加文件上传字段、日期选择器、...

编程技巧:Jquery实时验证,指定长度的「负小数」

为了保障【负小数】的正确性,做成了通过Jquery,在用户端,实时验证指定长度的【负小数】的方法。HTML代码<inputtype="text"class="forc...

一篇文章带你用jquery mobile设计颜色拾取器

【一、项目背景】现实生活中,我们经常会遇到配色的问题,这个时候去百度一下RGB表。而RGB表只提供相对于的颜色的RGB值而没有可以验证的模块。我们可以通过jquerymobile去设计颜色的拾取器...

编程技巧:Jquery实时验证,指定长度的「正小数」

为了保障【正小数】的正确性,做成了通过Jquery,在用户端,实时验证指定长度的【正小数】的方法。HTML做成方法<inputtype="text"class="fo...

jquery.validate检查数组全部验证

问题:html中有多个name[],每个参数都要进行验证是否为空,这个时候直接用required:true话,不能全部验证,只要这个数组中有一个有值就可以通过的。解决方法使用addmethod...

Vue进阶(幺叁肆):npm查看包版本信息

第一种方式npmviewjqueryversions这种方式可以查看npm服务器上所有的...

layui中使用lay-verify进行条件校验

一、layui的校验很简单,主要有以下步骤:1.在form表单内加上class="layui-form"2.在提交按钮上加上lay-submit3.在想要校验的标签,加上lay-...

jQuery是什么?如何使用? jquery是什么功能组件

jQuery于2006年1月由JohnResig在BarCampNYC首次发布。它目前由TimmyWilson领导,并由一组开发人员维护。jQuery是一个JavaScript库,它简化了客户...

django框架的表单form的理解和用法-9

表单呈现...

jquery对上传文件的检测判断 jquery实现文件上传

总体思路:在前端使用jquery对上传文件做部分初步的判断,验证通过的文件利用ajaxFileUpload上传到服务器端,并将文件的存储路径保存到数据库。<asp:FileUploadI...

Nodejs之MEAN栈开发(四)-- form验证及图片上传

这一节增加推荐图书的提交和删除功能,来学习node的form提交以及node的图片上传功能。开始之前需要源码同学可以先在git上fork:https://github.com/stoneniqiu/R...

大数据开发基础之JAVA jquery 大数据java实战

上一篇我们讲解了JAVAscript的基础知识、特点及基本语法以及组成及基本用途,本期就给大家带来了JAVAweb的第二个知识点jquery,大数据开发基础之JAVAjquery,这是本篇文章的主要...

推荐四个开源的jQuery可视化表单设计器

jquery开源在线表单拖拉设计器formBuilder(推荐)jQueryformBuilder是一个开源的WEB在线html表单设计器,开发人员可以通过拖拉实现一个可视化的表单。支持表单常用控件...

取消回复欢迎 发表评论: