1、提示 由於國內註冊 https://api.openai.com 比較麻煩,直接購買的第三方介面和key 淘寶購買,幾塊錢1個月 3、自己娛樂夠用 2、前端框架 Vant 移動端使用 axios 3、創建攔截器,api/request.js /* * @Descripttion: 文件說明 * ...
1、提示
- 由於國內註冊 https://api.openai.com 比較麻煩,直接購買的第三方介面和key
- 淘寶購買,幾塊錢1個月
3、自己娛樂夠用
2、前端框架
- Vant 移動端使用
- axios
3、創建攔截器,api/request.js
/*
* @Descripttion: 文件說明
* @version: 0.0.1
* @Author: pengshuai
* @Date: 2023-11-01 10:39:22
* @LastEditors: PengShuai
* @LastEditTime: 2023-11-02 10:33:28
*/
import axios from 'axios'
// 創建axios實例
const service = axios.create({
timeout: 300 * 1000, // ms請求超時時間
})
// request攔截器
service.interceptors.request.use(
(config) => {
return config
},
(error) => {
// Do something with request error
Promise.reject(error)
}
)
// respone攔截器
service.interceptors.response.use(
(response) => {
const res = response
if (res.status !== 200) {
return Promise.reject(response)
} else {
if (res.status === 200) {
return res.data
} else {
return Promise.reject(res.data.message)
}
}
},
(error) => {
return Promise.reject(error)
}
)
export default service
4、創建介面 api/index.js
import service from './request'
// 訪問介面地址
const baseUrl = window.configUrl.openApi
const openAi = (data) =>
service({
url: baseUrl + '/v1/chat/completions',
method: 'post',
headers: {
'content-type': 'application/json',
Authorization:
'Bearer YOU-KEY-63D8A64444655655C56a0838490e',
},
data,
})
export default { openAi }
5、完整代碼
<!--
* @Descripttion: 人工智障
* @version: 0.0.1
* @Author: PengShuai
* @Date: 2023-11-10 10:22:33
* @LastEditors: PengShuai
* @LastEditTime: 2023-11-10 16:02:25
-->
<template>
<div class="page">
<div class="header">
<nav-bar
title="人工智障"
right-text="清空"
@click-right="onClickRight"
></nav-bar>
</div>
<div class="main" ref="mainScroll">
<div
class="list"
v-for="(item, index) in params.messages"
:key="index"
:class="item.role + 'tr'"
>
<div :class="item.role">{{ item.content }}</div>
<base-loading
v-if="
item.role === 'user' &&
index === params.messages.length - 1 &&
loading
"
></base-loading>
</div>
</div>
<div class="footer">
<van-field placeholder="請輸入..." v-model="messages.content">
<template #button>
<van-button
size="small"
icon="guide-o"
type="primary"
@click="onSend"
@keyup.enter="onSend"
></van-button>
</template>
</van-field>
</div>
<div class="popup" v-if="loading"></div>
</div>
</template>
<script>
import api from '@/api'
import { NavBar, Field, Button, Notify } from 'vant'
import baseLoading from '@/components/baseLoading'
export default {
name: 'HomePage',
components: {
baseLoading,
NavBar,
[Notify.name]: Notify,
[Field.name]: Field,
[Button.name]: Button,
},
data() {
return {
// 參數
params: {
messages: [
{
role: 'system',
content: '你好,我是彭帥的人工智障,有什麼可以幫您?',
},
],
stream: true,
model: 'gpt-3.5-turbo',
temperature: 0.5,
presence_penalty: 0,
frequency_penalty: 0,
top_p: 1,
},
// 消息列表
infoList: [],
// 消息
messages: {
role: 'user',
content: '',
},
loading: false,
}
},
methods: {
/**
*@Descripttion:點擊右側清空
*@Author: PengShuai
*@Date: 2023-11-10 13:13:51
*/
onClickRight() {
this.params = {
messages: [
{
role: 'system',
content: '你好,我是彭帥的人工智障,有什麼可以幫您?',
},
],
stream: true,
model: 'gpt-3.5-turbo',
temperature: 0.5,
presence_penalty: 0,
frequency_penalty: 0,
top_p: 1,
}
this.messages.content = ''
},
/**
*@Descripttion:點擊發送
*@Author: PengShuai
*@Date: 2023-11-10 13:34:06
*/
onSend() {
this.loading = true
this.params.messages.push(JSON.parse(JSON.stringify(this.messages)))
let obj = {
role: '',
content: '',
}
this.messages.content = ''
this.onBottomScrollClick()
api
.openAi(this.params)
.then((res) => {
if (res) {
let info = null
info = res.split('\n\n')
info = info.map((obj) => obj.substring(5))
info.splice(-2)
info = info.map((obj) => JSON.parse(obj))
if (info.length > 0) {
info.forEach((item) => {
if (item.choices.length > 0) {
item.choices.forEach((o) => {
if (o.delta.role) {
obj.role = o.delta.role
} else if (o.delta.content) {
obj.content += o.delta.content
}
})
}
})
this.infoList.push(obj)
}
this.params.messages.push(this.infoList[this.infoList.length - 1])
this.loading = false
this.onBottomScrollClick()
}
})
.catch((err) => {
this.loading = false
Notify({ type: 'danger', message: err })
})
},
/**
*@Descripttion:滾動條到底部
*@Author: PengShuai
*@Date: 2023-11-10 15:38:21
*/
onBottomScrollClick() {
this.$nextTick(() => {
let scroll = this.$refs.mainScroll
scroll.scrollTo({ top: scroll.scrollHeight, behavior: 'smooth' })
})
},
},
}
</script>
<style lang="less" scoped>
.page {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
.header {
border-bottom: 1px solid #ddd;
}
.footer {
border: 1px solid #ddd;
}
.main {
flex: 1;
margin: 10px 0;
border: 1px solid #ddd;
overflow: auto;
}
.usertr {
text-align: right;
}
.list {
.system,
.assistant {
margin: 10px 10px 10px 35px;
text-align: left;
position: relative;
padding: 5px 0;
color: #878787;
border-bottom: 1px solid #ddd;
display: inline-block;
&::after {
content: '智';
position: absolute;
left: -25px;
top: 5px;
background: #07c160;
border-radius: 50%;
width: 22px;
height: 22px;
line-height: 22px;
text-align: center;
color: #fff;
font-size: 12px;
}
}
.user {
margin: 10px 40px 10px 10px;
text-align: right;
position: relative;
padding: 5px 0;
color: #505050;
border-bottom: 1px solid #ddd;
display: inline-block;
&::after {
content: '帥';
position: absolute;
right: -25px;
top: 5px;
background: #07c160;
border-radius: 50%;
width: 22px;
height: 22px;
line-height: 22px;
text-align: center;
color: #fff;
font-size: 12px;
}
}
}
.popup {
width: 100%;
height: 100%;
position: fixed;
top: 0;
left: 0;
z-index: 99;
display: flex;
justify-content: center;
align-items: center;
}
}
</style>
6、提示 baseLoading 請求loading 組件 可刪除自己寫
<template>
<div class="baseLoading">
<div class="loading">
<span style="--time: 1">智</span>
<span style="--time: 2">障</span>
<span style="--time: 3">正</span>
<span style="--time: 4">在</span>
<span style="--time: 5">思</span>
<span style="--time: 6">考</span>
<span style="--time: 7">中</span>
<span style="--time: 8">.</span>
<span style="--time: 9">.</span>
<span style="--time: 10">.</span>
</div>
</div>
</template>
<script>
export default {
name: 'baseLoading',
}
</script>
<style lang="less" scoped>
.loading {
text-align: center;
}
.loading span {
display: inline-block;
font-size: 12px;
font-weight: bold;
font-family: 'Courier New', Courier, monospace;
animation: loading 1s ease-in-out infinite;
animation-delay: calc(0.1s * var(--time));
color: #919191;
}
@keyframes loading {
0% {
transform: translateY(0px);
}
25% {
transform: translateY(-20px);
}
50%,
100% {
transform: translateY(0px);
}
}
</style>
7、示例
8、註意
Tips: 請求返回的數據格式為數組,一個字一個數組,需要自己進行數據整理和拼接。