- A+
所属分类:Web前端
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('nn') 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: 请求返回的数据格式为数组,一个字一个数组,需要自己进行数据整理和拼接。