|
@@ -16,7 +16,7 @@
|
|
|
|
|
|
|
|
<div
|
|
<div
|
|
|
v-for="(msg, index) in formattedMessageList"
|
|
v-for="(msg, index) in formattedMessageList"
|
|
|
- :key="msg.messageId"
|
|
|
|
|
|
|
+ :key="msg.messageId + index"
|
|
|
>
|
|
>
|
|
|
<div v-if="msg._showTimeText" class="chat-time-node">
|
|
<div v-if="msg._showTimeText" class="chat-time-node">
|
|
|
<span>{{ msg._showTimeText }}</span>
|
|
<span>{{ msg._showTimeText }}</span>
|
|
@@ -35,12 +35,12 @@
|
|
|
<div class="message-content">
|
|
<div class="message-content">
|
|
|
<div v-if="msg.msgType === 1" class="msg-text">{{ msg.content }}</div>
|
|
<div v-if="msg.msgType === 1" class="msg-text">{{ msg.content }}</div>
|
|
|
|
|
|
|
|
- <div v-else-if="msg.msgType === 2" class="msg-image" @click="onPreview(msg.objectUrl)">
|
|
|
|
|
|
|
+ <div v-else-if="msg.msgType === 22" class="msg-image" @click="onPreview(msg.objectUrl)">
|
|
|
<el-image :src="msg.objectUrl" fit="cover" />
|
|
<el-image :src="msg.objectUrl" fit="cover" />
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
<div
|
|
<div
|
|
|
- v-else-if="msg.msgType === 3"
|
|
|
|
|
|
|
+ v-else-if="msg.msgType === 23"
|
|
|
class="msg-audio"
|
|
class="msg-audio"
|
|
|
:style="{ width: 60 + (msg.duration || 5) * 4 + 'px' }"
|
|
:style="{ width: 60 + (msg.duration || 5) * 4 + 'px' }"
|
|
|
@click="toggleAudio(msg, msg.messageId)"
|
|
@click="toggleAudio(msg, msg.messageId)"
|
|
@@ -50,7 +50,7 @@
|
|
|
<div v-if="msg.isUnread" class="audio-unread-dot" />
|
|
<div v-if="msg.isUnread" class="audio-unread-dot" />
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
- <div v-else-if="msg.msgType === 4" class="msg-video" @click="viewVideo(msg.objectUrl)">
|
|
|
|
|
|
|
+ <div v-else-if="msg.msgType === 24" class="msg-video" @click="viewVideo(msg.objectUrl)">
|
|
|
<el-image :src="require('@/assets/img/video.jpg')" fit="cover" class="video-cover" />
|
|
<el-image :src="require('@/assets/img/video.jpg')" fit="cover" class="video-cover" />
|
|
|
<div class="video-play-mask">
|
|
<div class="video-play-mask">
|
|
|
<i class="el-icon-video-play play-icon" />
|
|
<i class="el-icon-video-play play-icon" />
|
|
@@ -58,7 +58,7 @@
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
<div
|
|
<div
|
|
|
- v-else-if="[5, 6, 7, 99].includes(msg.msgType)"
|
|
|
|
|
|
|
+ v-else-if="[21, 25, 26, 27].includes(msg.msgType)"
|
|
|
:class="['msg-file', { 'is-expired': msg.fileExpired }]"
|
|
:class="['msg-file', { 'is-expired': msg.fileExpired }]"
|
|
|
@click="msg.fileExpired ? handleExpiredClick() : handleFileClick(msg)"
|
|
@click="msg.fileExpired ? handleExpiredClick() : handleFileClick(msg)"
|
|
|
>
|
|
>
|
|
@@ -78,22 +78,27 @@
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
- <div v-else-if="msg.msgType === 8" class="msg-record">
|
|
|
|
|
|
|
+ <div v-else-if="msg.msgType === 5" class="msg-record">
|
|
|
<div class="record-title">聊天记录</div>
|
|
<div class="record-title">聊天记录</div>
|
|
|
<div v-for="(line, idx) in msg.previewLines" :key="idx" class="record-preview">
|
|
<div v-for="(line, idx) in msg.previewLines" :key="idx" class="record-preview">
|
|
|
{{ line }}
|
|
{{ line }}
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
- <div v-else-if="msg.msgType === 9" class="msg-transfer" @click="handleTransferClick(msg)">
|
|
|
|
|
|
|
+ <div
|
|
|
|
|
+ v-else-if="msg.msgType === 2"
|
|
|
|
|
+ :class="['msg-transfer', { 'is-received': msg.transferStatus === 2 }]"
|
|
|
|
|
+ @click="handleTransferClick(msg)"
|
|
|
|
|
+ >
|
|
|
<div class="transfer-main">
|
|
<div class="transfer-main">
|
|
|
<div class="transfer-icon-box">
|
|
<div class="transfer-icon-box">
|
|
|
- <i class="el-icon-refresh" />
|
|
|
|
|
|
|
+ <i :class="msg.transferStatus === 2 ? 'el-icon-check' : 'el-icon-refresh'" />
|
|
|
</div>
|
|
</div>
|
|
|
<div class="transfer-info">
|
|
<div class="transfer-info">
|
|
|
<div class="transfer-amount">¥{{ msg.amount || '0.00' }}</div>
|
|
<div class="transfer-amount">¥{{ msg.amount || '0.00' }}</div>
|
|
|
<div class="transfer-status">
|
|
<div class="transfer-status">
|
|
|
- {{ msg.transferStatus === 1 ? '转账给对方' : '已被接收' }}
|
|
|
|
|
|
|
+ <span v-if="msg.transferStatus === 1">待接收 / 点击收钱</span>
|
|
|
|
|
+ <span v-else-if="msg.transferStatus === 2">已被接收</span>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
@@ -101,20 +106,33 @@
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
<div
|
|
<div
|
|
|
- v-else-if="msg.msgType === 10"
|
|
|
|
|
- :class="['msg-lucky-money', { 'is-opened': msg.hbStatus === 1 }]"
|
|
|
|
|
|
|
+ v-else-if="msg.msgType === 3"
|
|
|
|
|
+ :class="['msg-lucky-money', { 'is-opened': msg.isSelfOpened }]"
|
|
|
@click="handleLuckyMoneyClick(msg)"
|
|
@click="handleLuckyMoneyClick(msg)"
|
|
|
>
|
|
>
|
|
|
<div class="hb-main">
|
|
<div class="hb-main">
|
|
|
<div class="hb-icon-box">
|
|
<div class="hb-icon-box">
|
|
|
- <i :class="msg.hbStatus === 1 ? 'el-icon-folder-opened' : 'el-icon-wallet'" />
|
|
|
|
|
|
|
+ <i :class="msg.isSelfOpened ? 'el-icon-folder-opened' : 'el-icon-wallet'" />
|
|
|
</div>
|
|
</div>
|
|
|
<div class="hb-info">
|
|
<div class="hb-info">
|
|
|
<div class="hb-title">{{ msg.content || '恭喜发财,大吉大利' }}</div>
|
|
<div class="hb-title">{{ msg.content || '恭喜发财,大吉大利' }}</div>
|
|
|
- <div v-if="msg.hbStatus === 1" class="hb-status">红包已被领完</div>
|
|
|
|
|
|
|
+
|
|
|
|
|
+ <div class="hb-status">
|
|
|
|
|
+ <span v-if="msg.isSelfOpened">
|
|
|
|
|
+ {{ msg.chatType === 2 ? '已抢到红包' : '红包已被接收' }}
|
|
|
|
|
+ </span>
|
|
|
|
|
+ <span v-else-if="msg.chatType === 2 && msg.transferStatus === 2">
|
|
|
|
|
+ 红包已被领完
|
|
|
|
|
+ </span>
|
|
|
|
|
+ <span v-else-if="msg.transferStatus === 3">
|
|
|
|
|
+ 红包已过期
|
|
|
|
|
+ </span>
|
|
|
|
|
+ </div>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
- <div class="hb-footer">微信红包</div>
|
|
|
|
|
|
|
+ <div class="hb-footer">
|
|
|
|
|
+ {{ msg.chatType === 2 ? '微信群红包' : '微信红包' }}
|
|
|
|
|
+ </div>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
@@ -238,20 +256,72 @@ export default {
|
|
|
this.parseRouteParams()
|
|
this.parseRouteParams()
|
|
|
},
|
|
},
|
|
|
methods: {
|
|
methods: {
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 点击转账气泡
|
|
|
|
|
+ */
|
|
|
handleTransferClick(msg) {
|
|
handleTransferClick(msg) {
|
|
|
- if (msg.transferStatus === 1) {
|
|
|
|
|
- this.$message.success(`点击了转账,金额:${msg.amount} 元,确认收钱逻辑待对接`)
|
|
|
|
|
- // 真实业务:调用后端确认收钱接口,然后把 msg.transferStatus 改为 2
|
|
|
|
|
- } else {
|
|
|
|
|
- this.$message.info('该转账已完成收钱')
|
|
|
|
|
|
|
+ // 如果已经收过钱了,直接返回或看详情
|
|
|
|
|
+ if (msg.transferStatus === 2) {
|
|
|
|
|
+ this.$message.info('该转账已完成,可在钱包明细中查看')
|
|
|
|
|
+ return
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ // 触发高仿微信收钱动画
|
|
|
|
|
+ const loading = this.$loading({
|
|
|
|
|
+ lock: true,
|
|
|
|
|
+ text: '正在收钱...',
|
|
|
|
|
+ spinner: 'el-icon-loading', // 产生菊花转圈动效
|
|
|
|
|
+ background: 'rgba(0, 0, 0, 0.7)',
|
|
|
|
|
+ customClass: 'wechat-loading-mask' // 注入自定义样式,包装成微信黑框提示
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ // 模拟网络请求延迟(1.5秒后收钱成功)
|
|
|
|
|
+ setTimeout(() => {
|
|
|
|
|
+ loading.close() // 关闭收钱动画
|
|
|
|
|
+
|
|
|
|
|
+ // 弹出成功提示
|
|
|
|
|
+ this.$message({
|
|
|
|
|
+ message: `成功入账 ¥${msg.amount} 元!`,
|
|
|
|
|
+ type: 'success'
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ // 核心:改变状态。由于给外层加了 transition,气泡颜色会当场丝滑变淡
|
|
|
|
|
+ this.$set(msg, 'transferStatus', 2)
|
|
|
|
|
+
|
|
|
|
|
+ // 实际业务:此处应该向后端发请求更新数据库:
|
|
|
|
|
+ // receiveTransfer({ messageId: msg.messageId }).then(resp => { ... })
|
|
|
|
|
+ }, 1500)
|
|
|
},
|
|
},
|
|
|
handleLuckyMoneyClick(msg) {
|
|
handleLuckyMoneyClick(msg) {
|
|
|
- if (msg.hbStatus !== 1) {
|
|
|
|
|
- this.$message.success('啪!打开红包动画,恭喜发财!')
|
|
|
|
|
- // 真实业务:弹出红包开启动画组件,调用抢红包接口,成功后将 msg.hbStatus 改为 1
|
|
|
|
|
|
|
+ // 1. 如果已经过期了
|
|
|
|
|
+ if (msg.transferStatus === 3) {
|
|
|
|
|
+ this.$message.info('该红包已超过24小时,已过期失效')
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 2. 如果是群聊红包 (chatType === 2)
|
|
|
|
|
+ if (msg.chatType === 2) {
|
|
|
|
|
+ if (msg.isSelfOpened) {
|
|
|
|
|
+ // 自己抢过了,点击直接看【群红包领取详情列表】(看手气最佳是谁)
|
|
|
|
|
+ this.$message.info('正在查看群红包领取详情...')
|
|
|
|
|
+ } else if (msg.transferStatus === 1) {
|
|
|
|
|
+ // 自己没抢过,但红包空了,点击弹窗提示“手慢了,红包派完了”,并把本地状态改为已读变灰
|
|
|
|
|
+ this.$message.warning('手慢了,红包已被领完!')
|
|
|
|
|
+ this.$set(msg, 'isSelfOpened', true)
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // 核心:有福同享,啪!弹出微信大红圈“開”字动画
|
|
|
|
|
+ this.$message.success('弹出群红包【開】弹窗,开始拼手气!')
|
|
|
|
|
+ // 成功抢到后后端配合把 msg.isSelfOpened 改为 true
|
|
|
|
|
+ }
|
|
|
} else {
|
|
} else {
|
|
|
- this.$message.info('查看该红包的领取详情列表')
|
|
|
|
|
|
|
+ // 3. 如果是单聊点对点红包 (chatType === 1)
|
|
|
|
|
+ if (msg.isSelfOpened || msg.transferStatus === 1) {
|
|
|
|
|
+ this.$message.info('查看红包领取金额')
|
|
|
|
|
+ } else {
|
|
|
|
|
+ this.$message.success('成功拆开好友红包!')
|
|
|
|
|
+ this.$set(msg, 'isSelfOpened', true)
|
|
|
|
|
+ this.$set(msg, 'transferStatus', 2)
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
},
|
|
},
|
|
|
/**
|
|
/**
|
|
@@ -271,8 +341,8 @@ export default {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// 2. 根据文件后缀或 msgType 判断策略
|
|
// 2. 根据文件后缀或 msgType 判断策略
|
|
|
- // msgType 对应关系参考:5-Word, 6-Excel, 7-PDF, 99-ZIP
|
|
|
|
|
- if (msg.msgType === 7 || url.toLowerCase().endsWith('.pdf')) {
|
|
|
|
|
|
|
+ // msgType 对应关系参考:25-Word, 26-Excel, 27-PDF, 99-ZIP
|
|
|
|
|
+ if (msg.msgType === 27 || url.toLowerCase().endsWith('.pdf')) {
|
|
|
// ==== 策略 A:在线预览 (主要针对 PDF 或高版本浏览器支持的公有流) ====
|
|
// ==== 策略 A:在线预览 (主要针对 PDF 或高版本浏览器支持的公有流) ====
|
|
|
// window.open(url, '_blank')
|
|
// window.open(url, '_blank')
|
|
|
} else {
|
|
} else {
|
|
@@ -399,7 +469,7 @@ export default {
|
|
|
getChatRecords(queryParams).then(resp => {
|
|
getChatRecords(queryParams).then(resp => {
|
|
|
if (resp.code === 0) {
|
|
if (resp.code === 0) {
|
|
|
resp.data.list.forEach(msg => {
|
|
resp.data.list.forEach(msg => {
|
|
|
- if ([5, 6, 7].includes(msg.msgType) && msg.content) {
|
|
|
|
|
|
|
+ if ([25, 26, 27].includes(msg.msgType) && msg.content) {
|
|
|
// 假设后端 content 规则为 "文件名|大小|下载地址"
|
|
// 假设后端 content 规则为 "文件名|大小|下载地址"
|
|
|
const parts = msg.content.split('|')
|
|
const parts = msg.content.split('|')
|
|
|
msg.fileName = parts[0] || '未知文件.docx'
|
|
msg.fileName = parts[0] || '未知文件.docx'
|
|
@@ -431,8 +501,6 @@ export default {
|
|
|
payload.chatId = this.currentContact.chatId
|
|
payload.chatId = this.currentContact.chatId
|
|
|
payload.lastMessageId = this.lastReadMessageId
|
|
payload.lastMessageId = this.lastReadMessageId
|
|
|
updateChatDialogue(payload).then(resp => {
|
|
updateChatDialogue(payload).then(resp => {
|
|
|
- if (resp.code === 0) {
|
|
|
|
|
- }
|
|
|
|
|
})
|
|
})
|
|
|
}
|
|
}
|
|
|
} else {
|
|
} else {
|
|
@@ -504,8 +572,6 @@ export default {
|
|
|
payload.chatId = this.currentContact.chatId
|
|
payload.chatId = this.currentContact.chatId
|
|
|
payload.lastMessageId = this.lastReadMessageId
|
|
payload.lastMessageId = this.lastReadMessageId
|
|
|
updateChatDialogue(payload).then(resp => {
|
|
updateChatDialogue(payload).then(resp => {
|
|
|
- if (resp.code === 0) {
|
|
|
|
|
- }
|
|
|
|
|
})
|
|
})
|
|
|
}
|
|
}
|
|
|
break
|
|
break
|
|
@@ -617,10 +683,10 @@ export default {
|
|
|
},
|
|
},
|
|
|
getFileIcon(type) {
|
|
getFileIcon(type) {
|
|
|
const iconMap = {
|
|
const iconMap = {
|
|
|
- 5: 'el-icon-document text-word',
|
|
|
|
|
- 6: 'el-icon-s-order text-excel',
|
|
|
|
|
- 7: 'el-icon-document-checked text-pdf',
|
|
|
|
|
- 99: 'el-icon-box text-zip'
|
|
|
|
|
|
|
+ 25: 'el-icon-document text-word',
|
|
|
|
|
+ 26: 'el-icon-s-order text-excel',
|
|
|
|
|
+ 27: 'el-icon-document-checked text-pdf',
|
|
|
|
|
+ 28: 'el-icon-box text-zip'
|
|
|
}
|
|
}
|
|
|
return iconMap[type] || 'el-icon-document'
|
|
return iconMap[type] || 'el-icon-document'
|
|
|
},
|
|
},
|
|
@@ -1057,4 +1123,36 @@ export default {
|
|
|
.msg-lucky-money.is-opened .hb-icon-box {
|
|
.msg-lucky-money.is-opened .hb-icon-box {
|
|
|
color: rgba(255,255,255,0.6); /* 金色变白透明 */
|
|
color: rgba(255,255,255,0.6); /* 金色变白透明 */
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+/* 转账基础气泡(待接收) */
|
|
|
|
|
+.msg-transfer {
|
|
|
|
|
+ width: 230px;
|
|
|
|
|
+ background-color: #fa9d3b;
|
|
|
|
|
+ border-radius: 4px;
|
|
|
|
|
+ overflow: hidden;
|
|
|
|
|
+ cursor: pointer;
|
|
|
|
|
+ box-shadow: 0 1px 2px rgba(0,0,0,0.05);
|
|
|
|
|
+
|
|
|
|
|
+ /* 【新增核心动画线】:让所有属性变化时都走0.3秒的丝滑渐变 */
|
|
|
|
|
+ transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/* 【已接收状态】的灰色/淡橙色过渡样式 */
|
|
|
|
|
+.msg-transfer.is-received {
|
|
|
|
|
+ background-color: #fcdbb3; /* 模拟微信收钱后,卡片颜色变浅半透明 */
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/* 微信特色:收钱后左侧圆圈背景和里面的对勾也会跟着变淡 */
|
|
|
|
|
+.msg-transfer.is-received .transfer-icon-box {
|
|
|
|
|
+ background: rgba(255, 255, 255, 0.4);
|
|
|
|
|
+ transform: rotate(0deg); /* 恢复正向显示对勾 */
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/* 图标本身的旋转过渡动画 */
|
|
|
|
|
+.transfer-icon-box i {
|
|
|
|
|
+ transition: transform 0.3s ease;
|
|
|
|
|
+}
|
|
|
|
|
+.msg-transfer:not(.is-received) .transfer-icon-box i {
|
|
|
|
|
+ transform: rotate(45deg); /* 待收钱时箭头倾斜 */
|
|
|
|
|
+}
|
|
|
</style>
|
|
</style>
|