1、提示
- 由于国内注册 https://api.openai.com 比较麻烦,直接购买的第三方接口和key
- 淘宝购买,几块钱1个月
3、自己娱乐够用
2、前端框架
- Vant 移动端使用
- axios
3、创建拦截器,api/request.js
| |
| |
| |
| |
| |
| |
| |
| |
| import axios from 'axios' |
| |
| const service = axios.create({ |
| timeout: 300 * 1000, |
| }) |
| |
| |
| service.interceptors.request.use( |
| (config) => { |
| return config |
| }, |
| (error) => { |
| |
| Promise.reject(error) |
| } |
| ) |
| |
| |
| 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、完整代码
| |
| |
| |
| |
| |
| |
| |
| |
| <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: { |
| |
| |
| |
| |
| |
| 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 = '' |
| }, |
| |
| |
| |
| |
| |
| 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 }) |
| }) |
| }, |
| |
| |
| |
| |
| |
| 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: 请求返回的数据格式为数组,一个字一个数组,需要自己进行数据整理和拼接。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律