✨ feat(im): 初始化消息转发 v0.2:第二次优化部分代码(一些工具类等)
parent
cf01143632
commit
82d065c270
|
|
@ -215,21 +215,27 @@ const isUploading = computed(() => props.uploadProgress != null)
|
|||
const uploadProgress = computed(() => props.uploadProgress ?? 0)
|
||||
const uploadProgressText = computed(() => `${uploadProgress.value}%`)
|
||||
|
||||
/** 各 payload */
|
||||
const textPayload = computed(() => (isText.value ? parseMessage<TextMessage>(props.content) : null))
|
||||
/**
|
||||
* 单一 parse 入口:content 一变只 parse 一次,按 type 分发到下面 7 个 payload
|
||||
*
|
||||
* 各类型 payload 共用同一棵 JSON 树,避免 7 个 computed 各自重 parse 同一份 content
|
||||
*/
|
||||
const parsedContent = computed<unknown>(() => parseMessage(props.content))
|
||||
|
||||
const textPayload = computed(() => (isText.value ? (parsedContent.value as TextMessage | null) : null))
|
||||
const imagePayload = computed(() =>
|
||||
isImage.value ? parseMessage<ImageMessage>(props.content) : null
|
||||
isImage.value ? (parsedContent.value as ImageMessage | null) : null
|
||||
)
|
||||
const filePayload = computed(() => (isFile.value ? parseMessage<FileMessage>(props.content) : null))
|
||||
const filePayload = computed(() => (isFile.value ? (parsedContent.value as FileMessage | null) : null))
|
||||
const voicePayload = computed(() =>
|
||||
isVoice.value ? parseMessage<AudioMessage>(props.content) : null
|
||||
isVoice.value ? (parsedContent.value as AudioMessage | null) : null
|
||||
)
|
||||
const videoPayload = computed(() =>
|
||||
isVideo.value ? parseMessage<VideoMessage>(props.content) : null
|
||||
isVideo.value ? (parsedContent.value as VideoMessage | null) : null
|
||||
)
|
||||
const cardPayload = computed(() => (isCard.value ? parseMessage<CardMessage>(props.content) : null))
|
||||
const cardPayload = computed(() => (isCard.value ? (parsedContent.value as CardMessage | null) : null))
|
||||
const mergePayload = computed(() =>
|
||||
isMerge.value ? parseMessage<MergeMessage>(props.content) : null
|
||||
isMerge.value ? (parsedContent.value as MergeMessage | null) : null
|
||||
)
|
||||
|
||||
/** 合并消息内嵌前 N 条派生「{昵称}:{摘要}」 */
|
||||
|
|
@ -248,7 +254,7 @@ const facePayload = computed(() => {
|
|||
if (!isFace.value) {
|
||||
return null
|
||||
}
|
||||
const raw = parseMessage<FaceMessage>(props.content)
|
||||
const raw = parsedContent.value as FaceMessage | null
|
||||
if (!raw) {
|
||||
return null
|
||||
}
|
||||
|
|
@ -324,3 +330,50 @@ onBeforeUnmount(() => {
|
|||
voicePlaying.value = false
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 气泡尾巴:小三角伪元素,指向对应头像(对方在左、自己在右)
|
||||
border 4 边色画三角:透明 3 边 + 实色 1 边,省一张图片;颜色与气泡背景对应,留 1px 视觉吃进去 */
|
||||
.message-bubble--other::before,
|
||||
.message-bubble--self::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 12px;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-style: solid;
|
||||
}
|
||||
.message-bubble--other::before {
|
||||
left: -5px;
|
||||
border-width: 5px 6px 5px 0;
|
||||
border-color: transparent var(--el-fill-color-light) transparent transparent;
|
||||
}
|
||||
.message-bubble--self::before {
|
||||
right: -5px;
|
||||
border-width: 5px 0 5px 6px;
|
||||
border-color: transparent transparent transparent #95ec69;
|
||||
}
|
||||
|
||||
/* el-icon 在暗色模式下全局 color 被 .el-icon{color:var(--color)} 干扰,把 voice 图标 fill 锁死 */
|
||||
.message-bubble__voice-icon :deep(svg) {
|
||||
fill: #606266 !important;
|
||||
}
|
||||
.message-bubble__voice-icon.im-voice-playing :deep(svg) {
|
||||
fill: #409eff !important;
|
||||
}
|
||||
|
||||
/* 播放中的脉冲动画 */
|
||||
.im-voice-playing {
|
||||
animation: im-voice-icon-pulse 0.8s infinite;
|
||||
}
|
||||
|
||||
@keyframes im-voice-icon-pulse {
|
||||
0%,
|
||||
100% {
|
||||
transform: scale(1);
|
||||
}
|
||||
50% {
|
||||
transform: scale(1.15);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -861,55 +861,7 @@ function handleDelete() {
|
|||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 气泡尾巴:小三角伪元素,指向对应头像(对方在左、自己在右),对齐微信观感
|
||||
- 用 border 4 边色画三角:透明 3 边 + 实色 1 边,省一张图片
|
||||
- 颜色对应气泡背景,留 1px 视觉吃进去;UnoCSS 写不顺手,索性用 scoped CSS */
|
||||
.message-bubble--other::before,
|
||||
.message-bubble--self::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 12px;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-style: solid;
|
||||
}
|
||||
.message-bubble--other::before {
|
||||
left: -5px;
|
||||
border-width: 5px 6px 5px 0;
|
||||
border-color: transparent var(--el-fill-color-light) transparent transparent;
|
||||
}
|
||||
.message-bubble--self::before {
|
||||
right: -5px;
|
||||
border-width: 5px 0 5px 6px;
|
||||
border-color: transparent transparent transparent #95ec69;
|
||||
}
|
||||
|
||||
/* el-icon 在暗色模式下全局 color 被 .el-icon{color:var(--color)} 干扰;
|
||||
这里把 voice 图标的 fill 锁死,避免字体色跟随主题变白;
|
||||
file 图标已迁到 Iconify 按扩展名走彩色,不在这里强制 */
|
||||
.message-bubble__voice-icon :deep(svg) {
|
||||
fill: #606266 !important;
|
||||
}
|
||||
.message-bubble__voice-icon.im-voice-playing :deep(svg) {
|
||||
fill: #409eff !important;
|
||||
}
|
||||
|
||||
/* 播放中的脉冲动画:keyframes 用 UnoCSS 不好写,保留 scoped */
|
||||
.im-voice-playing {
|
||||
animation: im-voice-icon-pulse 0.8s infinite;
|
||||
}
|
||||
|
||||
@keyframes im-voice-icon-pulse {
|
||||
0%,
|
||||
100% {
|
||||
transform: scale(1);
|
||||
}
|
||||
50% {
|
||||
transform: scale(1.15);
|
||||
}
|
||||
}
|
||||
|
||||
/* SENDING 状态的转圈动画:el-icon 自带 .is-loading 旋转,迁到 Iconify 后丢了,自己补一份 */
|
||||
/* SENDING 状态的转圈动画 */
|
||||
.im-loading-spin {
|
||||
animation: im-loading-spin 1s linear infinite;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -228,9 +228,12 @@ provide(IM_MERGE_DETAIL_DIALOG_KEY, (content) => mergeDetailDialogRef.value?.ope
|
|||
|
||||
const multiSelect = useMessageMultiSelect()
|
||||
|
||||
/** 切会话退出多选;避免上一会话的勾选状态泄漏到新会话 */
|
||||
/** 切会话退出多选;避免上一会话的勾选状态泄漏到新会话(type+targetId 一起监听,私聊与群聊 id 同号时也能触发) */
|
||||
watch(
|
||||
() => conversationStore.activeConversation?.targetId,
|
||||
() => [
|
||||
conversationStore.activeConversation?.type,
|
||||
conversationStore.activeConversation?.targetId
|
||||
],
|
||||
() => multiSelect.exit()
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -137,6 +137,7 @@ import {
|
|||
import CardLineLabel from '@/views/im/home/components/card/CardLineLabel.vue'
|
||||
import {
|
||||
parseMessage,
|
||||
getFileIconInfo,
|
||||
type ImageMessage,
|
||||
type FileMessage,
|
||||
type AudioMessage,
|
||||
|
|
@ -218,39 +219,8 @@ function openVideo() {
|
|||
}
|
||||
}
|
||||
|
||||
/** 文件图标:按扩展名分配 icon + 颜色,对齐 home 端 MessageItem 的观感 */
|
||||
const fileIconInfo = computed<{ icon: string; color: string }>(() => {
|
||||
const name = filePayload.value?.name || ''
|
||||
const ext = name.split('.').pop()?.toLowerCase() || ''
|
||||
if (ext === 'pdf') {
|
||||
return { icon: 'ant-design:file-pdf-filled', color: '#ed5757' }
|
||||
}
|
||||
if (['doc', 'docx'].includes(ext)) {
|
||||
return { icon: 'ant-design:file-word-filled', color: '#2b7cd3' }
|
||||
}
|
||||
if (['xls', 'xlsx'].includes(ext)) {
|
||||
return { icon: 'ant-design:file-excel-filled', color: '#1f7244' }
|
||||
}
|
||||
if (['ppt', 'pptx'].includes(ext)) {
|
||||
return { icon: 'ant-design:file-ppt-filled', color: '#d24726' }
|
||||
}
|
||||
if (['zip', 'rar', '7z', 'tar', 'gz'].includes(ext)) {
|
||||
return { icon: 'ant-design:file-zip-filled', color: '#f0ad4e' }
|
||||
}
|
||||
if (['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'svg'].includes(ext)) {
|
||||
return { icon: 'ant-design:file-image-filled', color: '#9c27b0' }
|
||||
}
|
||||
if (['mp4', 'mov', 'avi', 'mkv', 'wmv', 'flv'].includes(ext)) {
|
||||
return { icon: 'ant-design:video-camera-filled', color: '#9c27b0' }
|
||||
}
|
||||
if (['mp3', 'wav', 'ogg', 'flac', 'aac'].includes(ext)) {
|
||||
return { icon: 'ant-design:audio-filled', color: '#9c27b0' }
|
||||
}
|
||||
if (['txt', 'md', 'log', 'json', 'xml'].includes(ext)) {
|
||||
return { icon: 'ant-design:file-text-filled', color: '#909399' }
|
||||
}
|
||||
return { icon: 'ant-design:file-filled', color: '#909399' }
|
||||
})
|
||||
/** 文件图标:按扩展名分配 icon + 颜色 */
|
||||
const fileIconInfo = computed(() => getFileIconInfo(filePayload.value?.name))
|
||||
|
||||
/** 系统事件 / 未知类型 fallback:取 JSON 首层 content,否则原文 */
|
||||
const fallbackText = computed(() => {
|
||||
|
|
|
|||
Loading…
Reference in New Issue