|
@@ -5,7 +5,7 @@
|
|
|
<i class="el-icon-arrow-left" />
|
|
<i class="el-icon-arrow-left" />
|
|
|
</div>
|
|
</div>
|
|
|
<span class="chat-title">{{ chatReceiver.screenName }}</span>
|
|
<span class="chat-title">{{ chatReceiver.screenName }}</span>
|
|
|
- <i class="el-icon-more right-icon" />
|
|
|
|
|
|
|
+ <i class="el-icon-more right-icon" @click="handleMore" />
|
|
|
</header>
|
|
</header>
|
|
|
|
|
|
|
|
<div ref="messageBox" class="message-container" @scroll="handleScroll">
|
|
<div ref="messageBox" class="message-container" @scroll="handleScroll">
|
|
@@ -85,6 +85,40 @@
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
|
|
+ <div v-else-if="msg.msgType === 6">
|
|
|
|
|
+ <div>语音通话 {{ msg.discardReason }} - {{ msg.duration }}</div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div v-else-if="msg.msgType === 7">
|
|
|
|
|
+ <div>视频通话 {{ msg.discardReason }} - {{ msg.duration }}</div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div
|
|
|
|
|
+ v-else-if="msg.msgType === 6"
|
|
|
|
|
+ :class="['msg-call-bubble', isMe(msg.sender.userId) ? 'is-self' : 'is-other']"
|
|
|
|
|
+ >
|
|
|
|
|
+ <i v-if="!isMe(msg.sender.userId)" class="el-icon-phone-outline call-icon" />
|
|
|
|
|
+
|
|
|
|
|
+ <div class="call-content">
|
|
|
|
|
+ <span class="call-text">{{ getCallStatusText(msg) }}</span>
|
|
|
|
|
+ <span v-if="msg.duration" class="call-duration">{{ msg.duration }}</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <i v-if="isMe(msg.sender.userId)" class="el-icon-phone-outline call-icon" />
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <div
|
|
|
|
|
+ v-else-if="msg.msgType === 7"
|
|
|
|
|
+ :class="['msg-call-bubble', isMe(msg.sender.userId) ? 'is-self' : 'is-other']"
|
|
|
|
|
+ >
|
|
|
|
|
+ <i v-if="!isMe(msg.sender.userId)" class="el-icon-video-camera call-icon" />
|
|
|
|
|
+
|
|
|
|
|
+ <div class="call-content">
|
|
|
|
|
+ <span class="call-text">{{ getCallStatusText(msg) }}</span>
|
|
|
|
|
+ <span v-if="msg.duration" class="call-duration">{{ msg.duration }}</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <i v-if="isMe(msg.sender.userId)" class="el-icon-video-camera call-icon" />
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
<div
|
|
<div
|
|
|
v-else-if="msg.msgType === 2"
|
|
v-else-if="msg.msgType === 2"
|
|
|
:class="['msg-transfer', { 'is-received': msg.transferStatus === 2 }]"
|
|
:class="['msg-transfer', { 'is-received': msg.transferStatus === 2 }]"
|
|
@@ -145,9 +179,9 @@
|
|
|
|
|
|
|
|
<footer class="input-container">
|
|
<footer class="input-container">
|
|
|
<div class="tool-bar">
|
|
<div class="tool-bar">
|
|
|
- <i class="el-icon-microphone" />
|
|
|
|
|
|
|
+ <i class="el-icon-microphone" @click="handleMore" />
|
|
|
<el-input v-model="inputText" type="text" size="small" placeholder="发送消息..." @keyup.enter.native="sendMessage" />
|
|
<el-input v-model="inputText" type="text" size="small" placeholder="发送消息..." @keyup.enter.native="sendMessage" />
|
|
|
- <i class="el-icon-aim" />
|
|
|
|
|
|
|
+ <i class="el-icon-aim" @click="handleMore" />
|
|
|
<el-button type="success" size="mini" class="send-btn" @click="sendMessage">发送</el-button>
|
|
<el-button type="success" size="mini" class="send-btn" @click="sendMessage">发送</el-button>
|
|
|
</div>
|
|
</div>
|
|
|
</footer>
|
|
</footer>
|
|
@@ -184,7 +218,7 @@
|
|
|
</template>
|
|
</template>
|
|
|
|
|
|
|
|
<script>
|
|
<script>
|
|
|
-import { getChatDialogue, getChatRecords, updateChatDialogue, getChatUser } from '@/api/chat'
|
|
|
|
|
|
|
+import { getChatDialogue, getChatRecords, updateChatDialogue } from '@/api/chat'
|
|
|
import { getAuthedUser } from '@/utils/auth'
|
|
import { getAuthedUser } from '@/utils/auth'
|
|
|
|
|
|
|
|
export default {
|
|
export default {
|
|
@@ -256,6 +290,9 @@ export default {
|
|
|
this.parseRouteParams()
|
|
this.parseRouteParams()
|
|
|
},
|
|
},
|
|
|
methods: {
|
|
methods: {
|
|
|
|
|
+ handleMore() {
|
|
|
|
|
+ this.$message.info('功能开发中')
|
|
|
|
|
+ },
|
|
|
/**
|
|
/**
|
|
|
* 点击转账气泡
|
|
* 点击转账气泡
|
|
|
*/
|
|
*/
|
|
@@ -413,7 +450,7 @@ export default {
|
|
|
const query = this.$route.query
|
|
const query = this.$route.query
|
|
|
|
|
|
|
|
// 1. 防御性拦截:如果关键参数缺失,进行报错提示或重定向
|
|
// 1. 防御性拦截:如果关键参数缺失,进行报错提示或重定向
|
|
|
- if (!query.chatId || !query.receiverId) {
|
|
|
|
|
|
|
+ if (!query.chatId) {
|
|
|
this.$message.error('会话参数缺失,正在返回列表...')
|
|
this.$message.error('会话参数缺失,正在返回列表...')
|
|
|
setTimeout(() => {
|
|
setTimeout(() => {
|
|
|
this.goBack()
|
|
this.goBack()
|
|
@@ -425,21 +462,14 @@ export default {
|
|
|
// 注意:如果你的分布式雪花 ID 超过了 16 位(大于 9007199254740991),
|
|
// 注意:如果你的分布式雪花 ID 超过了 16 位(大于 9007199254740991),
|
|
|
// 请不要转成 Number,保持字符串进行传输,否则 JS 会丢失后三位精度!
|
|
// 请不要转成 Number,保持字符串进行传输,否则 JS 会丢失后三位精度!
|
|
|
this.currentContact.chatId = Number(query.chatId)
|
|
this.currentContact.chatId = Number(query.chatId)
|
|
|
- this.currentContact.receiverId = Number(query.receiverId)
|
|
|
|
|
-
|
|
|
|
|
- getChatUser({ 'userId': this.currentContact.receiverId }).then(resp => {
|
|
|
|
|
- if (resp.code === 0) {
|
|
|
|
|
- this.chatReceiver = resp.data
|
|
|
|
|
- }
|
|
|
|
|
- })
|
|
|
|
|
-
|
|
|
|
|
// 3. 联动逻辑:如果路由中没有传 lastMessageId,则强制补 0
|
|
// 3. 联动逻辑:如果路由中没有传 lastMessageId,则强制补 0
|
|
|
// this.lastReadMessageId = query.lastMessageId ? Number(query.lastMessageId) : 0
|
|
// this.lastReadMessageId = query.lastMessageId ? Number(query.lastMessageId) : 0
|
|
|
- this.lastReadMessageId = 0
|
|
|
|
|
|
|
+ // this.lastReadMessageId = 0
|
|
|
const queryParams = {}
|
|
const queryParams = {}
|
|
|
queryParams.chatId = this.currentContact.chatId
|
|
queryParams.chatId = this.currentContact.chatId
|
|
|
getChatDialogue(queryParams).then(resp => {
|
|
getChatDialogue(queryParams).then(resp => {
|
|
|
if (resp.code === 0) {
|
|
if (resp.code === 0) {
|
|
|
|
|
+ this.chatReceiver = resp.data.receiver
|
|
|
this.lastReadMessageId = resp.data.lastMessageId
|
|
this.lastReadMessageId = resp.data.lastMessageId
|
|
|
}
|
|
}
|
|
|
}).finally(() => {
|
|
}).finally(() => {
|
|
@@ -737,6 +767,34 @@ export default {
|
|
|
},
|
|
},
|
|
|
images: [url]
|
|
images: [url]
|
|
|
})
|
|
})
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 根据拒绝原因和发送方,动态翻译成微信原生通话文案
|
|
|
|
|
+ * msg.discardReason 可选值示例:
|
|
|
|
|
+ * 'SUCCESS'(通话成功), 'CANCEL'(主动取消), 'REJECT'(对方拒绝), 'TIMEOUT'(无人接听)
|
|
|
|
|
+ */
|
|
|
|
|
+ getCallStatusText(msg) {
|
|
|
|
|
+ const isSelf = this.isMe(msg.sender.userId) // 是否是当前登录用户发出的
|
|
|
|
|
+ const reason = msg.discardReason
|
|
|
|
|
+
|
|
|
|
|
+ if (reason === 'SUCCESS' || reason === 'HANGUP') {
|
|
|
|
|
+ return '通话时长 '
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (reason === 'CANCEL') {
|
|
|
|
|
+ return isSelf ? '已取消' : '对方已取消'
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (reason === 'REJECT') {
|
|
|
|
|
+ return isSelf ? '对方已拒绝' : '已拒绝'
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (reason === 'TIMEOUT' || reason === 'MISSED') {
|
|
|
|
|
+ return isSelf ? '对方无应答' : '未接听'
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return '通话结束'
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
@@ -770,7 +828,7 @@ export default {
|
|
|
}
|
|
}
|
|
|
.left-action { cursor: pointer; display: flex; align-items: center; font-size: 16px; }
|
|
.left-action { cursor: pointer; display: flex; align-items: center; font-size: 16px; }
|
|
|
.chat-title { font-size: 16px; font-weight: bold; }
|
|
.chat-title { font-size: 16px; font-weight: bold; }
|
|
|
-.right-icon { font-size: 18px; }
|
|
|
|
|
|
|
+.right-icon { font-size: 18px; cursor: pointer; }
|
|
|
|
|
|
|
|
/* 消息流区域:高度自适应并允许滚动 */
|
|
/* 消息流区域:高度自适应并允许滚动 */
|
|
|
.message-container {
|
|
.message-container {
|
|
@@ -1155,4 +1213,55 @@ export default {
|
|
|
.msg-transfer:not(.is-received) .transfer-icon-box i {
|
|
.msg-transfer:not(.is-received) .transfer-icon-box i {
|
|
|
transform: rotate(45deg); /* 待收钱时箭头倾斜 */
|
|
transform: rotate(45deg); /* 待收钱时箭头倾斜 */
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+/* ==================== 音视频通话气泡基础样式 ==================== */
|
|
|
|
|
+.msg-call-bubble {
|
|
|
|
|
+ display: inline-flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ padding: 10px 14px;
|
|
|
|
|
+ border-radius: 4px;
|
|
|
|
|
+ font-size: 14px;
|
|
|
|
|
+ color: #191919;
|
|
|
|
|
+ max-width: 240px;
|
|
|
|
|
+ box-shadow: 0 1px 2px rgba(0,0,0,0.03);
|
|
|
|
|
+ word-break: break-all;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/* 自己发出的气泡(居右,文字靠右,通常背景已经是微信绿,通话气泡在微信里依然是白色/或跟常规气泡一致) */
|
|
|
|
|
+/* 注意:如果你的聊天界面整体“自己发的气泡”有统一绿色背景,通话消息可以继承或单独写 */
|
|
|
|
|
+.msg-call-bubble.is-self {
|
|
|
|
|
+ background-color: #95ec69; /* 微信标志绿 */
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/* 对方发出的气泡(居左,白色) */
|
|
|
|
|
+.msg-call-bubble.is-other {
|
|
|
|
|
+ background-color: #ffffff;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/* 图标样式微调 */
|
|
|
|
|
+.call-icon {
|
|
|
|
|
+ font-size: 18px;
|
|
|
|
|
+}
|
|
|
|
|
+.msg-call-bubble.is-self .call-icon {
|
|
|
|
|
+ margin-left: 10px; /* 自己发出的,图标在文字右侧,加左边距 */
|
|
|
|
|
+}
|
|
|
|
|
+.msg-call-bubble.is-other .call-icon {
|
|
|
|
|
+ margin-right: 10px; /* 对方发出的,图标在文字左侧,加右边距 */
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/* 核心内容区 */
|
|
|
|
|
+.call-content {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.call-text {
|
|
|
|
|
+ line-height: 1.4;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/* 时长数字单独空开一点间距 */
|
|
|
|
|
+.call-duration {
|
|
|
|
|
+ margin-left: 6px;
|
|
|
|
|
+ font-weight: 500;
|
|
|
|
|
+}
|
|
|
</style>
|
|
</style>
|