// 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' }) } })