Files
kairos_miniapp/pages/chat/index.js
2026-03-27 18:00:04 +08:00

295 lines
6.5 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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,
}, () => {
that.setData({
scrollTop: 80000,
})
})
} 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)
},
})