import request, { streamRequest } from "@/utils/request"; import { copyText } from "@/utils/common" let outTimer; //输出定时器 let outStrList = [];//输出字符串数组 const app = getApp() let chat_uuid //工具 let toolText = '' let toolInfo = {} Page({ data: { chatList: [], //会话记录 aiStatus: 3,//1表示接口响应中,2表示接口响应完毕,3表示完全输出完毕 keyboardBottom: 0, //底部键盘高度 scrollTop: 0, //滚动条位置 inputText: '',//输入框内容 }, onLoad() { let str = `您好!我是术极守护AI助手 🤖\n\n我可以为您解答术后康复相关的问题。请注意,我只能提供基于康复指南的一般性建议,如遇紧急情况请立即就医或联系您的主治医生。` let mdResult = app.towxml(str, 'markdown', { base: "www.xxx.com", }); let list = [ { loading: false, end: true, chat_type: 2, md_content: mdResult, chat_content: str, quick_btn: [ { text: '🩹伤口有轻微渗液怎么办?' }, { text: '🌡️术后轻微发烧正常吗?' }, { text: '🚿什么时候可以洗澡?' }, { text: '🥗饮食需要注意什么?' } ] } ] this.setData({ chatList: list }) }, onShow() { this.getTabBar((tabBar) => { tabBar.setData({ selected: 'AI助手', }) }) }, //监听键盘弹出 bindkeyboardheightchange(event) { let height = event.detail.height let { keyboardBottom } = this.data let scrollTop = 0 //获取当前滚动距离 const query = wx.createSelectorQuery().in(this) query.select(".silder").scrollOffset() query.exec(function (res) { scrollTop = res[0].scrollTop }) this.setData({ keyboardBottom: height > 0 ? height - 50 : keyboardBottom, }, () => { if (height > 0) { setTimeout(() => { this.setData({ scrollTop: scrollTop + height }) }, 300) } }) }, //键盘失去焦点 bindkeyboardBlur() { let { scrollTop, keyboardBottom } = this.data let endScrollTop = scrollTop - keyboardBottom this.setData({ keyboardBottom: 0 }, () => { this.setData({ scrollTop: endScrollTop }) }) }, //监听输入框内容 changeInput(e) { let value = e.detail.value this.setData({ inputText: value }) }, async handQuick(e) { let { data } = e.currentTarget.dataset this.pushUserTemplate(data.text) this.sendMessage(data.text) }, // 文字提交发送 submitInput() { let { inputText, aiStatus } = this.data if (!inputText.trim() == '' && aiStatus) { console.log('111--'); this.pushUserTemplate(inputText) this.sendMessage(inputText) } }, //发送聊天 async sendMessage(text) { let { chatList, aiStatus } = this.data if (aiStatus == 3) { let that = this //初始化变量 toolText = '' toolInfo = {} chat_uuid = String(new Date().getTime()) this.setData({ aiStatus: 1 }) //输出 this.timerOutput() //请求 streamRequest("/ai/chat", { message: text, chat_uuid: chat_uuid, }, this.onChunkReceived).then(() => { console.log('-------------成功-----------'); if (this.data.aiStatus != 3) { that.setData({ aiStatus: 2 }) } }).catch(() => { console.log("------失败1---------"); that.data.chatList.at(-1).loading = false that.setData({ aiStatus: 3, chatList: chatList }) }) } }, //填充用户的聊天数据 pushUserTemplate(text) { let { chatList } = this.data let userItem = { loading: false, chat_type: 1, con_type: 1, chat_content: text, md_content: '', } let aiItem = { chat_type: 2, chat_content: '', md_content: '', con_type: 1, loading: true, } chatList.push(userItem) chatList.push(aiItem) //要修改的数据 let setData = { chatList, scrollTop: 80000, inputText: '' } this.setData(setData, () => { this.setData({ scrollTop: 80000 }) }) }, //只填充ai的默认回复 pushAiTemplate(text, options = {}) { let { chatList } = this.data let mdResult = app.towxml(text, 'markdown', { base: "www.xxx.com", }); let aiItem = { chat_type: 2, chat_content: text, md_content: mdResult, con_type: 1, quick_btn: options.quick_btn || [], ...options } chatList.push(aiItem) //要修改的数据 let setData = { chatList, scrollTop: 80000, inputText: '' } this.setData(setData, () => { this.setData({ scrollTop: 80000 }) }) }, //定时器输出 timerOutput() { let { chatList } = this.data let that = this clearInterval(outTimer) outStrList = [] let lastChat = this.data.chatList.at(-1) //定时 outTimer = setInterval(async () => { if ((outStrList.length == 0) && that.data.aiStatus == 2) { clearInterval(outTimer) lastChat.end = true //如果有工具,设置工具对象 let text = lastChat.chat_content // 上传AI响应结果 if (text) { await request.post("/ai/history/upload", { chat_content: text, chat_uuid: chat_uuid, }) } //调用工具 if (toolText) { } that.setData({ chatList, aiStatus: 3 }) } else if (outStrList.length > 0) { let firstValue = outStrList.shift(); lastChat.chat_content += firstValue //转换md let mdResult = app.towxml(lastChat.chat_content, 'markdown', { base: "www.xxx.com", }); lastChat.md_content = mdResult lastChat.loading = false that.setData({ chatList: chatList, scrollTop: 80000 }) } }, 50) }, //流回调 onChunkReceived(data) { data.forEach((item) => { let value = item?.choices[0].delta.content ?? '' let call = item?.choices[0].delta.tool_calls //储存tool的id和名称 if (call) { if (call[0].id) { toolInfo.tool_call_id = call[0].id toolInfo.name = call[0].function.name } let tool = call[0].function.arguments ?? '' toolText += tool } if (value) { outStrList.push(value) } }) }, //停止 async stopMessage() { clearInterval(outTimer) let lastChat = this.data.chatList.at(-1) lastChat.chat_status = 2 this.setData({ aiStatus: 3, chatList: this.data.chatList }) await request.post("/ai/history/upload", { chat_content: lastChat.chat_content || 'nocontent', chat_status: 2, chat_uuid: chat_uuid }) }, //复制文字 copy(e) { let { content, chat_content } = e.currentTarget.dataset.text console.log(e); if (!content) { content = chat_content } copyText(content) }, })