index.js 3.94 KB
// 标识必须独一无二 - 标识是为了使用insertText插入标识文本后,查找到标识所在delta位置的索引
export const linkFlag = '#-*=*-*=*-*=*@-link超链接标识link-@*=*-*=*-*=*-#'

export function addLink(editorCtx, attr, callback) {
	// 先插入一段文本内容
	editorCtx.insertText({
		text: linkFlag
	})
	// 获取全文delta内容
	editorCtx.getContents({
		success(res) {
			let options = res.delta.ops
			const findex = options.findIndex(item => {
				return item.insert && typeof item.insert !== 'object' && item.insert?.indexOf(linkFlag) !== -1
			})
			// 根据标识查找到插入的位置
			if (findex > -1) {
				const findOption = options[findex]
				const findAttributes = findOption.attributes
				// 将该findOption分成三部分:前内容 要插入的link 后内容
				const [prefix, suffix] = findOption.insert.split(linkFlag);
				const handleOps = []
				// 前内容
				if (prefix) {
					const prefixOps = findAttributes ? {
						insert: prefix,
						attributes: findAttributes
					} : {
						insert: prefix
					}
					handleOps.push(prefixOps)
				}
				// 插入的link
				const linkOps = {
					insert: attr.text,
					attributes: {
						link: attr.href,
						textDecoration: attr.textDecoration || 'none', // 下划线
						color: attr.color || '#007aff'
					}
				}
				handleOps.push(linkOps)
				// 后内容
				if (suffix) {
					const suffixOps = findAttributes ? {
						insert: suffix,
						attributes: findAttributes
					} : {
						insert: suffix
					}
					handleOps.push(suffixOps)
				}
				// 删除原options[findex]并在findex位置插入上述三个ops
				options.splice(findex, 1);
				options.splice(findex, 0, ...handleOps);
				// 最后重新初始化内容,注意该方法会导致光标重置到最开始位置
				editorCtx.setContents({
					delta: {
						ops: options
					}
				})
				// 所以最后建议使富文本光标失焦,让用户手动聚焦光标
				editorCtx.blur()

				// 后续回调操作
				if (callback) callback()
			}
		}
	})

}

/**
 * 将含有特殊图片形式视频的富文本转换成正常视频的富文本
 * @param {String} html 要进行处理的富文本字符串
 * @returns {String} 返回处理结果
 */
export function handleHtmlWithVideo(html) {
	// 正则表达式用于匹配img标签中带有alt属性且alt属性值为视频链接的模式
	const regex = /<img\s+src="[^"]*"\s+alt="([^"]*)"[^>]*>/g
	// 使用replace方法和一个函数回调来替换匹配到的内容
	return html.replace(regex, (match, videoUrl) => {
		// 替换为video标签,并添加controls属性以便用户可以控制播放
		return `<video width="80%" controls><source src="${videoUrl}" type="video/mp4"></video>`
	})
}

/**
 * 将img标签中内联style属性中的宽高样式提取出标签width与height属性
 * @param {Object} html 要处理的富文本字符串
 * @returns {Object} 返回处理结果
 */
export function convertImgStylesToAttributes(html) {
	return html.replace(/<img\s+([^>]+)\s*>/g, function(match, attributes) {
		// 分割属性
		const attrs = attributes.split(/\s+/);

		// 找到style属性的位置
		const styleIndex = attrs.findIndex(attr => attr.startsWith('style='));
		if (styleIndex === -1) return match; // 如果没有找到style属性,则返回原样

		// 提取style属性值
		const styleAttr = attrs.splice(styleIndex, 1)[0];
		const style = styleAttr.match(/"([^"]*)"/)[1];

		// 解析 style 属性
		const styleObj = {};
		style.split(';').forEach(function(part) {
			if (part) {
				const [name, value] = part.split(':');
				styleObj[name.trim()] = value.trim();
			}
		});

		// 创建新的 img 标签
		let newTag = '<img';
		if (styleObj.width) {
			newTag += ` width="${styleObj.width}"`;
		}
		if (styleObj.height) {
			newTag += ` height="${styleObj.height}"`;
		}

		// 添加原有的属性,包括修改过的style属性
		newTag += ` ${styleAttr} ${attrs.join(' ')}`;

		// 关闭 img 标签
		newTag += '>';

		return newTag;
	});
}