当前位置: 首页 > news >正文

component-富文本实现(WangEditor)

1.富文本

        富文本是指的是在文本内容中嵌入格式,样式,图像,链接等多媒体元素的文本格式。

2.WangEditor开源富文本编辑器

        开源的富文本编辑器,在vue3前端项目中的引入如下

npm install @wangeditor/editor @wangeditor/editor-for-vue

对应的官网如下

https://www.wangeditor.com/https://www.wangeditor.com/

3.应用与引入

        3.1父组件
​
<template><div class="header"><div class="header-title">父组件</div><divv-if="!isEditing"class="edit-button"@click="handleEdit">编辑</div><divv-if="isEditing"class="edit-button"@click="handleSave">保存</div><divv-if="isEditing"class="edit-button-cancel"@click="handCancel">取消</div></div><el-divider /><divv-if="!isEditing"class="content"><RichContentv-model="richTextContent":readonly="true":show-toolbar="false"/></div><!-- 编辑状态 --><divv-elseclass="content"><RichContentv-model="tempRichText":readonly="false"/></div>
</template>
<script setup lang="tsx">
import { onMounted, ref, watch } from 'vue'
import RichContent from '@/views/digital-matrix/components/RichContent.vue'
import { ElMessage, ElMessageBox } from 'element-plus'
const isEditing = ref(false)
// const richContent = ref()
//  存储最终保存的富文本内容(HTML 格式)
const richTextContent = ref('')
const id = ref(null)
const system_link = ref(null)
//  编辑时的临时内容(避免未保存就修改原内容)
const tempRichText = ref('')
const handleEdit = () => {isEditing.value = truetempRichText.value = richTextContent.value
}watch(() => tempRichText,(newValue) => {console.log('富文本标签', newValue)}
)const handleSave = async () => {try {await ElMessageBox.confirm(`确定保存并覆盖原来内容`, '', {confirmButtonText: '确定',cancelButtonText: '取消',type: 'warning'})// await deleteMaterialApi(row.id)if (!tempRichText.value.trim()) {ElMessage.warning('请输入内容后再保存')} else {console.log('富文本标签', tempRichText.value)richTextContent.value = tempRichText.value// await saveRichTextApi({//   type: 'ORG',//   content: richTextContent.value,//   id: id.value// })isEditing.value = false}ElMessage.success('修改成功')} catch (error) {if (error !== 'cancel') {ElMessage.error('修改失败')}}
}const handCancel = () => {tempRichText.value = ''isEditing.value = false
}// 图片上传接口,返回图片url
const handleImageUpload = async (file: File): Promise<string> => {// 自定义图片上传逻辑return 'https://example.com/image.jpg'
}onMounted(async () => {// await getRichTextApi({ type: 'ORG' }).then((response) => {//   console.log('~~~~~~~~~~~~~~response', response)//   richTextContent.value = response.data.content || '<p>暂无内容</p >'//   id.value = response.data.id || null//   system_link.value = response.data.system_link || null// })
})
</script>
<style lang="scss" scoped>
@use '@/styles/mixins' as *;@function vh($px) {@return calc($px / 1080) * 100vh;
}.header {display: flex;gap: 12px;&-title {@include text-style(var(--font-20), var(--el-font-family-bold), rgba(255, 255, 255, 1));}&-href {cursor: pointer;user-select: none;@include text-style(var(--font-20), var(--el-font-family-bold), #409eff);}
}.edit-button {@include panel;min-width: 80px;height: vh(32);margin-left: auto;line-height: vh(32);text-align: center;cursor: pointer;background: rgb(79 172 254 / 50%) !important;border: 1px solid #409eff !important;box-shadow: inset 0 0 20px 1px #0093f2 !important;@include text-style(var(--font-16), var(--el-font-family-regular), #fff);
}.edit-button-cancel {@include panel;min-width: 80px;height: vh(32);margin-left: 12px;line-height: vh(32);text-align: center;cursor: pointer;background: rgb(79 172 254 / 50%) !important;border: 1px solid #409eff !important;box-shadow: inset 0 0 20px 1px #0093f2 !important;@include text-style(var(--font-16), var(--el-font-family-regular), #fff);
}.content {display: flex;flex: 1;gap: 12px;padding: 12px;overflow: hidden;
}</style>​
        3.2 富文本子组件
<template><div class="rich-text-editor"><Toolbarv-if="showToolbar"class="toolbar":editor="editorRef":default-config="toolbarConfig":mode="mode"/><Editorv-model="valueHtml"class="editor":default-config="editorConfig":mode="mode"@on-created="handleCreated"@on-change="handleChange"/></div>
</template><script setup lang="ts">
import { onBeforeUnmount, ref, shallowRef, watch, nextTick } from 'vue'
import { Editor, Toolbar } from '@wangeditor/editor-for-vue'
import type { IDomEditor, IEditorConfig } from '@wangeditor/editor'
import '@wangeditor/editor/dist/css/style.css'// 定义 Props
interface Props {modelValue?: stringplaceholder?: stringreadonly?: booleanshowToolbar?: booleanuploadImage?: (file: File) => Promise<string>
}const props = withDefaults(defineProps<Props>(), {modelValue: '',placeholder: '请输入内容...',readonly: false,showToolbar: true,uploadImage: undefined
})// 定义 Emits
const emit = defineEmits<{'update:modelValue': [value: string]change: [value: string]created: [editor: IDomEditor]
}>()// 编辑器实例
const editorRef = shallowRef<IDomEditor>()
const valueHtml = ref(props.modelValue)
const mode = 'default'// 监听外部值变化
watch(() => props.modelValue,(newValue) => {if (newValue !== valueHtml.value) {valueHtml.value = newValue}}
)// 监听内部值变化
watch(valueHtml, (newValue) => {emit('update:modelValue', newValue)emit('change', newValue)
})// 工具栏配置
const toolbarConfig = {excludeKeys: ['group-video', 'fullScreen', 'insertTable']
}// 编辑器配置
const editorConfig: Partial<IEditorConfig> = {placeholder: props.placeholder,readOnly: props.readonly,MENU_CONF: {uploadImage: {allowedFileTypes: ['image/*'],maxFileSize: 10 * 1024 * 1024,maxNumberOfFiles: 10,async customUpload(file: File, insertFn: (url: string) => void) {try {if (props.uploadImage) {// 使用自定义上传函数const imageUrl = await props.uploadImage(file)insertFn(imageUrl)} else {// 默认使用本地预览const imageUrl = URL.createObjectURL(file)insertFn(imageUrl)}} catch (err) {console.error('图片上传失败', err)throw err}}}}
}// 编辑器创建回调
const handleCreated = (editor: IDomEditor) => {editorRef.value = editoremit('created', editor)
}// 内容变化回调
const handleChange = (editor: IDomEditor) => {// 内容变化已经在 watch 中处理
}// 设置只读状态
const setReadonly = (readonly: boolean) => {if (editorRef.value) {if (readonly) {editorRef.value.disable()} else {editorRef.value.enable()}}
}// 销毁编辑器
onBeforeUnmount(() => {const editor = editorRef.valueif (editor) {editor.destroy()}
})// 暴露方法给父组件
defineExpose({getEditor: () => editorRef.value,setReadonly,clear: () => {valueHtml.value = ''},getContent: () => valueHtml.value,setContent: (content: string) => {valueHtml.value = content}
})
</script><style scoped lang="scss">
.rich-text-editor {flex: 1;overflow: hidden;border-radius: 4px;// border: 1px solid #fff;.toolbar {border-bottom: 1px solid #dcdfe6;}.editor {flex: 1;overflow-y: auto;}
}// 编辑器样式调整
:deep(.w-e-text-container) {height: 100% !important;// 富文本默认颜色color: #fff;background: transparent;// 代码块pre > code {background-color: rgb(0 0 0 / 40%);}h1 {font-size: 36px;}h2 {font-size: 30px;}h3 {font-size: 26px;}h4 {font-size: 20px;}
}// toolBar样式
:deep(.w-e-bar) {background-color: transparent;svg {fill: #fff;}
}:deep(.w-e-bar-item) {color: #fff;
}// 滚动条
:deep(.w-e-scroll) {&::-webkit-scrollbar {width: 2px;background: transparent;}&::-webkit-scrollbar-track {background: rgb(54 148 255 / 20%);border-radius: 2px;}&::-webkit-scrollbar-thumb {background: #3694ff;border-radius: 2px;}
}:deep(.w-e-bar-item button) {color: inherit;
}:deep(.w-e-bar-item .active) {background-color: rgba($color: #a4bcff, $alpha: 20%);
}// 外圈
:deep(.w-e-bar-item:hover) {background-color: rgba($color: #a4bcff, $alpha: 20%);
}// 内圈
:deep(.w-e-bar-item button:hover) {background-color: transparent;
}:deep(.w-e-bar-item.active) {color: #409eff;background-color: #ecf5ff;
}// 下箭头呼出的容器
:deep(.w-e-drop-panel) {background: transparent;
}:deep(.w-e-select-list) {background: transparent;&::-webkit-scrollbar {width: 2px;background: transparent;}&::-webkit-scrollbar-track {background: rgb(54 148 255 / 20%);border-radius: 2px;}&::-webkit-scrollbar-thumb {background: #3694ff;border-radius: 2px;}
}:deep(.w-e-select-list ul li:hover) {background-color: rgba($color: #a4bcff, $alpha: 20%);
}:deep(.w-e-select-list ul .selected) {background: transparent;
}:deep(.w-e-bar-item-group .w-e-bar-item-menus-container) {background: transparent;
}
</style>

http://www.dtcms.com/a/618303.html

相关文章:

  • 烟台城乡住房建设厅网站网站alt标签
  • win11上使用Workbench备份mysql数据库
  • B站评论数据采集:基于Requests的智能爬虫实战
  • 信息学与容斥
  • 网易云音乐评论数据采集:基于Requests的智能爬虫实战
  • 网站空间登录网站建设模式有哪些内容
  • VSCode 中快捷键的使用:(大小写转换快捷键、自动补全函数注释快捷键、代码和注释自动缩进快捷键)
  • 使用 Python 语言 从 0 到 1 搭建完整 Web UI自动化测试学习系列 25--数据驱动--参数化处理 Excel 文件 2
  • SpringCloud微服务笔记
  • 广告公司网站官网安徽网站建设流程
  • 华为OD机试真题2025双机位A卷 --【压缩日志查询】(Python C++ JAVA JS GO)
  • 网站编辑怎么做内容分类手机网站 程序
  • 瑞安建设网站成都vr 网站开发
  • C++多线程【数据共享】之互斥锁
  • Java漏洞集合工具
  • JavaScript 正则表达式详解
  • 【CS创世SD NAND征文】高可靠性数控设备:技术方案与行业展望
  • 深入理解Go语言Slice的append操作:从内存分配到扩容机制
  • Linux---文件控制<fcntl.h> (file control, fcntl)
  • 网站放到服务器珠海市 网站建设
  • 农林科技公司网站模板seo研究中心官网
  • 东莞响应式网站哪家好架设网站开发环境
  • 类似淘宝网站建设有哪些模板wordpress文章图片全屏浏览
  • 技术演进中的开发沉思-194 JavaScript: Prototype 框架
  • Windows MongoDB 安装与配置指南
  • Kafka客户端整合
  • 购物网站建设方案手机建立网站的软件
  • 力扣hot100----1day
  • 二叉树的前序遍历解题思路
  • python手写数字识别计分系统+CNN模型+YOLOv5模型 深度学习 计算机毕业设计(建议收藏)✅