feat: improve realtime messaging and media handling
This commit is contained in:
@@ -47,7 +47,13 @@
|
||||
class="message-image"
|
||||
@click.stop="handleImageClick(message)"
|
||||
>
|
||||
<img :src="message.attachment.url" :alt="message.attachment.name || '图片'">
|
||||
<img
|
||||
:src="message.attachment.url"
|
||||
:alt="message.attachment.name || '图片'"
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
@error="retryImageLoad"
|
||||
>
|
||||
</button>
|
||||
<a
|
||||
v-else-if="isFileMessage(message)"
|
||||
@@ -63,7 +69,7 @@
|
||||
<small>{{ formatFileSize(message.attachment.size) }}</small>
|
||||
</span>
|
||||
</a>
|
||||
<p v-if="message.text">{{ message.text }}</p>
|
||||
<p v-if="displayMessageText(message)">{{ displayMessageText(message) }}</p>
|
||||
<time>
|
||||
<span v-if="isRecallNotice(message)" class="recall-mark">{{ message.recallStatus || '已撤回' }}</span>
|
||||
<span>{{ formatTime(message.displayTimestamp || message.timestamp) }}</span>
|
||||
@@ -130,6 +136,8 @@
|
||||
:src="imageViewer.url"
|
||||
:alt="imageViewer.name || '图片'"
|
||||
:style="imageViewerStyle"
|
||||
decoding="async"
|
||||
@error="retryImageLoad"
|
||||
draggable="false"
|
||||
>
|
||||
</div>
|
||||
@@ -325,7 +333,16 @@ const menuItems = computed(() => {
|
||||
function scrollToBottom() {
|
||||
nextTick(() => {
|
||||
if (listEl.value) {
|
||||
listEl.value.scrollTop = listEl.value.scrollHeight;
|
||||
const scroll = () => {
|
||||
if (listEl.value) {
|
||||
listEl.value.scrollTop = listEl.value.scrollHeight;
|
||||
}
|
||||
};
|
||||
scroll();
|
||||
requestAnimationFrame(() => {
|
||||
scroll();
|
||||
requestAnimationFrame(scroll);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -372,6 +389,22 @@ function messageIdentity(message) {
|
||||
return String(message.messageUid || message.id || '');
|
||||
}
|
||||
|
||||
function displayMessageText(message) {
|
||||
const text = String(message?.text || '').replace(/^引用:.*(?:\r?\n|$)/, '').trim();
|
||||
const contentType = Number(message?.contentType || 0);
|
||||
if (!text) {
|
||||
return '';
|
||||
}
|
||||
if (contentType !== 1 && text === '[暂不支持的消息类型]') {
|
||||
return '';
|
||||
}
|
||||
if ((contentType === 3 || message?.type === 'image') && (text === '[图片]' || text === '图片')) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
function canOpenMessageMenu(message) {
|
||||
return message && message.status !== 'failed' && message.status !== 'sending';
|
||||
}
|
||||
@@ -552,6 +585,30 @@ function formatFileSize(size) {
|
||||
return bytes > 0 ? `${bytes} B` : '';
|
||||
}
|
||||
|
||||
function retryImageLoad(event) {
|
||||
const image = event.currentTarget;
|
||||
if (!(image instanceof HTMLImageElement)) {
|
||||
return;
|
||||
}
|
||||
const attempts = Number(image.dataset.loadRetries || 0);
|
||||
if (attempts >= 2) {
|
||||
return;
|
||||
}
|
||||
const originalUrl = image.dataset.originalUrl || image.getAttribute('src') || '';
|
||||
if (!originalUrl) {
|
||||
return;
|
||||
}
|
||||
image.dataset.originalUrl = originalUrl;
|
||||
image.dataset.loadRetries = String(attempts + 1);
|
||||
window.setTimeout(() => {
|
||||
if (!image.isConnected) {
|
||||
return;
|
||||
}
|
||||
const separator = originalUrl.includes('?') ? '&' : '?';
|
||||
image.src = `${originalUrl}${separator}_image_retry=${attempts + 1}`;
|
||||
}, 500 * (attempts + 1));
|
||||
}
|
||||
|
||||
function handleMessageClick(message) {
|
||||
if (props.selectionMode) {
|
||||
emit('toggle-select', message);
|
||||
|
||||
Reference in New Issue
Block a user