2025-04-16 02:11:26 +01:00

262 lines
6.7 KiB
Vue
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.

<template>
<view class="content" ref="scrollView">
<view class="chat">
<view class="conversation" v-for="item in sparkResult">
<view class="question">
<view class="text">{{item.question}}</view>
<u-avatar class="avatar" bg-color="#2b7" size="25" icon="account-fill" fontSize="20"></u-avatar>
</view>
<view class="answer">
<view class="avatar">
<image class="img" src="../../static/logo.png" mode=""></image>
</view>
<view class="text">{{item.answer}}</view>
</view>
</view>
</view>
<view class="btn">
<u-input v-model="TEXT" placeholder="问我任何问题...">
<template slot="suffix">
<u-button @click="sendToSpark()" type="success" size="mini" icon="checkmark"
shape="circle"></u-button>
</template>
</u-input>
</view>
</view>
</template>
<script>
import CryptoJS from 'crypto-js';
import setting from '@/setting.js';
const base64 = require('js-base64').Base64
export default {
data() {
return {
TEXT: '',
// V1.1-V3.5动态获取,高于以上版本手动指定
modelDomain:'',
wsLiveFlag: false,
sparkResult: [],
historyTextList: [], // 历史会话信息由于最大token12000,可以结合实际使用,进行移出
tempRes: '' // 临时答复保存
}
},
methods: {
async sendToSpark() {
if(this.TEXT==""){
uni.showToast({
title:"输入框不能为空",
icon:"none"
})
return
}
let myUrl = await this.getWebSocketUrl();
this.tempRes = "";
this.socketTask = uni.connectSocket({
url: myUrl,
method: 'GET',
success: res => {
console.log(res, "ws成功连接...", myUrl)
this.wsLiveFlag = true;
}
})
this.socketTask.onError((res) => {
console.log("连接发生错误请检查appid是否填写", res)
})
this.socketTask.onOpen((res) => {
this.historyTextList.push({
"role": "user",
"content": this.TEXT
})
// 第一帧..........................................
let params = {
"header": {
"app_id": setting.APPID,
"uid": "aef9f963-7"
},
"parameter": {
"chat": {
"domain": this.modelDomain,
"temperature": 0.5,
"max_tokens": 1024
}
},
"payload": {
"message": {
"text": this.historyTextList
}
}
};
this.sparkResult.push({
question:this.TEXT,
answer:"思考中..."
})
this.socketTask.send({ // 发送消息都用uni的官方版本
data: JSON.stringify(params),
success:()=>{
this.TEXT = ""
console.log('第一帧发送成功')
}
});
});
// 接受到消息时
this.socketTask.onMessage((res) => {
let obj = JSON.parse(res.data)
let dataArray = obj.payload.choices.text;
if(this.sparkResult[this.sparkResult.length-1].answer=='思考中...'){
this.sparkResult[this.sparkResult.length-1].answer=""
}
for (let i = 0; i < dataArray.length; i++) {
this.sparkResult[this.sparkResult.length-1].answer += dataArray[i].content//动态拼接并显示回复的消息
this.tempRes = this.tempRes + dataArray[i].content
}
uni.pageScrollTo({
scrollTop: 999999999,
duration: 300
});
let temp = JSON.parse(res.data)
if (temp.header.code !== 0) {
this.socketTask.close({
success(res) {
console.log('关闭成功', res)
this.wsLiveFlag = false;
},
fail(err) {
console.log('关闭失败', err)
}
})
} else {
if (res.data && temp.header.status === 2) {
this.historyTextList.push({
"role": "assistant",
"content": this.tempRes
})
setTimeout(() => {
this.socketTask.close({
success(res) {
console.log('关闭成功', res)
},
fail(err) {
console.log('关闭失败', err)
}
})
}, 1000)
}
}
})
},
// 鉴权
getWebSocketUrl() {
var httpUrlHost = (setting.httpUrl).substring(8, 28);
var httpUrlPath = (setting.httpUrl).substring(28);
switch (httpUrlPath) {
case "/v1.1/chat":
this.modelDomain = "general";
break;
case "/v2.1/chat":
this.modelDomain = "generalv2";
break;
case "/v3.1/chat":
this.modelDomain = "generalv3";
break;
case "/v3.5/chat":
this.modelDomain = "generalv3.5";
break;
}
return new Promise((resolve, reject) => {
// https://spark-api.xf-yun.com/v1.1/chat V1.5 domain general
// https://spark-api.xf-yun.com/v2.1/chat V2.0 domain generalv2
var url = "wss://"+httpUrlHost+httpUrlPath;
var host = "spark-api.xf-yun.com";
var apiKeyName = "api_key";
var date = new Date().toGMTString();
var algorithm = "hmac-sha256";
var headers = "host date request-line";
var signatureOrigin = `host: ${host}\ndate: ${date}\nGET ${httpUrlPath} HTTP/1.1`;
var signatureSha = CryptoJS.HmacSHA256(signatureOrigin, setting.APISecret);
var signature = CryptoJS.enc.Base64.stringify(signatureSha);
var authorizationOrigin =
`${apiKeyName}="${setting.APIKey}", algorithm="${algorithm}", headers="${headers}", signature="${signature}"`;
var authorization = base64.encode(authorizationOrigin);
url = `${url}?authorization=${authorization}&date=${encodeURI(date)}&host=${host}`;
resolve(url); // 主要是返回地址
});
},
}
}
</script>
<style lang="scss" scoped>
// page{
// background: #fff;
// }
.content{
padding: 20rpx 20rpx 100rpx 20rpx;
.chat{
.conversation{
.question{
display: flex;
align-items: center;
justify-content: flex-end;
margin-bottom: 20rpx;
.avatar{
align-self: normal;
}
.text{
max-width: 80%;
margin-right: 10rpx;
background: #fff;
padding: 20rpx;
box-sizing: border-box;
border-radius: 20rpx;
}
}
.answer{
display: flex;
align-items: center;
margin-bottom: 20rpx;
.avatar{
width: 50rpx;
height: 50rpx;
display: flex;
align-items: center;
justify-content: center;
background: #fff;
border: 1px solid #eee;
border-radius: 25rpx;
margin-right: 10rpx;
align-self: normal ;
.img{
width: 40rpx;
height: 40rpx;
}
}
}
.text{
max-width: 80%;
word-break: break-word;
background: #fff;
padding: 20rpx;
box-sizing: border-box;
border-radius: 20rpx;
}
}
}
.btn{
position: fixed;
border-radius: 20rpx;
width: 100%;
bottom: var(--window-bottom);
left: 0;
background: #fff;
}
}
</style>