This commit is contained in:
左哥 2025-11-06 23:13:27 +08:00
parent 4e87f437e1
commit 66fabbf475
6 changed files with 223 additions and 117 deletions

View File

@ -41,3 +41,14 @@ export const send_template_message = (data) => {
data data
}) })
} }
// 查看小程序状态
export const checkMiniProgramStatus = (params) => {
return request({
url: `admin/app/check_status`,
method: 'get',
params
})
}

View File

@ -2,7 +2,7 @@
<div style="width: 100%;"> <div style="width: 100%;">
<!-- <el-input v-model="centent" suffix-icon="Search" @input="inputSearch" placeholder="搜索"></el-input> --> <!-- <el-input v-model="centent" suffix-icon="Search" @input="inputSearch" placeholder="搜索"></el-input> -->
<ul v-infinite-scroll="load" class="infinite-list" style="overflow: auto" :style="'height:' + height" :infinite-scroll-immediate="false" infinite-scroll-distance="5"> <ul v-infinite-scroll="load" class="infinite-list" style="overflow: auto" :style="'height:' + height" :infinite-scroll-immediate="false" infinite-scroll-distance="5">
<li v-for="item in cardlist" :key="item.sender_id" class="infinite-list-item" :class="{ active: item.active }" <li v-for="item in cardlist" :key="item.sender_id" class="infinite-list-item" :class="{ active: item.sender_id == activeId }"
@click="handleItem(item)"> @click="handleItem(item)">
<div class="avatar-container"> <div class="avatar-container">
<el-image class="user-avatar" :src="item.sender_avatar"></el-image> <el-image class="user-avatar" :src="item.sender_avatar"></el-image>
@ -65,8 +65,9 @@ const inputSearch = (e) => {
emits('input', e) emits('input', e)
}, 500) }, 500)
} }
const activeId = ref(null)
const handleItem = (row) => { const handleItem = (row) => {
activeId.value = row.sender_id
if (props.multiple) { if (props.multiple) {
if (!row.active) { if (!row.active) {
row.active = true row.active = true
@ -272,7 +273,7 @@ onMounted(() => {
} }
.active { .active {
background-color: var(--el-color-primary-light-7); background-color: var(--el-color-primary-light-9);
} }
.infinite-list .infinite-list-item+.list-item { .infinite-list .infinite-list-item+.list-item {

View File

@ -1,16 +1,33 @@
<script setup> <script setup>
import { computed } from 'vue' import { computed, onMounted, onUnmounted, ref } from 'vue'
import { useRoute, useRouter } from 'vue-router'; import { useRoute, useRouter } from 'vue-router';
import Setting from '@/layout/tabbar/setting/index.vue' import Setting from '@/layout/tabbar/setting/index.vue'
import appDetail from '@/store/modules/appDetail'
const appInfoStore = appDetail()
// ensure route/router are defined
const route = useRoute() const route = useRoute()
const router = useRouter() const router = useRouter()
console.log('appInfoStore', appInfoStore);
const appStatus = computed(() => {
// guard access and provide fallback
try {
return appInfoStore.appDetail?.check_status_text || ''
} catch (e) {
return ''
}
})
const newPath = computed(() => { const newPath = computed(() => {
return route.path return route.path
}) })
const toLink = (path) => { const toLink = (path) => {
router.push(path) router.push(path)
} }
</script> </script>
<template> <template>
@ -25,7 +42,8 @@ const toLink = (path) => {
小程序聊天系统 小程序聊天系统
</div> </div>
<div class="right"> <div class="right">
<div class="nav"> <div class="nav" v-if="newPath == '/chat'">
状态{{ appStatus }}
<!-- <ul><li @click="toLink('/dashboard')" :class="{ active: newPath == '/dashboard' }">首页</li></ul> --> <!-- <ul><li @click="toLink('/dashboard')" :class="{ active: newPath == '/dashboard' }">首页</li></ul> -->
</div> </div>
@ -70,6 +88,7 @@ const toLink = (path) => {
padding-left: 10px; padding-left: 10px;
width: calc(100% - 300px); width: calc(100% - 300px);
.nav{ .nav{
padding-left: 10px;
li{ li{
display: inline-block; display: inline-block;
padding: 0 12px; padding: 0 12px;

View File

@ -0,0 +1,24 @@
// 机器人相关仓库
import { defineStore } from 'pinia'
import { ref } from 'vue'
const appDetail = defineStore(
'appDetail',
() => {
const appDetail = ref({
check_status_text: ''
})
return {
appDetail
}
},
{
persist: true
}
)
export default appDetail

View File

@ -428,4 +428,9 @@ span[class=el-tooltip__trigger] {
.upload-button{ .upload-button{
padding-top: 0!important; padding-top: 0!important;
} }
}
.el-image__error{
font-size: 12px!important;
line-height: 14px;
} }

View File

@ -1,19 +1,22 @@
<template> <template>
<div class="chat-page"> <div class="chat-page">
<div class="left-panel"> <div class="left-panel">
<WxUserCard @load="onLoadUserList" :cardlist="userList" height="calc(100vh - 120px)" @change="onSelectUser" /> <WxUserCard @load="onLoadUserList" :cardlist="userList" height="calc(100vh - 120px)"
@change="onSelectUser" />
</div> </div>
<div class="right-panel"> <div class="right-panel">
<div :class="['chat-body', { 'chat-body--loading': isLoadingMessages }]" ref="chatBody" @scroll="handleScroll"> <div :class="['chat-body', { 'chat-body--loading': isLoadingMessages }]" ref="chatBody"
@scroll="handleScroll">
<!-- 加载更多提示 --> <!-- 加载更多提示 -->
<div v-if="loadingMore" class="loading-more">加载中...</div> <div v-if="loadingMore" class="loading-more">加载中...</div>
<WxChatRecord :msgList="messages" :sendeInfo="sendeInfo" msgType="user" @retry="handleRetry" /> <WxChatRecord :msgList="messages" :sendeInfo="sendeInfo" msgType="user" @retry="handleRetry" />
</div> </div>
<div class="chat-footer"> <div class="chat-footer">
<div class="footer-left" style=""> <div class="footer-left" style="">
<div class="upload-row" style="display:flex; align-items:center; gap:12px;margin-left: 6px;position: relative; top: 10px;"> <div class="upload-row"
style="display:flex; align-items:center; gap:12px;margin-left: 6px;position: relative; top: 10px;">
<el-icon style="font-size:24px;cursor:pointer" @click="triggerImageInput"> <el-icon style="font-size:24px;cursor:pointer" @click="triggerImageInput">
<svg class="icon" aria-hidden="true"> <svg class="icon" aria-hidden="true">
<use xlink:href="#icon-tupian1"></use> <use xlink:href="#icon-tupian1"></use>
@ -28,21 +31,11 @@
</div> </div>
<!-- 发送按钮 --> <!-- 发送按钮 -->
<div class="send-button-container" style="margin-left: 12px;"> <div class="send-button-container" style="margin-left: 12px;">
<el-button <el-button type="primary" @click="send" :disabled="!draft.trim() || isSending || isUploading"
type="primary" :loading="isSending" style="height: 60px;">
@click="send"
:disabled="!draft.trim() || isSending || isUploading"
:loading="isSending"
style="height: 60px;"
>
{{ isSending ? '发送中...' : '发送' }} {{ isSending ? '发送中...' : '发送' }}
</el-button> </el-button>
<el-button <el-button type="primary" @click="sendTemplateMessage" :loading="isSending2" style="height: 60px;">
type="primary"
@click="sendTemplateMessage"
:loading="isSending2"
style="height: 60px;"
>
{{ isSending2 ? '发送中...' : '发送模板消息' }} {{ isSending2 ? '发送中...' : '发送模板消息' }}
</el-button> </el-button>
</div> </div>
@ -58,10 +51,12 @@ import WxUserCard from '@/components/UserCard/index.vue'
import UserMessage from '@/components/UserMessage/index.vue' import UserMessage from '@/components/UserMessage/index.vue'
import mihoutai from '@/assets/images/mihoutai.png' import mihoutai from '@/assets/images/mihoutai.png'
import V3Emoji from "vue3-emoji"; import V3Emoji from "vue3-emoji";
import { getUserList, send_message, get_messages, send_template_message } from '@/api/chat' import { getUserList, send_message, get_messages, send_template_message, checkMiniProgramStatus } from '@/api/chat'
import { useRoute } from 'vue-router' import { useRoute } from 'vue-router'
import { uploadFile } from '@/api/upload' import { uploadFile } from '@/api/upload'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import appDetail from '@/store/modules/appDetail'
const appInfoStore = appDetail()
const route = useRoute() const route = useRoute()
@ -101,6 +96,8 @@ const messageTimer = ref(null) // 定时器实例
const isSending = ref(false) // const isSending = ref(false) //
const lastSendTime = ref(0) // const lastSendTime = ref(0) //
const isUploading = ref(false) // const isUploading = ref(false) //
//
const appStatusTimer = ref(null)
// //
const onSelectUser = async (id) => { const onSelectUser = async (id) => {
@ -149,12 +146,12 @@ const sortMessagesByTime = (messages) => {
const ts = new Date(msg.timestamp).getTime() const ts = new Date(msg.timestamp).getTime()
if (!isNaN(ts)) return ts if (!isNaN(ts)) return ts
} }
if (msg.created_at) { if (msg.created_at) {
const ts = new Date(msg.created_at).getTime() const ts = new Date(msg.created_at).getTime()
if (!isNaN(ts)) return ts if (!isNaN(ts)) return ts
} }
// send_time // send_time
if (msg.send_time) { if (msg.send_time) {
// 2025-10-22 01:52:19 // 2025-10-22 01:52:19
@ -172,13 +169,13 @@ const sortMessagesByTime = (messages) => {
const days = parseInt(msg.send_time.match(/(\d+)天前/)?.[1] || '0') const days = parseInt(msg.send_time.match(/(\d+)天前/)?.[1] || '0')
return Date.now() - (days * 24 * 60 * 60 * 1000) return Date.now() - (days * 24 * 60 * 60 * 1000)
} }
// //
const timestamp = new Date(msg.send_time).getTime() const timestamp = new Date(msg.send_time).getTime()
if (!isNaN(timestamp)) return timestamp if (!isNaN(timestamp)) return timestamp
} }
} }
// IDID // IDID
if (msg.id || msg._id || msg.message_id) { if (msg.id || msg._id || msg.message_id) {
const id = String(msg.id || msg._id || msg.message_id) const id = String(msg.id || msg._id || msg.message_id)
@ -196,23 +193,23 @@ const sortMessagesByTime = (messages) => {
const timestampMatch = id.match(/(\d{13})/) // 13 const timestampMatch = id.match(/(\d{13})/) // 13
if (timestampMatch) return parseInt(timestampMatch[1]) if (timestampMatch) return parseInt(timestampMatch[1])
} }
// //
return Date.now() return Date.now()
} }
return getTimestamp(a) - getTimestamp(b) return getTimestamp(a) - getTimestamp(b)
}) })
// //
console.log('消息排序结果:', sorted.map(m => ({ console.log('消息排序结果:', sorted.map(m => ({
id: m.id || m._id || m.message_id, id: m.id || m._id || m.message_id,
send_time: m.send_time, send_time: m.send_time,
content: typeof m.content === 'object' ? content: typeof m.content === 'object' ?
(m.content.message || JSON.stringify(m.content)) : (m.content.message || JSON.stringify(m.content)) :
String(m.content).slice(0, 20) + '...' String(m.content).slice(0, 20) + '...'
}))) })))
return sorted return sorted
} }
@ -229,7 +226,7 @@ const getMessages = async (isHistory = true, pageOverride = null) => {
if (!isPolling) { if (!isPolling) {
loadingMore.value = true loadingMore.value = true
} }
// page=1 isHistory=true isLoadingMessages true // page=1 isHistory=true isLoadingMessages true
if ((pageOverride ?? msgQuery.page) === 1 && isHistory) { if ((pageOverride ?? msgQuery.page) === 1 && isHistory) {
isLoadingMessages.value = true isLoadingMessages.value = true
@ -241,18 +238,18 @@ const getMessages = async (isHistory = true, pageOverride = null) => {
const res = await get_messages({ ...query }) const res = await get_messages({ ...query })
msgTotal = res.total || 0 msgTotal = res.total || 0
const list = res.list || [] const list = res.list || []
console.log(`获取消息 - isHistory: ${isHistory}, page: ${queryPage}, 返回消息数: ${list.length}`) console.log(`获取消息 - isHistory: ${isHistory}, page: ${queryPage}, 返回消息数: ${list.length}`)
if (list.length > 0) { if (list.length > 0) {
const newMessages = list.map(item => { const newMessages = list.map(item => {
// ChatRecord // ChatRecord
try { try {
const text = JSON.parse(item.content) const text = JSON.parse(item.content)
item.content = { item.content = {
message: text.message || text.messages message: text.message || text.messages
} }
} catch (e) { } catch (e) {
// //
item.content = { message: item.content } item.content = { message: item.content }
} }
@ -282,28 +279,28 @@ const getMessages = async (isHistory = true, pageOverride = null) => {
// //
const currentMessages = [...messages.value] const currentMessages = [...messages.value]
let hasUpdates = false let hasUpdates = false
// //
for (const newMsg of newMessages) { for (const newMsg of newMessages) {
let isProcessed = false let isProcessed = false
// 1. // 1.
const tempMsgIndex = currentMessages.findIndex(existingMsg => { const tempMsgIndex = currentMessages.findIndex(existingMsg => {
if (!existingMsg._isTemp || existingMsg._failed) return false if (!existingMsg._isTemp || existingMsg._failed) return false
// //
const senderMatch = existingMsg.sender_id === newMsg.sender_id const senderMatch = existingMsg.sender_id === newMsg.sender_id
const typeMatch = existingMsg.msg_type === newMsg.msg_type const typeMatch = existingMsg.msg_type === newMsg.msg_type
// 10 // 10
const timeDiff = Math.abs(Date.now() - (existingMsg._timestamp || 0)) const timeDiff = Math.abs(Date.now() - (existingMsg._timestamp || 0))
const timeMatch = timeDiff < 10000 const timeMatch = timeDiff < 10000
// //
let contentMatch = false let contentMatch = false
if (existingMsg.msg_type === 1) { if (existingMsg.msg_type === 1) {
// //
const existingContent = existingMsg._tempContent || const existingContent = existingMsg._tempContent ||
(typeof existingMsg.content === 'object' ? existingMsg.content.message : existingMsg.content) (typeof existingMsg.content === 'object' ? existingMsg.content.message : existingMsg.content)
const newContent = typeof newMsg.content === 'object' ? newMsg.content.message : newMsg.content const newContent = typeof newMsg.content === 'object' ? newMsg.content.message : newMsg.content
contentMatch = existingContent === newContent contentMatch = existingContent === newContent
@ -317,10 +314,10 @@ const getMessages = async (isHistory = true, pageOverride = null) => {
} }
} }
} }
return senderMatch && typeMatch && contentMatch && timeMatch return senderMatch && typeMatch && contentMatch && timeMatch
}) })
if (tempMsgIndex !== -1) { if (tempMsgIndex !== -1) {
// //
console.log('找到匹配的临时消息,进行替换:', { console.log('找到匹配的临时消息,进行替换:', {
@ -337,31 +334,31 @@ const getMessages = async (isHistory = true, pageOverride = null) => {
// 2. ID // 2. ID
const existingMsgIndex = currentMessages.findIndex(existingMsg => { const existingMsgIndex = currentMessages.findIndex(existingMsg => {
if (existingMsg._isTemp) return false // if (existingMsg._isTemp) return false //
// 使ID // 使ID
const existingId = existingMsg.id || existingMsg._id || existingMsg.message_id const existingId = existingMsg.id || existingMsg._id || existingMsg.message_id
const newId = newMsg.id || newMsg._id || newMsg.message_id const newId = newMsg.id || newMsg._id || newMsg.message_id
if (existingId && newId && existingId === newId) { if (existingId && newId && existingId === newId) {
return true return true
} }
// ID使 // ID使
const existingContentStr = typeof existingMsg.content === 'object' ? const existingContentStr = typeof existingMsg.content === 'object' ?
(existingMsg.content.message || JSON.stringify(existingMsg.content)) : (existingMsg.content.message || JSON.stringify(existingMsg.content)) :
String(existingMsg.content) String(existingMsg.content)
const newContentStr = typeof newMsg.content === 'object' ? const newContentStr = typeof newMsg.content === 'object' ?
(newMsg.content.message || JSON.stringify(newMsg.content)) : (newMsg.content.message || JSON.stringify(newMsg.content)) :
String(newMsg.content) String(newMsg.content)
const compositeMatch = existingMsg.sender_id === newMsg.sender_id && const compositeMatch = existingMsg.sender_id === newMsg.sender_id &&
existingMsg.send_time === newMsg.send_time && existingMsg.send_time === newMsg.send_time &&
existingMsg.msg_type === newMsg.msg_type && existingMsg.msg_type === newMsg.msg_type &&
existingContentStr === newContentStr existingContentStr === newContentStr
return compositeMatch return compositeMatch
}) })
if (existingMsgIndex === -1) { if (existingMsgIndex === -1) {
// 3. // 3.
console.log('发现新消息:', { console.log('发现新消息:', {
@ -378,7 +375,7 @@ const getMessages = async (isHistory = true, pageOverride = null) => {
} }
} }
} }
if (hasUpdates) { if (hasUpdates) {
// //
messages.value = sortMessagesByTime(currentMessages) messages.value = sortMessagesByTime(currentMessages)
@ -411,7 +408,7 @@ const getMessages = async (isHistory = true, pageOverride = null) => {
if (!isPolling) { if (!isPolling) {
loadingMore.value = false loadingMore.value = false
} }
// loading finally // loading finally
if ((pageOverride ?? msgQuery.page) === 1 && isHistory) { if ((pageOverride ?? msgQuery.page) === 1 && isHistory) {
isLoadingMessages.value = false isLoadingMessages.value = false
@ -470,32 +467,32 @@ const loadMoreMessages = () => {
const send = async () => { const send = async () => {
const content = draft.value.trim() const content = draft.value.trim()
if (!content || isSending.value || isUploading.value) return if (!content || isSending.value || isUploading.value) return
// //
const now = Date.now() const now = Date.now()
if (now - lastSendTime.value < 500) { if (now - lastSendTime.value < 500) {
console.log('发送过于频繁,已忽略') console.log('发送过于频繁,已忽略')
return return
} }
// //
const hasSendingMessage = messages.value.some(msg => const hasSendingMessage = messages.value.some(msg =>
msg._sending && msg._sending &&
msg.sender_id === '888888' && msg.sender_id === '888888' &&
msg.msg_type === 1 && msg.msg_type === 1 &&
typeof msg.content === 'object' && typeof msg.content === 'object' &&
msg.content.message === content && msg.content.message === content &&
(now - (msg._timestamp || 0)) < 5000 (now - (msg._timestamp || 0)) < 5000
) )
if (hasSendingMessage) { if (hasSendingMessage) {
console.log('存在相同内容的发送中消息,已忽略') console.log('存在相同内容的发送中消息,已忽略')
return return
} }
isSending.value = true isSending.value = true
lastSendTime.value = now lastSendTime.value = now
// ID使 // ID使
const tempId = `temp_${Date.now()}_${Math.random().toString(36).substr(2, 9)}` const tempId = `temp_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`
const msg = { const msg = {
@ -511,14 +508,14 @@ const send = async () => {
_timestamp: now, _timestamp: now,
_tempContent: content // _tempContent: content //
} }
messages.value.push(msg) messages.value.push(msg)
draft.value = '' draft.value = ''
// //
setTimeout(() => scrollToBottom(), 100) setTimeout(() => scrollToBottom(), 100)
isAutoScroll.value = true isAutoScroll.value = true
// //
send_message({ send_message({
app_id: route.query.app_id, app_id: route.query.app_id,
@ -531,7 +528,7 @@ const send = async () => {
msg._sending = false msg._sending = false
msg._failed = false msg._failed = false
console.log('消息发送成功', response) console.log('消息发送成功', response)
// ID // ID
setTimeout(() => { setTimeout(() => {
getMessages(false, 1) // getMessages(false, 1) //
@ -566,21 +563,21 @@ const triggerImageInput = () => {
const handleImageChange = (e) => { const handleImageChange = (e) => {
const file = e.target.files[0] const file = e.target.files[0]
if (!file) return if (!file) return
// //
if (isUploading.value) { if (isUploading.value) {
console.log('图片正在上传中,请稍候') console.log('图片正在上传中,请稍候')
return return
} }
// 10MB // 10MB
if (file.size > 10 * 1024 * 1024) { if (file.size > 10 * 1024 * 1024) {
ElMessage({ type: 'error', message: '图片大小不能超过10MB' }) ElMessage({ type: 'error', message: '图片大小不能超过10MB' })
return return
} }
isUploading.value = true isUploading.value = true
// //
const url = URL.createObjectURL(file) const url = URL.createObjectURL(file)
const tempId = `temp_img_${Date.now()}_${Math.random().toString(36).substr(2, 9)}` const tempId = `temp_img_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`
@ -600,11 +597,11 @@ const handleImageChange = (e) => {
} }
messages.value.push(msg) messages.value.push(msg)
e.target.value = '' e.target.value = ''
// //
setTimeout(() => scrollToBottom(), 100) setTimeout(() => scrollToBottom(), 100)
isAutoScroll.value = true isAutoScroll.value = true
// //
const form = new FormData() const form = new FormData()
form.append('file', file) form.append('file', file)
@ -614,7 +611,7 @@ const handleImageChange = (e) => {
uploadFile(form, import.meta.env.VITE_APP_BASE_API + 'admin/upload/image').then((resp) => { uploadFile(form, import.meta.env.VITE_APP_BASE_API + 'admin/upload/image').then((resp) => {
// send_message // send_message
const imageUrl = import.meta.env.VITE_APP_BASE_API_img + resp.preview_image_url.replace('/api/', '') const imageUrl = import.meta.env.VITE_APP_BASE_API_img + resp.preview_image_url.replace('/api/', '')
send_message({ send_message({
app_id: route.query.app_id, app_id: route.query.app_id,
msg_type: 2, msg_type: 2,
@ -626,12 +623,12 @@ const handleImageChange = (e) => {
if (index !== -1) { if (index !== -1) {
messages.value.splice(index, 1) messages.value.splice(index, 1)
} }
// URL // URL
if (url.startsWith('blob:')) { if (url.startsWith('blob:')) {
URL.revokeObjectURL(url) URL.revokeObjectURL(url)
} }
// //
setTimeout(() => { setTimeout(() => {
getMessages(false, 1) getMessages(false, 1)
@ -661,22 +658,22 @@ const handleImageChange = (e) => {
const handleRetry = (id) => { const handleRetry = (id) => {
const msg = messages.value.find((m) => m._id === id) const msg = messages.value.find((m) => m._id === id)
if (!msg) return if (!msg) return
msg._failed = false msg._failed = false
msg._sending = true msg._sending = true
if (msg.msg_type === 0) { if (msg.msg_type === 0) {
send_message({ send_message({
app_id: route.query.app_id, app_id: route.query.app_id,
content: msg.content.content, content: msg.content.content,
msg_type: 1, msg_type: 1,
to_user_id: activeUser.value.sender_id to_user_id: activeUser.value.sender_id
}).then(() => { }).then(() => {
msg._sending = false msg._sending = false
}).catch(() => { }).catch(() => {
msg._sending = false; msg._sending = false;
msg._failed = true; msg._failed = true;
ElMessage({ type: 'error', message: '重发失败' }) ElMessage({ type: 'error', message: '重发失败' })
}) })
} else if (msg.msg_type === 2) { } else if (msg.msg_type === 2) {
const form = new FormData() const form = new FormData()
@ -684,12 +681,12 @@ const handleRetry = (id) => {
form.append('app_id', route.query.app_id) form.append('app_id', route.query.app_id)
form.append('msg_type', 2) form.append('msg_type', 2)
form.append('to_user_id', activeUser.value.sender_id) form.append('to_user_id', activeUser.value.sender_id)
send_message(form).then(() => { send_message(form).then(() => {
msg._sending = false msg._sending = false
}).catch(() => { }).catch(() => {
msg._sending = false; msg._sending = false;
msg._failed = true; msg._failed = true;
ElMessage({ type: 'error', message: '重发失败' }) ElMessage({ type: 'error', message: '重发失败' })
}) })
} }
} }
@ -784,37 +781,37 @@ const stopUserListTimer = () => {
// //
const formatLastMessage = (user) => { const formatLastMessage = (user) => {
if (!user.content) return '' if (!user.content) return ''
try { try {
// JSONcontent // JSONcontent
const contentObj = JSON.parse(user.content) const contentObj = JSON.parse(user.content)
// messages // messages
if (contentObj.messages) { if (contentObj.messages) {
const content = String(contentObj.messages).slice(0, 30) const content = String(contentObj.messages).slice(0, 30)
return content.length > 30 ? content + '...' : content return content.length > 30 ? content + '...' : content
} }
// text // text
if (contentObj.text) { if (contentObj.text) {
const content = String(contentObj.text).slice(0, 30) const content = String(contentObj.text).slice(0, 30)
return content.length > 30 ? content + '...' : content return content.length > 30 ? content + '...' : content
} }
// content // content
if (contentObj.content) { if (contentObj.content) {
const content = String(contentObj.content).slice(0, 30) const content = String(contentObj.content).slice(0, 30)
return content.length > 30 ? content + '...' : content return content.length > 30 ? content + '...' : content
} }
// JSON30 // JSON30
const content = JSON.stringify(contentObj).slice(0, 30) const content = JSON.stringify(contentObj).slice(0, 30)
return content.length > 30 ? content + '...' : content return content.length > 30 ? content + '...' : content
} catch (error) { } catch (error) {
// JSON // JSON
let content = String(user.content) let content = String(user.content)
// //
if (user.msg_type === 2) { if (user.msg_type === 2) {
content = '[图片] ' + content content = '[图片] ' + content
@ -825,7 +822,7 @@ const formatLastMessage = (user) => {
} else if (user.msg_type === 5) { } else if (user.msg_type === 5) {
content = '[视频] ' + content content = '[视频] ' + content
} }
// //
content = content.slice(0, 30) content = content.slice(0, 30)
return content.length > 30 ? content + '...' : content return content.length > 30 ? content + '...' : content
@ -855,25 +852,68 @@ const sendTemplateMessage = async (templateId) => {
"app_secret": route.query.app_secret, "app_secret": route.query.app_secret,
"template_id": route.query.template_id, "template_id": route.query.template_id,
"touser": activeUser.value.sender_id "touser": activeUser.value.sender_id
}).then((res) => {
if (res.succes == true) {
ElMessage({ type: 'success', message: '模板消息发送成功' })
// ID
setTimeout(() => {
getMessages(false, 1) //
}, 500)
} else {
ElMessage({ type: 'error', message: res.message || '模板消息发送失败' })
}
}) })
ElMessage({ type: 'success', message: '模板消息发送成功' })
// ID
setTimeout(() => {
getMessages(false, 1) //
}, 500)
} catch (error) { } catch (error) {
console.error('模板消息发送失败:', error) console.error('模板消息发送失败:', error)
ElMessage({ type: 'error', message: '模板消息发送失败' }) ElMessage({ type: 'error', message: '模板消息发送失败' })
} }
} }
const getAppStaus = async () => {
try {
const res = await checkMiniProgramStatus({
app_id: route.query.app_id
})
// appInfoStore.appDetail ref使 .value
if (appInfoStore && appInfoStore.appDetail) {
appInfoStore.appDetail.check_status_text = res.check_status_text || ''
}
return res
} catch (error) {
if (appInfoStore && appInfoStore.appDetail) {
appInfoStore.appDetail.check_status_text = ''
}
console.error('获取应用状态失败:', error)
return null
}
}
const startAppStatusTimer = () => {
if (appStatusTimer.value) clearInterval(appStatusTimer.value)
// 60
getAppStaus()
appStatusTimer.value = setInterval(() => {
if (!route.query.app_id) return
getAppStaus()
}, 60 * 1000)
}
const stopAppStatusTimer = () => {
if (appStatusTimer.value) {
clearInterval(appStatusTimer.value)
appStatusTimer.value = null
}
}
// //
onMounted(() => { onMounted(() => {
getUsers() getUsers()
getAppStaus()
// //
startUserListTimer() startUserListTimer()
// //
window.addEventListener('beforeunload', () => { window.addEventListener('beforeunload', () => {
stopMessageTimer() stopMessageTimer()
@ -984,24 +1024,30 @@ watch(messages, async (newVal, oldVal) => {
/* For WebKit-based browsers (Chrome, Safari, Edge Chromium) */ /* For WebKit-based browsers (Chrome, Safari, Edge Chromium) */
-webkit-overflow-scrolling: touch; -webkit-overflow-scrolling: touch;
} }
.chat-body::-webkit-scrollbar { .chat-body::-webkit-scrollbar {
width: 0px; width: 0px;
height: 0px; height: 0px;
} }
/* For Firefox */ /* For Firefox */
.chat-body { .chat-body {
scrollbar-width: none; /* Firefox */ scrollbar-width: none;
/* Firefox */
} }
/* For IE and older Edge */ /* For IE and older Edge */
.chat-body { .chat-body {
-ms-overflow-style: none; /* IE 10+ */ -ms-overflow-style: none;
/* IE 10+ */
} }
/* When loading messages during user switch, hide visual content to avoid jump/flicker */ /* When loading messages during user switch, hide visual content to avoid jump/flicker */
.chat-body--loading { .chat-body--loading {
opacity: 0; opacity: 0;
transition: opacity 180ms ease-in-out; transition: opacity 180ms ease-in-out;
pointer-events: none; /* prevent interaction during transient loading */ pointer-events: none;
/* prevent interaction during transient loading */
} }
// //