写在前面:最近的ai是真的火啊,琢磨了一下,弄个uniappx的版本开发个东西玩一下,想了想不知道放啥内容,突然觉得deepseek可以接入,好家伙,一对接以后发现这是个付费的玩意,我穷,最后找了个免费的ai对接了-讯飞星火(这个免费指的是部分功能免费,并且有免费额度,总的来说自己玩一下够用了)
注意:我用的是Spark4.0 Ultra这个模型,以下代码都是这个模型对接使用的。
1、去讯飞自己注册一下,获取key(免费额度用一点少一点,测试还是要悠着点)
接口文档:(多看看文档,可以解决很多问题)
注意:用的WebSocket的方式连接, 对方给你返回的数据是一点一点给的,你需要处理一下如何再展示。
效果图:
以下是完整代码:
<template>
<view class="container">
<scroll-view class="message-list" scroll-y>
<view v-for="(msg, index) in messages" :key="index" class="message">
{{ msg.role === 'user' ? '我:' : 'AI:' }} {{ msg.content }}
</view>
</scroll-view>
<view class="input-area">
<input v-model="inputText" placeholder="请输入问题" @confirm="sendMessage" class="inputText"/>
<button @click="sendMessage" class="butSub">发送</button>
</view>
</view>
</template>
<script>
import CryptoJS from 'crypto-js'; // 需要安装crypto-js库
const apiKey = 'your_api_key'; // 替换为你的API Key
const apiSecret = 'your_api_secret'; // 替换为你的API Secret
const appid = 'your_appid'; // 替换为你的AppID
export default {
data() {
return {
inputText: '',
messages: [],
socketTask: null,
textCont:[]
};
},
methods: {
// 确保连接可用的方法
async ensureConnection() {
if (!this.socketTask || this.socketTask.readyState !== 1) {
await new Promise((resolve, reject) => {
this.connectWebSocket(); // 重新建立连接
// 设置连接超时
const timeout = setTimeout(() => {
reject(new Error('连接超时'));
}, 5000);
// 监听连接成功
this.socketTask.onOpen(() => {
clearTimeout(timeout);
resolve();
});
// 监听连接失败
this.socketTask.onError((err) => {
clearTimeout(timeout);
reject(err);
});
});
}
return true;
},
// 修改后的发送方法
async sendMessage() {
if (!this.inputText.trim()) return;
try {
// 添加加载状态
this.isSending = true;
// 确保连接有效
await this.ensureConnection();
// 添加用户消息到历史
this.messages.push({
role: 'user',
content: this.inputText
});
// 构造请求数据(需包含之前修正的domain参数)
const requestData = {
header: {
app_id: appid,
uid: 'user123'
},
parameter: {
chat: {
domain: '4.0Ultra',
temperature: 0.5,
max_tokens: 8192
}
},
payload: {
message: {
text: this.messages
}
}
};
// 发送消息
await new Promise((resolve, reject) => {
this.socketTask.send({
data: JSON.stringify(requestData),
success: resolve,
fail: reject
});
});
this.inputText = '';
} catch (err) {
console.error('发送失败:', err);
uni.showToast({
title: '消息发送失败,请重试',
icon: 'none'
});
// 自动重连机制
this.reconnect();
} finally {
this.isSending = false;
}
},
// 重连机制
reconnect() {
if (this.reconnecting) return;
this.reconnecting = true;
// 指数退避重试
let retries = 0;
const maxRetries = 3;
const attemptReconnect = () => {
this.connectWebSocket();
this.socketTask.onOpen(() => {
this.reconnecting = false;
uni.showToast({
title: '重新连接成功',
icon: 'none'
});
});
this.socketTask.onError(() => {
if (retries < maxRetries) {
retries++;
setTimeout(attemptReconnect, Math.pow(2, retries) * 1000);
} else {
this.reconnecting = false;
uni.showToast({
title: '连接服务器失败',
icon: 'none'
});
}
});
};
attemptReconnect();
},
// 生成请求签名
getWebsocketUrl() {
const date = new Date().toGMTString();
const host = 'spark-api.xf-yun.com';
const path = '/v4.0/chat';
// 生成签名
const tmp = `host: ${host}\ndate: ${date}\nGET ${path} HTTP/1.1`;
const signature = CryptoJS.enc.Base64.stringify(
CryptoJS.HmacSHA256(tmp, apiSecret)
);
// 构造授权参数
const authorization =
`api_key="${apiKey}", algorithm="hmac-sha256", headers="host date request-line", signature="${signature}"`;
// 生成WebSocket URL
return `wss://${host}${path}?` +
`authorization=${btoa(authorization).replace(/=+$/, '')}` +
`&date=${encodeURIComponent(date)}` +
`&host=${encodeURIComponent(host)}`;
},
// 建立WebSocket连接
connectWebSocket() {
const url = this.getWebsocketUrl();
this.socketTask = uni.connectSocket({
url: url,
success: () => {
console.log('WebSocket连接建立中...');
},
fail: (err) => {
console.error('连接失败:', err);
}
});
// 在onError回调中增加详细错误处理
this.socketTask.onError((err) => {
console.error('WebSocket错误详情:', {
errCode: err.errCode,
errMsg: err.errMsg,
errObj: JSON.stringify(err)
});
});
// 监听事件
this.socketTask.onOpen(() => {
console.log('WebSocket连接已打开');
});
this.socketTask.onMessage((res) => {
const data = JSON.parse(res.data);
console.log(data,'---*****-发送信息--',)
this.handleResponse(data);
});
this.socketTask.onError((err) => {
console.error('WebSocket错误:', err);
});
this.socketTask.onClose(() => {
console.log('WebSocket连接已关闭');
});
},
// 处理响应
handleResponse(data) {
if (data.header.code !== 0) {
// console.error(data,'请求错误:', data.header);
uni.showToast({
title: `错误代码: ${data.header.code}`,
icon: 'none'
});
console.error('完整错误信息:', data.header);
return;
}
// 拼接结果
const textArr=data.payload.choices.text;
if(textArr.length>0){
for(let i=0;i<textArr.length;i++){
this.textCont.push(textArr[i].content)
}
}
console.log(this.textCont,'===textCont=====')
const content =this.textCont
const index = this.messages.findIndex(msg => msg.role === 'assistant');
console.log(data,'====data====接受消息',content)
if (index !== -1) {
this.messages[index].content += content;
} else {
this.messages.push({
role: 'assistant',
content: content
});
}
// 滚动到底部
this.$nextTick(() => {
const query = uni.createSelectorQuery().in(this);
query.select('.message-list').boundingClientRect();
query.exec(res => {
if (res) {
uni.pageScrollTo({
scrollTop: res.height,
duration: 300
});
}
});
});
// 关闭连接
if (data.header.status === 2) {
this.socketTask.close();
}
},
},
mounted() {
this.connectWebSocket();
},
beforeDestroy() {
if (this.socketTask) {
this.socketTask.close();
}
}
};
</script>
<style>
.container {
padding: 20px;
}
.message-list {
height: calc(90vh - 105px);
margin-bottom: 20px;
border: 1px solid #eee;
padding: 10px;
}
.message {
margin: 10px 0;
padding: 8px;
background: #f5f5f5;
border-radius: 4px;
}
.input-area {
display: flex;
gap: 10px;
}
.inputText {
/* flex: 1; */
border: 1px solid #ccc;
padding: 0 8px;
border-radius: 4px;
margin-bottom: 15px;
height: 40px;
}
.butSub {
/* padding: 8px 16px; */
background: #007AFF;
color: white;
border: none;
height: 40px;
line-height: 40px;
border-radius: 4px;
}
</style>