fix: render chat media by URL (images/video/voice) instead of unknown-type placeholder

MessageBubble.vue was keys off the (incomplete) type code, so many messages
with real OSS media URLs (https://wx.oss.yunkecn.com/.../*.webp|*.mp4) showed
as '[未知消息]'. Rendering is now URL-driven: pick the best http URL from
file_url > file_hd_url > file_th_url, then render by extension
(image/video/audio/file). Type code is only used as a last-resort label.

- MessageBubble.vue: URL-driven cascade (URL by ext → duration →
  transcription → text → structured-type placeholder → localized tag).
  Image/video/audio/file children @click.stop so the all-fields drawer
  still opens on bubble click but not on media interactions.
- format.ts: wechatTypeLabel(type_raw) maps known codes (3→图片, 34→语音,
  43→图片/表情, 49→文件/名片, 42→名片, 9→表情, 20→红包/转账).
- ChatTimeline.vue drawer type tag uses wechatTypeLabel for WeChat msgs.
- style.css: compact .yk-msg__audio player for voice-with-URL messages.

Verified against smoke.db: 25 type-3 images + 3 type-43 videos that
previously showed '[未知消息]' now render as images/video. Build clean.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
浅念
2026-08-05 10:23:01 +08:00
parent 0d466c4e4a
commit 8fde1d8597
4 changed files with 192 additions and 58 deletions
+3 -3
View File
@@ -6,7 +6,7 @@ import { ArrowLeft } from '@element-plus/icons-vue'
import { getMessages } from '@/api'
import type { Channel, Message } from '@/api/types'
import MessageBubble from '@/components/MessageBubble.vue'
import { channelLabel, formatTime } from '@/utils/format'
import { channelLabel, formatTime, wechatTypeLabel } from '@/utils/format'
import { buildMessageSections } from '@/utils/messageFields'
/**
@@ -238,12 +238,12 @@ onMounted(loadInitial)
{{ channelLabel(channelTyped) }}
</el-tag>
<el-tag
v-if="drawerMessage.type"
v-if="drawerMessage.type || drawerMessage.type_raw != null"
size="small"
type="info"
effect="plain"
>
{{ drawerMessage.type }}
{{ drawerMessage.channel === 'wechat' ? wechatTypeLabel(drawerMessage.type_raw) : (drawerMessage.type || '消息') }}
<template v-if="drawerMessage.type_raw != null">
({{ drawerMessage.type_raw }})
</template>