const app = getApp(); import { formatDate } from '../../static/lib/utils'; import { getLocalImUserId } from '../../utils/im-user.js'; import { resolveAvatarUrl, getDefaultAvatarUrl, resolveAvatarFromStorage, subscribeConfigAvatarRefresh, ensureAvatarUrl } from '../../utils/avatar.js'; import { fetchHistoryMessages, getOldestHistoryTimestamp, mergeHistoryMessages, waitChatImReady, } from '../../utils/chat-history.js'; import { matchAutoReply, getWelcomeText } from '../../utils/scriptService.js'; Page({ data: { teamId: '', teamName: 'ๅฎขๆœ', teamAvatar: '', currentUser: null, messages: [], inputText: '', showEmojiPanel: false, showPlusPanel: false, showOrderSender: false, loadingHistory: false, isRefreshing: false, lastTimestamp: null, hasMore: true, scrollToView: '', detailText: '', showDetailModal: false, lastTapTime: 0, lastTapMsgId: '', showOrderDetail: false, detailOrder: null, emojiList: ['๐Ÿ˜€','๐Ÿ˜','๐Ÿ˜‚','๐Ÿคฃ','๐Ÿ˜ƒ','๐Ÿ˜„','๐Ÿ˜…','๐Ÿ˜†','๐Ÿ˜‰','๐Ÿ˜Š','๐Ÿ˜‹','๐Ÿ˜Ž','๐Ÿ˜','๐Ÿ˜˜','๐Ÿ˜—','๐Ÿ˜™','๐Ÿ˜š','๐Ÿ™‚','๐Ÿค—','๐Ÿคฉ','๐Ÿค”','๐Ÿคจ','๐Ÿ˜','๐Ÿ˜‘','๐Ÿ˜ถ','๐Ÿ™„','๐Ÿ˜','๐Ÿ˜ฃ','๐Ÿ˜ฅ','๐Ÿ˜ฎ','๐Ÿค','๐Ÿ˜ฏ','๐Ÿ˜ช','๐Ÿ˜ซ','๐Ÿ˜ด','๐Ÿ˜Œ','๐Ÿ˜›','๐Ÿ˜œ','๐Ÿ˜','๐Ÿคค','๐Ÿ˜’','๐Ÿ˜“','๐Ÿ˜”','๐Ÿ˜•','๐Ÿ™ƒ','๐Ÿค‘','๐Ÿ˜ฒ','โ˜น','๐Ÿ™','๐Ÿ˜–','๐Ÿ˜ž','๐Ÿ˜Ÿ','๐Ÿ˜ค','๐Ÿ˜ข','๐Ÿ˜ญ','๐Ÿ˜ฆ','๐Ÿ˜ง','๐Ÿ˜จ','๐Ÿ˜ฉ','๐Ÿคฏ','๐Ÿ˜ฌ','๐Ÿ˜ฐ','๐Ÿ˜ฑ','๐Ÿฅต','๐Ÿฅถ','๐Ÿ˜ณ','๐Ÿคช','๐Ÿ˜ต','๐Ÿ˜ก','๐Ÿ˜ ','๐Ÿคฌ','๐Ÿ˜ท','๐Ÿค’','๐Ÿค•','๐Ÿคข','๐Ÿคฎ','๐Ÿคง','๐Ÿ˜‡','๐Ÿค ','๐Ÿคก','๐Ÿคฅ','๐Ÿคซ','๐Ÿคญ','๐Ÿง','๐Ÿค“','๐Ÿ˜ˆ','๐Ÿ‘ฟ','๐Ÿ‘น','๐Ÿ‘บ','๐Ÿ’€','๐Ÿ‘ป','๐Ÿ‘ฝ','๐Ÿค–','๐Ÿ’ฉ','๐Ÿ˜บ','๐Ÿ˜ธ','๐Ÿ˜น','๐Ÿ˜ป','๐Ÿ˜ผ','๐Ÿ˜ฝ','๐Ÿ™€','๐Ÿ˜ฟ','๐Ÿ˜พ'], pendingImage: '', pendingImageFile: null, keyboardHeight: 0, bottomSafeHeight: 140, defaultAvatarUrl: '', welcomeText: 'ไฝ ๅฅฝ๏ผŒ่ฏท้—ฎๆœ‰ไป€ไนˆๅฏไปฅๅธฎๅˆฐๆ‚จ็š„๏ผŸ', }, onLoad(options) { let teamId = '', teamName = 'ๅฎขๆœ', teamAvatar = ''; const defAvatar = getDefaultAvatarUrl(app); if (options.data) { try { const p = JSON.parse(decodeURIComponent(options.data)); teamId = p.teamId || ''; teamName = p.teamName || 'ๅฎขๆœ'; teamAvatar = p.teamAvatar || ''; } catch (e) { } } let teamAvatarResolved = teamAvatar ? ensureAvatarUrl(teamAvatar, app) : defAvatar; if (!teamAvatarResolved || !teamAvatarResolved.startsWith('http')) { teamAvatarResolved = defAvatar; } this.setData({ teamId, teamName, teamAvatar: teamAvatarResolved, defaultAvatarUrl: defAvatar }); wx.setNavigationBarTitle({ title: teamName }); this.initCurrentUser(); this.loadWelcomeScript(); this._unsubConfigAvatar = subscribeConfigAvatarRefresh(this, () => { const def = getDefaultAvatarUrl(app); this.setData({ defaultAvatarUrl: def, teamAvatar: def, }); this.refreshCurrentUserAvatar(); }); }, onUnload() { if (this._unsubConfigAvatar) { this._unsubConfigAvatar(); this._unsubConfigAvatar = null; } }, onShow() { const defAvatar = getDefaultAvatarUrl(app); const patch = { defaultAvatarUrl: defAvatar, teamAvatar: defAvatar, }; this.setData(patch); this.refreshCurrentUserAvatar(); this.autoConnect(); }, refreshCurrentUserAvatar() { const defAvatar = getDefaultAvatarUrl(app); const cu = this.data.currentUser; if (!cu) return; const avatar = resolveAvatarUrl(cu.avatar || wx.getStorageSync('touxiang'), app) || defAvatar; if (avatar !== cu.avatar) { this.setData({ currentUser: { ...cu, avatar } }); } }, onHide() { this.clearAllListeners(); }, async loadWelcomeScript() { try { const text = await getWelcomeText(); if (!text) return; this.setData({ welcomeText: text }); const msgs = this.data.messages; const idx = msgs.findIndex((m) => m.messageId === 'welcome-cs'); if (idx >= 0) { const updated = [...msgs]; updated[idx] = { ...updated[idx], payload: { ...updated[idx].payload, text } }; this.setData({ messages: updated }); } } catch (e) { console.warn('loadWelcomeScript failed', e); } }, async autoConnect() { const targetUserId = getLocalImUserId(app); if (!targetUserId) return; const ready = async () => { this.setupAllListeners(); await this.loadHistory(true); }; try { const ok = await waitChatImReady(app, 12000); if (ok) { await ready(); return; } } catch (e) { console.warn('[ๅฎขๆœ] IM ่ฟžๆŽฅๅคฑ่ดฅ', e); } if (this.isConnected()) { await ready(); } else { this.connectGoEasy(targetUserId); } }, isConnected() { const s = wx.goEasy.getConnectionStatus ? wx.goEasy.getConnectionStatus() : 'disconnected'; return s === 'connected' || s === 'reconnected'; }, connectGoEasy(userId) { const { currentUser } = this.data; if (!currentUser) return; wx.goEasy.connect({ id: userId, data: { name: currentUser.name, avatar: resolveAvatarUrl(currentUser.avatar, app) || getDefaultAvatarUrl(app) }, onSuccess: () => { this.setupAllListeners(); this.loadHistory(true); }, onFailed: (error) => { if (error.code === 408) { this.setupAllListeners(); this.loadHistory(true); } else { wx.showToast({ title: '่ฟžๆŽฅๅคฑ่ดฅ๏ผŒ่ฏท้‡่ฏ•', icon: 'none' }); } } }); }, setupAllListeners() { this.clearAllListeners(); this.listenNewMsg(); this.listenRecall(); this.listenDelete(); this.listenRead(); }, clearAllListeners() { if (this._msgHandler) { wx.goEasy.im.off(wx.GoEasy.IM_EVENT.CS_MESSAGE_RECEIVED, this._msgHandler); this._msgHandler = null; } if (this._recallHandler) { wx.goEasy.im.off(wx.GoEasy.IM_EVENT.MESSAGE_RECALLED, this._recallHandler); this._recallHandler = null; } if (this._deleteHandler) { wx.goEasy.im.off(wx.GoEasy.IM_EVENT.MESSAGE_DELETED, this._deleteHandler); this._deleteHandler = null; } if (this._readHandler) { wx.goEasy.im.off(wx.GoEasy.IM_EVENT.MESSAGE_READ, this._readHandler); this._readHandler = null; } }, initCurrentUser() { const imId = getLocalImUserId(app); const uid = wx.getStorageSync('uid'); const defAvatar = getDefaultAvatarUrl(app); const avatar = resolveAvatarFromStorage(app) || defAvatar; this.setData({ currentUser: { id: imId || ('Boss' + uid), name: app.globalData.currentUser?.name || wx.getStorageSync('nicheng') || `็”จๆˆท${uid}`, avatar, }, }); }, fixAvatar(url) { return resolveAvatarUrl(url, app); }, buildImageFile(tempPath, size) { return { path: tempPath, tempFilePath: tempPath, size: size || 0, }; }, normalizeMessage(msg) { if (!msg) return msg; if (!msg.payload) msg.payload = {}; if (msg.type === 'image') { const p = msg.payload; if (!p.url) p.url = p.thumbnail || p.thumb || p.imageUrl || ''; } return msg; }, mergeSentMessage(localId, sent) { if (!sent) { this.updateMsg(localId, 'success'); return; } const msgs = this.data.messages.map((m) => { if (m.messageId !== localId) return m; return this.normalizeMessage({ ...m, ...sent, status: 'success', messageId: sent.messageId || m.messageId, payload: sent.payload || m.payload, senderData: m.senderData || sent.senderData, }); }); this.setData({ messages: msgs }); }, mapHistoryMessage(m, idx, list) { m = this.normalizeMessage(m); m.formattedTime = formatDate(m.timestamp); if (idx === 0) m.showTime = true; else m.showTime = (m.timestamp - list[idx - 1].timestamp) / 60000 > 5; if (m.senderId === this.data.currentUser?.id) { const av = resolveAvatarUrl(this.data.currentUser.avatar, app) || this.data.defaultAvatarUrl; m.senderData = { name: this.data.currentUser.name, avatar: av }; } else { const av = resolveAvatarUrl(this.data.teamAvatar, app) || this.data.defaultAvatarUrl; m.senderData = { name: this.data.teamName, avatar: av }; } return m; }, injectWelcomeMessage(messages) { const list = Array.isArray(messages) ? [...messages] : []; const welcomeId = 'welcome-cs'; if (!list.some((m) => m.messageId === welcomeId)) { const welcome = this.createWelcomeMessage(); welcome.messageId = welcomeId; welcome.showTime = true; list.unshift(welcome); } return list; }, // ๅކๅฒๆถˆๆฏ async loadHistory(refresh) { if (!refresh && !this.data.hasMore) return; if (this.data.loadingHistory) return; if (!this.data.teamId) return; this.setData({ loadingHistory: true }); try { const ready = await waitChatImReady(app, 12000); if (!ready) { wx.showToast({ title: 'ๆถˆๆฏๆœๅŠกๆœช่ฟžๆŽฅ๏ผŒ่ฏท็จๅŽ้‡่ฏ•', icon: 'none' }); return; } const ts = refresh ? null : getOldestHistoryTimestamp(this.data.messages, this.data.lastTimestamp); if (!refresh && ts == null) { this.setData({ hasMore: false }); wx.showToast({ title: 'ๆฒกๆœ‰ๆ›ดๆ—ฉ็š„ๆถˆๆฏไบ†', icon: 'none' }); return; } const raw = await fetchHistoryMessages({ scene: wx.GoEasy.IM_SCENE.CS, id: this.data.teamId, lastTimestamp: ts, limit: 20, }); let merged = mergeHistoryMessages({ incoming: raw, existing: this.data.messages, refresh, limit: 20, mapMessage: (m, idx, list) => this.mapHistoryMessage(m, idx, list), }); merged.messages = this.injectWelcomeMessage(merged.messages); merged.lastTimestamp = getOldestHistoryTimestamp(merged.messages, merged.lastTimestamp); const update = { messages: merged.messages, hasMore: merged.hasMore, lastTimestamp: merged.lastTimestamp, }; if (refresh) update.scrollToView = 'msg-bottom'; this.setData(update); } catch (e) { console.warn('[ๅฎขๆœ] ๅކๅฒๆถˆๆฏๅŠ ่ฝฝๅคฑ่ดฅ', e); if (!refresh) { wx.showToast({ title: 'ๅŠ ่ฝฝๅކๅฒๆถˆๆฏๅคฑ่ดฅ', icon: 'none' }); } } finally { this.setData({ loadingHistory: false }); } }, createWelcomeMessage() { return { messageId: 'welcome-cs', type: 'text', timestamp: Date.now(), senderId: 'system', receiverId: this.data.currentUser?.id, senderData: { name: this.data.teamName || 'ๅฎขๆœ', avatar: resolveAvatarUrl(this.data.teamAvatar, app) || this.data.defaultAvatarUrl, }, payload: { text: this.data.welcomeText || 'ไฝ ๅฅฝ๏ผŒ่ฏท้—ฎๆœ‰ไป€ไนˆๅฏไปฅๅธฎๅˆฐๆ‚จ็š„๏ผŸ' }, status: 'success', read: false, formattedTime: formatDate(Date.now()), showTime: true }; }, // ๆŽฅๆ”ถๆ–ฐๅฎขๆœๆถˆๆฏ listenNewMsg() { if (this._msgHandler) wx.goEasy.im.off(wx.GoEasy.IM_EVENT.CS_MESSAGE_RECEIVED, this._msgHandler); this._msgHandler = (msg) => { if (msg.teamId !== this.data.teamId && msg.to !== this.data.teamId) return; if (msg.senderId === this.data.currentUser.id) return; msg = this.normalizeMessage(msg); msg.formattedTime = formatDate(msg.timestamp); msg.senderData = { name: this.data.teamName, avatar: resolveAvatarUrl(this.data.teamAvatar, app) || this.data.defaultAvatarUrl, }; const msgs = this.data.messages; const last = msgs.length ? msgs[msgs.length-1] : null; msg.showTime = last ? (msg.timestamp - last.timestamp) / 60000 > 5 : true; if (msgs.some(m => m.messageId === msg.messageId)) return; this.setData({ messages: [...msgs, msg], scrollToView: 'msg-bottom' }); this.markCsMessageAsRead(); }; wx.goEasy.im.on(wx.GoEasy.IM_EVENT.CS_MESSAGE_RECEIVED, this._msgHandler); }, markCsMessageAsRead() { if (!this.data.teamId) return; wx.goEasy.im.markMessageAsRead({ type: wx.GoEasy.IM_SCENE.CS, id: this.data.teamId }); }, // ๅทฒ่ฏปๅ›žๆ‰ง็›‘ๅฌ listenRead() { if (this._readHandler) wx.goEasy.im.off(wx.GoEasy.IM_EVENT.MESSAGE_READ, this._readHandler); this._readHandler = (msgs) => { if (!msgs || !msgs.length) return; const ids = new Set(msgs.map(m => m.messageId)); const newMsgs = this.data.messages.map(m => ids.has(m.messageId) ? { ...m, read: true } : m); this.setData({ messages: newMsgs }); }; wx.goEasy.im.on(wx.GoEasy.IM_EVENT.MESSAGE_READ, this._readHandler); }, // ๆ’คๅ›ž็›‘ๅฌ listenRecall() { if (this._recallHandler) wx.goEasy.im.off(wx.GoEasy.IM_EVENT.MESSAGE_RECALLED, this._recallHandler); this._recallHandler = (msgs) => { if (!msgs || !msgs.length) return; const ids = new Set(msgs.map(m => m.messageId)); this.setData({ messages: this.data.messages.filter(m => !ids.has(m.messageId)) }); }; wx.goEasy.im.on(wx.GoEasy.IM_EVENT.MESSAGE_RECALLED, this._recallHandler); }, // ๅˆ ้™ค็›‘ๅฌ listenDelete() { if (this._deleteHandler) wx.goEasy.im.off(wx.GoEasy.IM_EVENT.MESSAGE_DELETED, this._deleteHandler); this._deleteHandler = (msgs) => { if (!msgs || !msgs.length) return; const ids = new Set(msgs.map(m => m.messageId)); this.setData({ messages: this.data.messages.filter(m => !ids.has(m.messageId)) }); }; wx.goEasy.im.on(wx.GoEasy.IM_EVENT.MESSAGE_DELETED, this._deleteHandler); }, // ้ขๆฟๆŽงๅˆถ togglePlusPanel() { this.setData({ showPlusPanel: !this.data.showPlusPanel, showEmojiPanel: false }); }, closePlusPanel() { this.setData({ showPlusPanel: false }); }, openEmojiFromPlus() { this.setData({ showPlusPanel: false, showEmojiPanel: true }); }, toggleEmoji() { this.setData({ showEmojiPanel: !this.data.showEmojiPanel }); }, insertEmoji(e) { this.setData({ inputText: this.data.inputText + e.currentTarget.dataset.emoji }); }, closeEmojiPanel() { this.setData({ showEmojiPanel: false }); }, // ๅ‘้€ๆถˆๆฏ onInput(e) { this.setData({ inputText: e.detail.value }); }, chooseImage() { const that = this; wx.chooseMedia({ count: 1, mediaType: ['image'], sizeType: ['compressed'], sourceType: ['album', 'camera'], success(res) { const file = (res.tempFiles && res.tempFiles[0]) || null; if (!file || !file.tempFilePath) return; that.setData({ pendingImage: file.tempFilePath, pendingImageFile: that.buildImageFile(file.tempFilePath, file.size), }); }, fail() { wx.showToast({ title: '้€‰ๆ‹ฉๅ›พ็‰‡ๅคฑ่ดฅ', icon: 'none' }); }, }); }, clearPendingImage() { this.setData({ pendingImage: '', pendingImageFile: null }); }, sendMessage() { if (this.data.pendingImage) { this.sendImageMsg(this.data.pendingImage, this.data.pendingImageFile); this.setData({ pendingImage: '', pendingImageFile: null }); return; } const text = this.data.inputText.trim(); if (!text) return; this.sendText(text); this.setData({ inputText: '' }); }, sendText(text) { const { currentUser, teamId, teamName, teamAvatar } = this.data; if (!teamId || !currentUser) return; const id = 'local-'+Date.now(); const local = { messageId:id, type:'text', timestamp:Date.now(), senderId:currentUser.id, receiverId:teamId, senderData: { name: currentUser.name, avatar: currentUser.avatar }, payload:{text}, status:'sending', read:false, formattedTime:formatDate(Date.now()), showTime:this.needShow(Date.now()) }; this.setData({ messages: [...this.data.messages, local], scrollToView: 'msg-bottom' }); const msg = wx.goEasy.im.createTextMessage({ text, to:{ type:wx.GoEasy.IM_SCENE.CS, id:teamId, data:{ name:teamName, avatar:teamAvatar } } }); wx.goEasy.im.sendMessage({ message:msg, onSuccess:() => { this.updateMsg(id, 'success'); this.tryAutoReply(text); }, onFailed:() => this.updateMsg(id, 'failed') }); }, async tryAutoReply(userText) { try { const result = await matchAutoReply(userText); if (result && result.matched && result.content) { this.insertCsAutoReply(result); } } catch (e) { console.warn('tryAutoReply failed', e); } }, insertCsAutoReply(result) { const replyType = (result && result.reply_type) || 'text'; const content = (result && result.content) || ''; if (!content) return; const isImage = replyType === 'image'; const msg = { messageId: 'auto-reply-' + Date.now(), type: isImage ? 'image' : 'text', timestamp: Date.now(), senderId: this.data.teamId, receiverId: this.data.currentUser?.id, senderData: { name: this.data.teamName, avatar: resolveAvatarUrl(this.data.teamAvatar, app) || this.data.defaultAvatarUrl, }, payload: isImage ? { url: content } : { text: content }, status: 'success', read: false, formattedTime: formatDate(Date.now()), showTime: this.needShow(Date.now()), }; if (isImage) msg.imageUrl = content; this.setData({ messages: [...this.data.messages, msg], scrollToView: 'msg-bottom', }); }, sendImageMsg(filePath, fileObj) { const { currentUser, teamId, teamName, teamAvatar } = this.data; if (!teamId || !currentUser || !filePath) return; if (!wx.goEasy || !wx.goEasy.im) { wx.showToast({ title: '่Šๅคฉๆœช่ฟžๆŽฅ', icon: 'none' }); return; } const id = 'img-'+Date.now(); const imageFile = fileObj || this.buildImageFile(filePath, 0); const local = { messageId:id, type:'image', timestamp:Date.now(), senderId:currentUser.id, receiverId:teamId, senderData: { name: currentUser.name, avatar: currentUser.avatar }, payload:{url:filePath}, status:'sending', read:false, formattedTime:formatDate(Date.now()), showTime:this.needShow(Date.now()) }; this.setData({ messages: [...this.data.messages, local], scrollToView: 'msg-bottom' }); const imgMsg = wx.goEasy.im.createImageMessage({ file: imageFile, to:{ type:wx.GoEasy.IM_SCENE.CS, id:teamId, data:{ name:teamName, avatar:teamAvatar } } }); wx.goEasy.im.sendMessage({ message: imgMsg, onSuccess: (sent) => this.mergeSentMessage(id, sent), onFailed: (error) => { console.error('[ๅฎขๆœ] ๅ›พ็‰‡ๅ‘้€ๅคฑ่ดฅ', error); this.updateMsg(id, 'failed'); wx.showToast({ title: 'ๅ›พ็‰‡ๅ‘้€ๅคฑ่ดฅ', icon: 'none' }); }, }); }, // ่ฎขๅ•ๅ‘้€ openOrderSender() { this.setData({ showOrderSender: true, showPlusPanel: false }); }, closeOrderSender() { this.setData({ showOrderSender: false }); }, onSendOrder(e) { const order = e.detail.order; if (!order) return; this.sendOrderMessage(order); }, sendOrderMessage(order) { const { currentUser, teamId, teamName, teamAvatar } = this.data; if (!teamId || !currentUser) return; const id = 'order-'+Date.now(); const local = { messageId:id, type:'order', timestamp:Date.now(), senderId:currentUser.id, receiverId:teamId, senderData: { name: currentUser.name, avatar: currentUser.avatar }, payload: order, status:'sending', read:false, formattedTime:formatDate(Date.now()), showTime:this.needShow(Date.now()) }; this.setData({ messages: [...this.data.messages, local], scrollToView: 'msg-bottom' }); const customMsg = wx.goEasy.im.createCustomMessage({ type:'order', payload: order, to:{ type:wx.GoEasy.IM_SCENE.CS, id:teamId, data:{ name:teamName, avatar:teamAvatar } } }); wx.goEasy.im.sendMessage({ message:customMsg, onSuccess:() => this.updateMsg(id, 'success'), onFailed:() => this.updateMsg(id, 'failed') }); }, updateMsg(id, status) { this.setData({ messages: this.data.messages.map(m => m.messageId === id ? { ...m, status } : m) }); }, needShow(ts) { const ms = this.data.messages; if (!ms.length) return true; const last = ms[ms.length-1]; return (ts - last.timestamp) / 60000 > 5; }, // ๆถˆๆฏๆ“ไฝœ onBubbleTap(e) { const msgId = e.currentTarget.dataset.messageid; const text = e.currentTarget.dataset.text; const now = Date.now(); if (msgId === this.data.lastTapMsgId && now - this.data.lastTapTime < 350) { this.setData({ detailText: text, showDetailModal: true, lastTapTime:0, lastTapMsgId:'' }); } else { this.data.lastTapTime = now; this.data.lastTapMsgId = msgId; } }, hideDetail() { this.setData({ showDetailModal: false }); }, copyDetail() { wx.setClipboardData({ data: this.data.detailText }); }, showAction(e) { const msgId = e.currentTarget.dataset.messageid; if (!msgId) return; const msg = this.data.messages.find(m => m.messageId === msgId); if (!msg || msg.messageId.startsWith('local-')) return; const itemList = ['ๅคๅˆถ', 'ๅˆ ้™ค']; if (msg.senderId === this.data.currentUser.id && (Date.now() - msg.timestamp < 120000)) { itemList.push('ๆ’คๅ›ž'); } wx.showActionSheet({ itemList, success: (res) => { if (itemList[res.tapIndex] === 'ๅคๅˆถ') { if (msg.type === 'text') wx.setClipboardData({ data: msg.payload.text }); } else if (itemList[res.tapIndex] === 'ๅˆ ้™ค') { this.deleteMsg(msg); } else if (itemList[res.tapIndex] === 'ๆ’คๅ›ž') { this.recallMsg(msg); } } }); }, deleteMsg(msg) { wx.showModal({ title: 'ๆ็คบ', content: 'ไป…่‡ชๅทฑ็œ‹ไธๅˆฐ', success: (res) => { if (res.confirm) { wx.goEasy.im.deleteMessage({ messages: [msg] }); this.setData({ messages: this.data.messages.filter(m => m.messageId !== msg.messageId) }); } } }); }, recallMsg(msg) { wx.showModal({ title: 'ๆ็คบ', content: 'ๆ’คๅ›žๅŽๅฏนๆ–นไนŸ็œ‹ไธๅˆฐ', success: (res) => { if (res.confirm) { wx.goEasy.im.recallMessage({ messages: [msg] }); const newMsgs = this.data.messages.map(m => { if (m.messageId === msg.messageId) return { ...m, type:'text', payload:{text:'ไฝ ๆ’คๅ›žไบ†ไธ€ๆกๆถˆๆฏ'}, recalled:true }; return m; }); this.setData({ messages: newMsgs }); } } }); }, previewImage(e) { const url = e.currentTarget.dataset.url; if (url) wx.previewImage({ urls: [url], current: url }); }, // ่ฎขๅ•่ฏฆๆƒ…ๅผน็ช— showOrderDetailPopup(e) { const order = e.currentTarget.dataset.order; this.setData({ showOrderDetail: true, detailOrder: order }); }, hideOrderDetail() { this.setData({ showOrderDetail: false, detailOrder: null }); }, onKeyboardHeightChange(e) { this.setData({ keyboardHeight: e.detail.height }); }, onTeamAvatarError() { this.setData({ teamAvatar: getDefaultAvatarUrl(app) }); }, onSelfAvatarError() { const def = getDefaultAvatarUrl(app); const cu = this.data.currentUser; if (cu) { this.setData({ currentUser: { ...cu, avatar: def } }); } }, onPullDownRefresh() { if (this.data.loadingHistory) return; if (!this.data.hasMore) { wx.showToast({ title: 'ๆฒกๆœ‰ๆ›ดๆ—ฉ็š„ๆถˆๆฏไบ†', icon: 'none' }); this.setData({ isRefreshing: false }); wx.stopPullDownRefresh(); return; } this.setData({ isRefreshing: true }); this.loadHistory(false).finally(() => { this.setData({ isRefreshing: false }); wx.stopPullDownRefresh(); }); } });