微信小程序页面迁移校验之前 P5任务处理之前

This commit is contained in:
Neo
2026-03-09 01:19:21 +08:00
parent 263bf96035
commit 6e20987d2f
1112 changed files with 153824 additions and 219694 deletions

View File

@@ -0,0 +1,168 @@
// pages/chat/chat.ts — AI 对话页
import { mockChatMessages } from '../../utils/mock-data'
import type { ChatMessage } from '../../utils/mock-data'
import { simulateStreamOutput } from '../../utils/chat'
/** 将 referenceCard.data (Record<string,string>) 转为数组供 WXML wx:for 渲染 */
function toDataList(data?: Record<string, string>): Array<{ key: string; value: string }> {
if (!data) return []
return Object.keys(data).map((k) => ({ key: k, value: data[k] }))
}
/** 为消息列表中的 referenceCard 补充 dataList 字段 */
function enrichMessages(msgs: ChatMessage[]) {
return msgs.map((m) => ({
...m,
referenceCard: m.referenceCard
? { ...m.referenceCard, dataList: toDataList(m.referenceCard.data) }
: undefined,
}))
}
/** Mock AI 回复模板 */
const mockAIReplies = [
'根据数据分析,这位客户近期消费频次有所下降,建议安排一次主动回访,了解具体原因。',
'好的,我已经记录了你的需求。下次回访时我会提醒你。',
'这位客户偏好中式台球,最近对斯诺克也产生了兴趣,可以推荐相关课程。',
]
Page({
data: {
/** 页面状态 */
pageState: 'loading' as 'loading' | 'empty' | 'normal',
/** 消息列表 */
messages: [] as Array<ChatMessage & { referenceCard?: ChatMessage['referenceCard'] & { dataList?: Array<{ key: string; value: string }> } }>,
/** 输入框内容 */
inputText: '',
/** AI 正在流式回复 */
isStreaming: false,
/** 流式输出中的内容 */
streamingContent: '',
/** 滚动锚点 */
scrollToId: '',
/** 页面顶部引用卡片(从其他页面跳转时) */
referenceCard: null as { title: string; summary: string } | null,
/** 客户 ID */
customerId: '',
},
/** 消息计数器,用于生成唯一 ID */
_msgCounter: 0,
onLoad(options) {
const customerId = options?.customerId || ''
this.setData({ customerId })
this.loadMessages(customerId)
},
/** 加载消息Mock */
loadMessages(customerId: string) {
this.setData({ pageState: 'loading' })
setTimeout(() => {
// TODO: 替换为真实 API 调用
const messages = enrichMessages(mockChatMessages)
this._msgCounter = messages.length
// 如果携带 customerId显示引用卡片
const referenceCard = customerId
? { title: '客户详情', summary: `正在查看客户 ${customerId} 的相关信息` }
: null
const isEmpty = messages.length === 0 && !referenceCard
this.setData({
pageState: isEmpty ? 'empty' : 'normal',
messages,
referenceCard,
})
// 滚动到底部
this.scrollToBottom()
}, 500)
},
/** 输入框内容变化 */
onInputChange(e: WechatMiniprogram.Input) {
this.setData({ inputText: e.detail.value })
},
/** 发送消息 */
onSendMessage() {
const text = this.data.inputText.trim()
if (!text || this.data.isStreaming) return
this._msgCounter++
const userMsg = {
id: `msg-user-${this._msgCounter}`,
role: 'user' as const,
content: text,
timestamp: new Date().toISOString(),
}
const messages = [...this.data.messages, userMsg]
this.setData({
messages,
inputText: '',
pageState: 'normal',
})
this.scrollToBottom()
// 模拟 AI 回复
setTimeout(() => {
this.triggerAIReply()
}, 300)
},
/** 触发 AI 流式回复 */
triggerAIReply() {
this._msgCounter++
const aiMsgId = `msg-ai-${this._msgCounter}`
const replyText = mockAIReplies[this._msgCounter % mockAIReplies.length]
// 先添加空的 AI 消息占位
const aiMsg = {
id: aiMsgId,
role: 'assistant' as const,
content: '',
timestamp: new Date().toISOString(),
}
const messages = [...this.data.messages, aiMsg]
this.setData({
messages,
isStreaming: true,
streamingContent: '',
})
this.scrollToBottom()
// 流式输出
const aiIndex = messages.length - 1
simulateStreamOutput(replyText, (partial: string) => {
const key = `messages[${aiIndex}].content`
this.setData({
[key]: partial,
streamingContent: partial,
})
this.scrollToBottom()
}).then(() => {
this.setData({
isStreaming: false,
streamingContent: '',
})
})
},
/** 滚动到底部 */
scrollToBottom() {
// 使用 nextTick 确保 DOM 更新后再滚动
setTimeout(() => {
this.setData({ scrollToId: '' })
setTimeout(() => {
this.setData({ scrollToId: 'scroll-bottom' })
}, 50)
}, 50)
},
})