This commit is contained in:
zhu
2026-03-27 17:55:18 +08:00
parent ee03132cee
commit 382f6b9811
34 changed files with 1406 additions and 376 deletions

292
pages/chat/index.js Normal file
View File

@@ -0,0 +1,292 @@
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)
},
})