<!--
* Copyright (c) Huawei Technologies Co., Ltd. 2024-2024. All rights reserved.
* @Description: 富文本组件
-->
<template>
<div :style="{ '--height': `${setHeight + 100}px` }" class="rich-text-editor-wrapper">
<Editor
v-model="richContent"
class="editor"
:editor-config="editorConfig"
:show-count="false"
:extensions="definePluginConfig({ toolbar: ToolbarConfig, shortcutMenu: [] })"
resize="vertical"
@focus="onFocus"
@blur="onBlur"
/>
</div>
</template>
<script setup>
import { debounce } from '@plmcsdk/js-utility-library';
import { Editor, definePluginConfig, defaultToolbarConfig } from '@plmcsdk/rich-text-editor-starter';
import { computed, defineExpose } from 'vue';
import { t } from '@/entry/i18n/index';
import tinyEditorErrorImg from '@/shared/assets/imgs/tinyEditorErrImg.svg';
const { hdpCommon, ajax } = window.vue;
// 屏蔽的功能
const EXCLUDE_TOOL_BAR = ['Emoji', 'Video', 'Audio', 'File', 'TaskList', 'Codeblock'];
const ToolbarConfig = defaultToolbarConfig.map(subArr => {
return subArr.filter(item => {
return !EXCLUDE_TOOL_BAR.includes(item.name);
});
});
const FILE_PREFIX = 'file:///';
const props = defineProps({
modelValue: {
type: String,
default: '',
},
setHeight: {
type: Number,
default: 400,
},
// 设置支持图片格式,默认jpg,png,gif
setFormat: {
type: Array,
default: () => ['image/jpeg', 'image/png', 'image/gif'],
},
// 文件大小,,默认不限制,无穷大
fileSize: {
type: Number,
default: Number.POSITIVE_INFINITY,
},
// 上传图片数量,默认不限制,无穷大
imgCount: {
type: Number,
default: Number.POSITIVE_INFINITY,
},
});
const emits = defineEmits(['input', 'update:modelValue', 'getContent', 'focus', 'blur', 'verifyingImgUpload']);
const richContent = computed({
get() {
return props.modelValue;
},
set: debounce(val => {
// 解决复制本地多张图片显示异常的问题
const localImgReg = new RegExp(`<[^>]+?${FILE_PREFIX}[^>]+?>`, 'g');
if (localImgReg.test(val)) {
const defaultErrorImg = `<img src="${tinyEditorErrorImg}">`;
const content = val?.replaceAll(localImgReg, defaultErrorImg);
onInput(content);
return;
}
onInput(val);
}, 100),
});
function onInput(val) {
emits('input', val);
emits('update:modelValue', val);
}
// 点击了提交富文本
function submit() {
let content = richContent.value;
content = content.replace(/src="http:/g, 'src="'); // 兼容http图片不显示的问题
emits('getContent', window.vue.$xss.process(content));
}
/**
*
* @param blob 文件对象
* @param type image | video | audio | file
*/
const upload = (fileParam, type) => {
const { name, fileType, file: blob } = fileParam;
if (type === 'image') {
if (!props.setFormat.includes(fileType)) {
return new Promise((resolve, reject) => {
hdpCommon.setNotice(t('HDP.TASK.RICH_TEXT_UPLOAD_FORMAT_TIP'), 'warning');
reject(t('HDP.TASK.RICH_TEXT_UPLOAD_FORMAT_TIP'));
});
}
emits('verifyingImgUpload', true);
// 不支持的图片格式
// 判断上传图片大小
const limtFileSize = blob.size / 1024 / 1024 < props.fileSize;
if (!limtFileSize) {
hdpCommon.setNotice(t('HDP.CMS.TIPS.UPLOAD_FILE_SIZE_LIMIT', { size: props.fileSize }), 'error');
return;
}
// 判断上传图片张数
const imgRegex = /<img.*?>/g;
const imgTags = richContent.value.match(imgRegex);
if (imgTags?.length >= props.imgCount) {
hdpCommon.setNotice(t('HDP.CMS.TIPS.UPLOAD_PHOTO_COUNT_LIMIT', { count: props.imgCount }), 'error');
return;
}
return uploadImageToS3(blob);
}
return new Promise((resolve, reject) => {
hdpCommon.setNotice(t('HDP.TASK.RICH_TEXT_UPLOAD_FORMAT_TIP'), 'warning');
reject(t('HDP.TASK.RICH_TEXT_UPLOAD_FORMAT_TIP'));
});
};
// 图片上传到S3
function uploadImageToS3(file) {
const formData = new FormData();
formData.append('upload', file, file.name); // 此处与源文档不一样
const url = 'service/cfg/services/file/uploadAttachment?type=card';
return ajax.post(url, formData).then(
handleUploadResponse,
// 接口报错,需要抛出错误信息、由富文本上传组件展示错误提示语
handleUploadError
);
}
/** 处理上传接口的报错: 需要抛出错误信息、由富文本上传组件展示错误提示语 */
function handleUploadError(error) {
const errorMessage = hdpCommon.getErrorMessage(error);
throw errorMessage;
}
/** 处理上传接口的返回值 */
function handleUploadResponse(res) {
if (!res.success) {
// 校验不通过的场景,需要抛出错误信息、由富文本上传组件展示错误提示语
handleUploadError(res);
}
emits('verifyingImgUpload', false);
// 接口正常返回,把已上传图片的url返回给富文本上传组件
return res.response.url;
}
function onFocus(params) {
emits('focus');
}
function onBlur(params) {
emits('blur');
}
const editorConfig = computed(() => {
return {
locale: 'zh',
upload,
contextmenuZIndex: 999,
showFullScreen: true,
};
});
// 编辑,将内容赋值给富文本
function setContent(val) {
val = val === null ? '' : val;
richContent.value = val;
}
// 兼容CK编辑器
function initCkEditor() {}
defineExpose({ submit, setContent, initCkEditor });
</script>
<style lang="less" scoped>
.rich-text-editor-wrapper {
width: 100%;
}
.editor {
height: var(--height);
}
// 覆盖主应用样式,聚焦不显示高亮
:deep([contenteditable='true']:focus) {
outline: none !important;
border: none !important;
}
// 默认行高,修复组件默认行高过小的问题
.rich-text-editor-wrapper :deep(.ipd-editor-content .editor-content p) {
line-height: 1.6;
font-size: 13px;
}
// 修改覆盖默认样式
.rich-text-editor-wrapper :deep(.ipd-editor-content .editor-content) {
table {
th {
background-color: transparent; // 覆盖表头背景颜色
}
}
}
</style>
富文本框绑定的richContent返回的字符串中带了<p></p>这个标签,我想在页面回显时去掉,我该怎么操作呢?