ai-c/pages/support/support.js
2026-02-02 18:21:32 +08:00

215 lines
4.9 KiB
JavaScript

// pages/support/support.js
const app = getApp()
const api = require('../../utils/api')
const util = require('../../utils/util')
const imageUrl = require('../../utils/imageUrl')
Page({
data: {
statusBarHeight: 44,
navHeight: 96,
myAvatar: '/images/default-avatar.svg',
messages: [],
inputText: '',
inputFocus: false,
isTyping: false,
ticketId: '',
scrollIntoView: '',
scrollTop: 0,
pollingTimer: null
},
onLoad() {
const { statusBarHeight, navHeight, userInfo } = app.globalData
const myAvatar = imageUrl.getAvatarUrl(userInfo?.avatar)
this.setData({
statusBarHeight,
navHeight,
myAvatar
})
this.initSupport()
},
onAvatarError() {
this.setData({
myAvatar: '/images/default-avatar.svg'
})
},
onUnload() {
this.stopPolling()
},
onHide() {
this.stopPolling()
},
onShow() {
if (this.data.ticketId) {
this.startPolling()
}
},
/**
* 初始化客服会话
*/
async initSupport() {
wx.showLoading({ title: '加载中...' })
try {
const guestId = wx.getStorageSync('guestId') || util.generateId()
if (!wx.getStorageSync('guestId')) {
wx.setStorageSync('guestId', guestId)
}
// 获取已有咨询列表
const res = await api.customerService.getList(guestId)
const data = res.data || {}
const tickets = data.tickets || []
if (tickets.length > 0) {
// 使用最近的一个工单
const latestTicket = tickets[0]
this.setData({ ticketId: latestTicket.id })
await this.loadMessages(latestTicket.id)
} else {
// 如果没有工单,可以在首次发送消息时创建
console.log('[support] No existing tickets found.')
}
} catch (err) {
console.error('[support] initSupport error:', err)
} finally {
wx.hideLoading()
this.startPolling()
}
},
/**
* 加载消息历史
*/
async loadMessages(ticketId) {
try {
const res = await api.customerService.getDetail(ticketId)
if (res.success && res.data) {
const messages = res.data.messages.map(msg => ({
id: msg.id,
isMe: msg.senderType === 'user',
text: msg.content,
time: util.formatTime(new Date(msg.createdAt), 'HH:mm'),
senderName: msg.senderName
}))
// 如果有新消息才更新,避免闪烁
if (JSON.stringify(messages) !== JSON.stringify(this.data.messages)) {
this.setData({ messages }, () => {
this.scrollToBottom()
})
}
}
} catch (err) {
console.error('[support] loadMessages error:', err)
}
},
/**
* 发送消息
*/
async onSend() {
const content = this.data.inputText.trim()
if (!content || this.isSending) return
this.isSending = true
const tempId = util.generateId()
const now = new Date()
// 先在本地显示
const userMsg = {
id: tempId,
isMe: true,
text: content,
time: util.formatTime(now, 'HH:mm')
}
this.setData({
messages: [...this.data.messages, userMsg],
inputText: '',
inputFocus: true
}, () => {
this.scrollToBottom()
})
try {
if (this.data.ticketId) {
// 回复已有工单
await api.customerService.reply({
ticketId: this.data.ticketId,
content: content,
userName: app.globalData.userInfo?.nickname || '访客'
})
} else {
// 创建新工单
const guestId = wx.getStorageSync('guestId')
const res = await api.customerService.create({
category: 'other',
content: content,
userName: app.globalData.userInfo?.nickname || '访客',
guestId: guestId
})
if (res.success && res.data) {
this.setData({ ticketId: res.data.ticketId })
}
}
// 发送后立即拉取一次
if (this.data.ticketId) {
await this.loadMessages(this.data.ticketId)
}
} catch (err) {
console.error('[support] send message error:', err)
wx.showToast({ title: '发送失败', icon: 'none' })
} finally {
this.isSending = false
}
},
onInput(e) {
this.setData({ inputText: e.detail.value })
},
/**
* 开始轮询
*/
startPolling() {
this.stopPolling()
this.data.pollingTimer = setInterval(() => {
if (this.data.ticketId) {
this.loadMessages(this.data.ticketId)
}
}, 4000) // 每4秒轮询一次
},
/**
* 停止轮询
*/
stopPolling() {
if (this.data.pollingTimer) {
clearInterval(this.data.pollingTimer)
this.data.pollingTimer = null
}
},
onBack() {
wx.navigateBack()
},
onTapChatArea() {
this.setData({ inputFocus: false })
},
scrollToBottom() {
this.setData({
scrollIntoView: 'chat-bottom-anchor'
})
}
})