用来用去还是用回了ueditor-Vue富文本编辑器二次扩展
yuyutoo 2024-10-12 01:13 3 浏览 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
大家可以前往下载
相关推荐
- 史上最全的浏览器兼容性问题和解决方案
-
微信ID:WEB_wysj(点击关注)◎◎◎◎◎◎◎◎◎一┳═┻︻▄(页底留言开放,欢迎来吐槽)●●●...
-
- 平面设计基础知识_平面设计基础知识实验收获与总结
-
CSS构造颜色,背景与图像1.使用span更好的控制文本中局部区域的文本:文本;2.使用display属性提供区块转变:display:inline(是内联的...
-
2025-02-21 16:01 yuyutoo
- 写作排版简单三步就行-工具篇_作文排版模板
-
和我们工作中日常word排版内部交流不同,这篇教程介绍的写作排版主要是用于“微信公众号、头条号”网络展示。写作展现的是我的思考,排版是让写作在网格上更好地展现。在写作上花费时间是有累积复利优势的,在排...
- 写一个2048的游戏_2048小游戏功能实现
-
1.创建HTML文件1.打开一个文本编辑器,例如Notepad++、SublimeText、VisualStudioCode等。2.将以下HTML代码复制并粘贴到文本编辑器中:html...
- 今天你穿“短袖”了吗?青岛最高23℃!接下来几天气温更刺激……
-
最近的天气暖和得让很多小伙伴们喊“热”!!! 昨天的气温到底升得有多高呢?你家有没有榜上有名?...
- CSS不规则卡片,纯CSS制作优惠券样式,CSS实现锯齿样式
-
之前也有写过CSS优惠券样式《CSS3径向渐变实现优惠券波浪造型》,这次再来温习一遍,并且将更为详细的讲解,从布局到具体样式说明,最后定义CSS变量,自定义主题颜色。布局...
- 你的自我界限够强大吗?_你的自我界限够强大吗英文
-
我的结果:A、该设立新的界限...
- 行内元素与块级元素,以及区别_行内元素和块级元素有什么区别?
-
行内元素与块级元素首先,CSS规范规定,每个元素都有display属性,确定该元素的类型,每个元素都有默认的display值,分别为块级(block)、行内(inline)。块级元素:(以下列举比较常...
-
- 让“成都速度”跑得潇潇洒洒,地上地下共享轨交繁华
-
去年的两会期间,习近平总书记在参加人大会议四川代表团审议时,对治蜀兴川提出了明确要求,指明了前行方向,并带来了“祝四川人民的生活越来越安逸”的美好祝福。又是一年...
-
2025-02-21 16:00 yuyutoo
- 今年国家综合性消防救援队伍计划招录消防员15000名
-
记者24日从应急管理部获悉,国家综合性消防救援队伍2023年消防员招录工作已正式启动。今年共计划招录消防员15000名,其中高校应届毕业生5000名、退役士兵5000名、社会青年5000名。本次招录的...
- 一起盘点最新 Chrome v133 的5大主流特性 ?
-
1.CSS的高级attr()方法CSSattr()函数是CSSLevel5中用于检索DOM元素的属性值并将其用于CSS属性值,类似于var()函数替换自定义属性值的方式。...
- 竞走团体世锦赛5月太仓举行 世界冠军杨家玉担任形象大使
-
style="text-align:center;"data-mce-style="text-align:...
- 学物理能做什么?_学物理能做什么 卢昌海
-
作者:曹则贤中国科学院物理研究所原标题:《物理学:ASourceofPowerforMan》在2006年中央电视台《对话》栏目的某期节目中,主持人问过我一个的问题:“学物理的人,如果日后不...
-
- 你不知道的关于这只眯眼兔的6个小秘密
-
在你们忙着给熊本君做表情包的时候,要知道,最先在网络上引起轰动的可是这只脸上只有两条缝的兔子——兔斯基。今年,它更是迎来了自己的10岁生日。①关于德艺双馨“老艺...
-
2025-02-21 16:00 yuyutoo
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- mybatis plus (70)
- scheduledtask (71)
- css滚动条 (60)
- java学生成绩管理系统 (59)
- 结构体数组 (69)
- databasemetadata (64)
- javastatic (68)
- jsp实用教程 (53)
- fontawesome (57)
- widget开发 (57)
- vb net教程 (62)
- hibernate 教程 (63)
- case语句 (57)
- svn连接 (74)
- directoryindex (69)
- session timeout (58)
- textbox换行 (67)
- extension_dir (64)
- linearlayout (58)
- vba高级教程 (75)
- iframe用法 (58)
- sqlparameter (59)
- trim函数 (59)
- flex布局 (63)
- contextloaderlistener (56)