feat(chat): 聊天排版升级微信标准(左右分侧+头像+绿气泡)+is_send数据修复
- is_send 根因:云客消息方向在 mine 字段(isSend 全 null),归一化加 fallback +存量回填 60.7 万行(51万收/16万发)——分侧数据首次完整 - MessageBubble:头像(对方=raw headUrl 在左/自己=默认在右)、自己侧隐藏昵称、 接收白底描边/发送微信绿 #95ec69、气泡列 max-width 78% - 实测混合对话:13发(绿+头像右)/7收(白+头像左)全部正确 Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { Microphone, VideoPlay, Document, Picture, Paperclip } from '@element-plus/icons-vue'
|
||||
import { Microphone, VideoPlay, Document, Picture, Paperclip, User } from '@element-plus/icons-vue'
|
||||
import type { Message } from '@/api/types'
|
||||
import { formatDuration, formatTime, wechatTypeLabel } from '@/utils/format'
|
||||
|
||||
@@ -37,6 +37,13 @@ const emit = defineEmits<{ (e: 'open-detail', message: Message): void }>()
|
||||
|
||||
const isSend = computed(() => !!props.message.is_send)
|
||||
|
||||
/** 对方头像(raw_json.headUrl);自己侧用默认占位(headUrl 语义不保证是自己)。 */
|
||||
const peerAvatarUrl = computed(() => {
|
||||
const rj = props.message.raw_json as Record<string, unknown> | null | undefined
|
||||
const u = rj?.headUrl
|
||||
return typeof u === 'string' && u.startsWith('http') ? u : undefined
|
||||
})
|
||||
|
||||
// Centered (system/divider) rows ignore direction.
|
||||
const isCentered = computed(
|
||||
() => props.message.type === 'divider' || props.message.type === 'system',
|
||||
@@ -187,8 +194,17 @@ const typeTagLabel = computed(() => {
|
||||
children (image / video / audio / file link) stop propagation so
|
||||
their own clicks don't also open the drawer. -->
|
||||
<div v-else class="yk-msg__row yk-msg__row--clickable" @click="emit('open-detail', message)">
|
||||
<!-- 头像:对方=raw headUrl;自己=默认占位(row-reverse 时视觉在右) -->
|
||||
<div class="yk-msg__avatar-wrap">
|
||||
<img v-if="!isSend && peerAvatarUrl" class="yk-msg__avatar" :src="peerAvatarUrl" alt="" />
|
||||
<div v-else class="yk-msg__avatar yk-msg__avatar--ph">
|
||||
<el-icon :size="20"><User /></el-icon>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="yk-msg__body">
|
||||
<div class="yk-msg__meta">
|
||||
<span>{{ senderLabel }}</span>
|
||||
<span v-if="!isSend">{{ senderLabel }}</span>
|
||||
<span v-if="timeLabel">{{ timeLabel }}</span>
|
||||
</div>
|
||||
|
||||
@@ -299,6 +315,7 @@ const typeTagLabel = computed(() => {
|
||||
<el-icon><Picture /></el-icon>
|
||||
[{{ typeTagLabel }}]
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
Reference in New Issue
Block a user