用的是VUE3组和式开发使用的是科大讯飞的大模型聊天框我是使用了组件开发 需要更加详细的内容可以留言 需要如何拿科大讯飞的appid之内的可以评论到时候我出一篇文章
<template>
<view style="height: 100%">
<view style="padding-bottom: 84rpx">
<view class="cu-chat">
<view class="cu-item">
<view class="cu-avatar radius" style="background-image: url(https://image.meiye.art/Fha6tqRTIwHtlLW3xuZBJj8ZXSX3?imageMogr2/thumbnail/450x/interlace/1)"></view>
<view class="main">
<view class="content shadow">请输入你想咨询的问题(由AI大模型提供内容仅供参考)</view>
</view>
</view>
<!-- 动态聊天记录 -->
<view v-for="(item, index) in historyTextList" :key="index" :class="['cu-item', item.role === 'user' || item.role === 'user-typing' ? 'self' : '']">
<view v-if="item.role !== 'user' && item.role !== 'user-typing'" class="cu-avatar radius" style="background-image: url(https://image.meiye.art/Fha6tqRTIwHtlLW3xuZBJj8ZXSX3?imageMogr2/thumbnail/450x/interlace/1)"></view>
<view class="main">
<view :class="['content', item.role === 'user' || item.role === 'user-typing' ? 'bg-green' : 'shadow']">
<text>{{ item.content }}</text>
</view>
</view>
<view v-if="item.role === 'user' || item.role === 'user-typing'" class="cu-avatar radius" style="background-image: url(https://image.meiye.art/FlqKg5bugFQD5Qzm_QhGM7ET4Mtx?imageMogr2/thumbnail/450x/interlace/1)"></view>
<view class="date">{{ new Date().toLocaleTimeString() }}</view>
</view>
</view>
<view :class="'cu-bar foot input ' + (InputBottom != 0 ? 'cur' : '')" :style="'bottom:' + InputBottom + 'px'">
<view class="action">
<text class="cuIcon-sound text-grey"></text>
</view>
<input v-model="TEXT" class="solid-bottom" @focus="InputFocus" @blur="InputBlur" :adjust-position="false" :focus="false" maxlength="300" cursor-spacing="10" />
<view class="action">
<text class="cuIcon-emojifill text-grey"></text>
</view>
<button class="cu-btn bg-green shadow" @click="sendToSpark()">发送</button>
</view>
</view>
</view>
</template>
<script setup>
import { ref } from 'vue';
import * as base64 from 'base-64';
import CryptoJS from 'crypto-js';
import parser from 'fast-xml-parser';
import * as utf8 from 'utf8';
import URL from 'url';
const TEXT = ref('');
const httpUrl = ref("https://spark-api.xf-yun.com/v3.5/chat");
const modelDomain = ref(''); // V1.1-V3.5动态获取,高于以上版本手动指定
const APPID = ref(''); // 需要填写你自己的
const APISecret = ref('');// 需要填写你自己的
const APIKey = ref('=');// 需要填写你自己的
const sparkResult = ref('');
const historyTextList = ref([]); // 历史会话信息,由于最大token12000,可以结合实际使用,进行移出
const tempRes = ref(''); // 临时答复保存
const currentAssistantMessageIndex = ref(-1); // 当前助理消息的索引
const InputBottom = ref(0);
const sendToSpark = async () => {
let myUrl = await getWebSocketUrl();
tempRes.value = "";
const socketTask = uni.connectSocket({
url: myUrl,
method: 'GET',
success: res => {
console.log(res, "ws成功连接...", myUrl);
}
});
socketTask.onError((res) => {
console.log("连接发生错误,请检查appid是否填写", res);
});
socketTask.onOpen((res) => {
historyTextList.value.push({
"role": "user",
"content": TEXT.value
});
let params = {
"header": {
"app_id": APPID.value,
"uid": "aef9f963-7"
},
"parameter": {
"chat": {
"domain": modelDomain.value,
"temperature": 0.5,
"max_tokens": 1024
}
},
"payload": {
"message": {
"text": [
{ "role": "system", "content": "你现在扮演一位医生,你专业、严谨,能够准确回答医疗相关问题。接下来请用医生的口吻和用户对话。" },
...historyTextList.value.map(item => ({
role: item.role,
content: item.content
})),
{ "role": "user", "content": TEXT.value }
]
}
}
};
socketTask.send({
data: JSON.stringify(params),
success() {
console.log('第一帧发送成功');
}
});
// 清空输入框内容
TEXT.value = '';
});
socketTask.onMessage((res) => {
console.log('收到API返回的内容:', res.data);
let obj = JSON.parse(res.data);
let dataArray = obj.payload.choices.text;
for (let i = 0; i < dataArray.length; i++) {
tempRes.value += dataArray[i].content;
// 如果当前没有助理消息,则创建一个新的
if (currentAssistantMessageIndex.value === -1) {
currentAssistantMessageIndex.value = historyTextList.value.length;
historyTextList.value.push({
"role": "assistant",
"content": ""
});
}
// 更新当前助理消息的内容
historyTextList.value[currentAssistantMessageIndex.value].content += dataArray[i].content;
}
let temp = JSON.parse(res.data);
if (temp.header.code !== 0) {
console.log(`${temp.header.code}:${temp.message}`);
socketTask.close({
success(res) {
console.log('关闭成功', res);
},
fail(err) {
console.log('关闭失败', err);
}
});
}
if (temp.header.code === 0) {
if (res.data && temp.header.status === 2) {
// 消息接收完毕,重置状态
currentAssistantMessageIndex.value = -1;
setTimeout(() => {
socketTask.close({
success(res) {
console.log('关闭成功', res);
},
fail(err) {
console.log('关闭失败', err);
}
});
}, 1000);
}
}
});
};
const getWebSocketUrl = async () => {
const httpUrlHost = httpUrl.value.substring(8, 28);
const httpUrlPath = httpUrl.value.substring(28);
switch (httpUrlPath) {
case "/v1.1/chat":
modelDomain.value = "general";
break;
case "/v2.1/chat":
modelDomain.value = "generalv2";
break;
case "/v3.1/chat":
modelDomain.value = "generalv3";
break;
case "/v3.5/chat":
modelDomain.value = "generalv3.5";
break;
}
return new Promise((resolve, reject) => {
const url = `wss://${httpUrlHost}${httpUrlPath}`;
const host = "spark-api.xf-yun.com";
const apiKeyName = "api_key";
const date = new Date().toGMTString();
const algorithm = "hmac-sha256";
const headers = "host date request-line";
const signatureOrigin = `host: ${host}\ndate: ${date}\nGET ${httpUrlPath} HTTP/1.1`;
const signatureSha = CryptoJS.HmacSHA256(signatureOrigin, APISecret.value);
const signature = CryptoJS.enc.Base64.stringify(signatureSha);
const authorizationOrigin = `${apiKeyName}="${APIKey.value}", algorithm="${algorithm}", headers="${headers}", signature="${signature}"`;
const authorization = base64.encode(authorizationOrigin);
const finalUrl = `${url}?authorization=${authorization}&date=${encodeURI(date)}&host=${host}`;
resolve(finalUrl);
});
};
const InputFocus = (e) => {
InputBottom.value = e.detail.height;
};
const InputBlur = (e) => {
InputBottom.value = 0;
};
</script>