feat(编辑器): 增强编辑器功能并优化图片处理
- 添加对粘贴图片的支持,自动触发上传流程 - 优化图片插入逻辑,保留原始尺寸信息并改进显示效果 - 重构消息内容解析逻辑,完善数据结构 - 移除冗余的文件插入功能,专注于图片处理优化 - 调整编辑器样式,改进图片显示效果
This commit is contained in:
parent
c89056d7f1
commit
f279248a51
@ -240,6 +240,22 @@ const insertMention = (member) => {
|
|||||||
const handlePaste = (event) => {
|
const handlePaste = (event) => {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
|
|
||||||
|
// 检查是否有图片
|
||||||
|
const items = event.clipboardData?.items
|
||||||
|
if (items) {
|
||||||
|
for (let i = 0; i < items.length; i++) {
|
||||||
|
if (items[i].type.indexOf('image') !== -1) {
|
||||||
|
// 获取粘贴的图片文件
|
||||||
|
const file = items[i].getAsFile()
|
||||||
|
if (file) {
|
||||||
|
// 使用现有的上传图片功能处理
|
||||||
|
onUploadFile([file])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 获取粘贴的纯文本内容
|
// 获取粘贴的纯文本内容
|
||||||
const text = event.clipboardData?.getData('text/plain') || ''
|
const text = event.clipboardData?.getData('text/plain') || ''
|
||||||
|
|
||||||
@ -295,10 +311,19 @@ const handleKeydown = (event) => {
|
|||||||
console.log('editorContent.value', editorContent.value)
|
console.log('editorContent.value', editorContent.value)
|
||||||
console.log('editorHtml.value', editorHtml.value)
|
console.log('editorHtml.value', editorHtml.value)
|
||||||
// 确保编辑器内容不为空(文本、图片、文件或表情)
|
// 确保编辑器内容不为空(文本、图片、文件或表情)
|
||||||
if (editorContent.value.trim() ||
|
// 由于我们已经在 handleInput 中处理了表情文本,editorContent.value 应该包含表情文本
|
||||||
editorHtml.value.includes('<img') ||
|
if (editorContent.value.trim()) {
|
||||||
editorHtml.value.includes('editor-file') ||
|
// 解析并输出编辑器内容
|
||||||
editorHtml.value.includes('editor-emoji')) {
|
const messageData = parseEditorContent()
|
||||||
|
console.log('编辑器内容解析结果:', JSON.stringify(messageData, null, 2))
|
||||||
|
|
||||||
|
// 输出详细信息
|
||||||
|
console.log('消息项目数量:', messageData.items.length)
|
||||||
|
console.log('消息项目类型:', messageData.items.map(item => item.type))
|
||||||
|
console.log('提及用户IDs:', messageData.mentionUids)
|
||||||
|
console.log('引用消息ID:', messageData.quoteId)
|
||||||
|
|
||||||
|
// 继续发送消息
|
||||||
sendMessage()
|
sendMessage()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -309,16 +334,26 @@ const handleKeydown = (event) => {
|
|||||||
// 发送消息
|
// 发送消息
|
||||||
const sendMessage = () => {
|
const sendMessage = () => {
|
||||||
console.log('发送消息');
|
console.log('发送消息');
|
||||||
// 检查编辑器是否有内容:文本、图片、文件或表情
|
// 检查编辑器是否有内容:文本内容(包括表情文本)
|
||||||
if (!editorContent.value.trim() &&
|
if (!editorContent.value.trim()) {
|
||||||
!editorHtml.value.includes('<img') &&
|
|
||||||
!editorHtml.value.includes('editor-file') &&
|
|
||||||
!editorHtml.value.includes('editor-emoji')) {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
console.log('发送消息1');
|
console.log('发送消息1');
|
||||||
const messageData = parseEditorContent()
|
const messageData = parseEditorContent()
|
||||||
|
|
||||||
|
// 输出完整的消息数据结构
|
||||||
|
console.log('完整消息数据:', {
|
||||||
|
items: messageData.items,
|
||||||
|
mentionUids: messageData.mentionUids,
|
||||||
|
quoteId: messageData.quoteId,
|
||||||
|
quoteData: quoteData.value ? {
|
||||||
|
msg_id: quoteData.value.msg_id,
|
||||||
|
title: quoteData.value.title,
|
||||||
|
describe: quoteData.value.describe,
|
||||||
|
image: quoteData.value.image
|
||||||
|
} : null
|
||||||
|
})
|
||||||
|
|
||||||
if (editingMessage.value) {
|
if (editingMessage.value) {
|
||||||
// 编辑消息
|
// 编辑消息
|
||||||
emit('editor-event', {
|
emit('editor-event', {
|
||||||
@ -361,6 +396,18 @@ const parseEditorContent = () => {
|
|||||||
const tempDiv = document.createElement('div')
|
const tempDiv = document.createElement('div')
|
||||||
tempDiv.innerHTML = editorHtml.value
|
tempDiv.innerHTML = editorHtml.value
|
||||||
|
|
||||||
|
// 检查是否有引用元素
|
||||||
|
const quoteElements = tempDiv.querySelectorAll('.editor-quote')
|
||||||
|
let quoteInfo = null
|
||||||
|
if (quoteElements.length > 0 && quoteData.value) {
|
||||||
|
quoteInfo = {
|
||||||
|
msg_id: quoteData.value.msg_id,
|
||||||
|
title: quoteData.value.title,
|
||||||
|
describe: quoteData.value.describe,
|
||||||
|
image: quoteData.value.image
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let textContent = ''
|
let textContent = ''
|
||||||
const nodes = Array.from(tempDiv.childNodes)
|
const nodes = Array.from(tempDiv.childNodes)
|
||||||
|
|
||||||
@ -368,6 +415,11 @@ const parseEditorContent = () => {
|
|||||||
if (node.nodeType === Node.TEXT_NODE) {
|
if (node.nodeType === Node.TEXT_NODE) {
|
||||||
textContent += node.textContent
|
textContent += node.textContent
|
||||||
} else if (node.nodeType === Node.ELEMENT_NODE) {
|
} else if (node.nodeType === Node.ELEMENT_NODE) {
|
||||||
|
// 跳过引用元素的处理,因为我们已经单独处理了
|
||||||
|
if (node.classList.contains('editor-quote')) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if (node.classList.contains('mention')) {
|
if (node.classList.contains('mention')) {
|
||||||
const userId = node.getAttribute('data-user-id')
|
const userId = node.getAttribute('data-user-id')
|
||||||
if (userId) {
|
if (userId) {
|
||||||
@ -377,8 +429,8 @@ const parseEditorContent = () => {
|
|||||||
} else if (node.tagName === 'IMG') {
|
} else if (node.tagName === 'IMG') {
|
||||||
// 处理图片
|
// 处理图片
|
||||||
const src = node.getAttribute('src')
|
const src = node.getAttribute('src')
|
||||||
const width = node.getAttribute('width') || ''
|
const width = node.getAttribute('data-original-width') || node.getAttribute('width') || ''
|
||||||
const height = node.getAttribute('height') || ''
|
const height = node.getAttribute('data-original-height') || node.getAttribute('height') || ''
|
||||||
const isEmoji = node.classList.contains('editor-emoji')
|
const isEmoji = node.classList.contains('editor-emoji')
|
||||||
|
|
||||||
if (textContent.trim()) {
|
if (textContent.trim()) {
|
||||||
@ -407,7 +459,9 @@ const parseEditorContent = () => {
|
|||||||
// 处理普通图片
|
// 处理普通图片
|
||||||
items.push({
|
items.push({
|
||||||
type: 3,
|
type: 3,
|
||||||
content: src + (width && height ? `?width=${width}&height=${height}` : '')
|
content: src + (width && height ? `?width=${width}&height=${height}` : ''),
|
||||||
|
width: width,
|
||||||
|
height: height
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
} else if (node.classList.contains('emoji')) {
|
} else if (node.classList.contains('emoji')) {
|
||||||
@ -447,11 +501,19 @@ const parseEditorContent = () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
// 构建完整的消息数据结构
|
||||||
|
const result = {
|
||||||
items: items.length > 0 ? items : [{ type: 1, content: '' }],
|
items: items.length > 0 ? items : [{ type: 1, content: '' }],
|
||||||
mentionUids,
|
mentionUids,
|
||||||
quoteId: quoteData.value?.msg_id || 0
|
quoteId: quoteData.value?.msg_id || 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 如果有引用信息,添加到结果中
|
||||||
|
if (quoteInfo) {
|
||||||
|
result.quoteInfo = quoteInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
// 清空编辑器
|
// 清空编辑器
|
||||||
@ -479,17 +541,24 @@ const clearEditor = () => {
|
|||||||
|
|
||||||
|
|
||||||
// 插入图片
|
// 插入图片
|
||||||
const insertImage = (url, width, height) => {
|
const insertImage = (src, width, height) => {
|
||||||
const selection = window.getSelection()
|
const selection = window.getSelection()
|
||||||
if (!selection.rangeCount) return
|
if (!selection.rangeCount) return
|
||||||
|
|
||||||
const range = selection.getRangeAt(0)
|
const range = selection.getRangeAt(0)
|
||||||
|
|
||||||
|
// 创建图片元素
|
||||||
const img = document.createElement('img')
|
const img = document.createElement('img')
|
||||||
img.src = url
|
img.src = src
|
||||||
img.style.maxWidth = '200px'
|
img.className = 'editor-image'
|
||||||
|
img.alt = '图片'
|
||||||
img.style.maxHeight = '200px'
|
img.style.maxHeight = '200px'
|
||||||
if (width) img.setAttribute('width', width)
|
img.style.maxWidth = '100%'
|
||||||
if (height) img.setAttribute('height', height)
|
img.style.objectFit = 'contain' // 保持原始比例
|
||||||
|
|
||||||
|
// 存储原始尺寸信息,但不直接设置宽高属性
|
||||||
|
if (width) img.setAttribute('data-original-width', width)
|
||||||
|
if (height) img.setAttribute('data-original-height', height)
|
||||||
|
|
||||||
range.deleteContents()
|
range.deleteContents()
|
||||||
range.insertNode(img)
|
range.insertNode(img)
|
||||||
@ -502,35 +571,6 @@ const insertImage = (url, width, height) => {
|
|||||||
handleInput({ target: editorRef.value })
|
handleInput({ target: editorRef.value })
|
||||||
}
|
}
|
||||||
|
|
||||||
// 插入文件
|
|
||||||
const insertFile = (url, fileName, fileSize) => {
|
|
||||||
const selection = window.getSelection()
|
|
||||||
if (!selection.rangeCount) return
|
|
||||||
|
|
||||||
const range = selection.getRangeAt(0)
|
|
||||||
|
|
||||||
// 创建文件链接元素
|
|
||||||
const fileLink = document.createElement('a')
|
|
||||||
fileLink.href = url
|
|
||||||
fileLink.target = '_blank'
|
|
||||||
fileLink.className = 'editor-file'
|
|
||||||
fileLink.textContent = fileName
|
|
||||||
fileLink.setAttribute('data-size', formatFileSize(fileSize))
|
|
||||||
fileLink.setAttribute('data-url', url) // 添加URL属性用于解析
|
|
||||||
fileLink.setAttribute('data-name', fileName) // 添加文件名属性用于解析
|
|
||||||
fileLink.setAttribute('data-size-raw', fileSize) // 添加原始文件大小属性用于解析
|
|
||||||
|
|
||||||
range.deleteContents()
|
|
||||||
range.insertNode(fileLink)
|
|
||||||
range.setStartAfter(fileLink)
|
|
||||||
range.collapse(true)
|
|
||||||
selection.removeAllRanges()
|
|
||||||
selection.addRange(range)
|
|
||||||
|
|
||||||
editorRef.value.focus()
|
|
||||||
handleInput({ target: editorRef.value })
|
|
||||||
}
|
|
||||||
|
|
||||||
// 格式化文件大小
|
// 格式化文件大小
|
||||||
const formatFileSize = (size) => {
|
const formatFileSize = (size) => {
|
||||||
if (size < 1024) {
|
if (size < 1024) {
|
||||||
@ -547,21 +587,61 @@ const formatFileSize = (size) => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 文件上传处理
|
* 文件上传处理
|
||||||
* @param e 上传事件对象
|
* @param e 上传事件对象或文件数组
|
||||||
*/
|
*/
|
||||||
|
// 文件上传处理
|
||||||
async function onUploadFile(e) {
|
async function onUploadFile(e) {
|
||||||
let file = e.target.files[0]
|
let files;
|
||||||
|
|
||||||
e.target.value = null // 清空input,允许再次选择相同文件
|
// 判断参数类型
|
||||||
|
if (Array.isArray(e)) {
|
||||||
|
// 直接传入的文件数组
|
||||||
|
files = e;
|
||||||
|
} else {
|
||||||
|
// 传入的是事件对象
|
||||||
|
files = e.target.files;
|
||||||
|
e.target.value = null; // 清空input,允许再次选择相同文件
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确保有文件
|
||||||
|
if (!files || files.length === 0) return;
|
||||||
|
|
||||||
|
// 处理第一个文件
|
||||||
|
const file = files[0];
|
||||||
|
|
||||||
console.log("文件类型"+file.type)
|
console.log("文件类型"+file.type)
|
||||||
if (file.type.indexOf('image/') === 0) {
|
if (file.type.indexOf('image/') === 0) {
|
||||||
console.log("进入图片")
|
console.log("进入图片")
|
||||||
// 处理图片文件 - 立即显示临时消息,然后上传
|
// 创建临时URL
|
||||||
let fn = emitCall('image_event', file, () => {})
|
const tempUrl = URL.createObjectURL(file);
|
||||||
emit('editor-event', fn)
|
|
||||||
|
|
||||||
return
|
// 创建图片对象以获取尺寸
|
||||||
|
const image = new Image();
|
||||||
|
image.src = tempUrl;
|
||||||
|
|
||||||
|
image.onload = () => {
|
||||||
|
// 上传图片到服务器
|
||||||
|
const form = new FormData();
|
||||||
|
form.append('file', file);
|
||||||
|
form.append("source", "fonchain-chat"); // 图片来源标识
|
||||||
|
form.append("urlParam", `width=${image.width}&height=${image.height}`);
|
||||||
|
|
||||||
|
// 先将临时图片插入编辑器,不直接设置宽高,而是传递原始尺寸信息
|
||||||
|
insertImage(tempUrl, image.width, image.height);
|
||||||
|
|
||||||
|
// 上传图片并获取永久URL
|
||||||
|
uploadImg(form).then(({ code, data, message }) => {
|
||||||
|
if (code == 0) {
|
||||||
|
// 上传成功后,可以将临时URL替换为永久URL
|
||||||
|
// 但这里我们不做替换,因为临时URL在当前会话中已足够使用
|
||||||
|
console.log('图片上传成功:', data.ori_url);
|
||||||
|
} else {
|
||||||
|
window['$message'].error(message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (file.type.indexOf('video/') === 0) {
|
if (file.type.indexOf('video/') === 0) {
|
||||||
@ -1536,12 +1616,14 @@ const onVoteSubmit = (data) => {
|
|||||||
* 添加圆角和鼠标指针样式
|
* 添加圆角和鼠标指针样式
|
||||||
*/
|
*/
|
||||||
.editor-image {
|
.editor-image {
|
||||||
max-width: 100px;
|
max-width: 300px;
|
||||||
max-height: 100px;
|
max-height: 200px;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
background-color: #48484d;
|
background-color: #48484d;
|
||||||
margin: 0px 2px;
|
margin: 0px 2px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
object-fit: contain; /* 保持原始比例 */
|
||||||
|
display: inline-block; /* 确保图片正确显示 */
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -18,7 +18,7 @@ export function isLoggedIn() {
|
|||||||
*/
|
*/
|
||||||
export function getAccessToken() {
|
export function getAccessToken() {
|
||||||
// return storage.get(AccessToken) || ''
|
// return storage.get(AccessToken) || ''
|
||||||
return JSON.parse(localStorage.getItem('token'))||'46d71a72d8d845ad7ed23eba9bdde260e635407190c2ce1bf7fd22088e41682ea07773ec65cae8946d2003f264d55961f96e0fc5da10eb96d3a348c1664e9644ce2108c311309f398ae8ea1b8200bfd490e5cb6e8c52c9e5d493cbabb163368f8351420451a631dbfa749829ee4cda49b77b5ed2d3dced5d0f2b7dd9ee76ba5465c84a17c23af040cd92b6b2a4ea48befbb5c729dcdad0a9c9668befe84074cc24f78899c1d947f8e7f94c7eda5325b8ed698df729e76febb98549ef3482ae942fb4f4a1c92d21836fa784728f0c5483aab2760a991b6b36e6b10c84f840a6433a6ecc31dee36e8f1c6158818bc89d22726726265e9af0db370a54ea5ee002b43662d571b84c8468ac15330f79503a5cd5e72282d8bee92749b1a3c1b7fd87ae70b64b90e437e84c1b558c64a35e181b2ecf5db3007680c3607eac1edee7f59d'
|
return JSON.parse(localStorage.getItem('token'))||'46d71a72d8d845ad7ed23eba9bdde260e635407190c2ce1bf7fd22088e41682ea07773ec65cae8946d2003f264d55961f96e0fc5da10eb96d3a348c1664e9644ce2108c311309f398ae8ea1b8200bfd490e5cb6e8c52c9e5d493cbabb163368f8351420451a631dbfa749829ee4cda49b77b5ed2d3dced5d0f2b7dd9ee76ba5465c84a17c23af040cd92b6b2a4ea48befbb5c729dcdad0a9c9668befe84074cc24f78899c1d947f8e7f94c7eda5325b8ed698df729e76febb98549ef3482ae942fb4f4a1c92d21836fa784728f0c5483aab2760a991b6b36e6b10c84f840a6433a6ecc31dee36e8f1c6158818bc89d22403363066ad3c046839f7b2cf8a6186da017388f197c0c3b219b1c04e7d986e9774b72664a22a6075cee77da3584b7a2131365913796a5fcabc8f4594284e480a592a84a40a9aa7f5f27c951a53a369c'
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Loading…
Reference in New Issue
Block a user