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 errorPromise.reject(error)})// respone拦截器service.interceptors.response.use((response) => {const res = responseif (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/* * @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/* * @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.openApiconst openAi = (data) =>service({url: baseUrl + '/v1/chat/completions',method: 'post',headers: {'content-type': 'application/json',Authorization:'Bearer YOU-KEY-63D8A64444655655C56a0838490e',},data,})export default { openAi }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 }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-bartitle="人工智障"right-text="清空"@click-right="onClickRight"></nav-bar></div><div class="main" ref="mainScroll"><divclass="list"v-for="(item, index) in params.messages":key="index":class="item.role + 'tr'"><div :class="item.role">{{ item.content }}</div><base-loadingv-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-buttonsize="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 = truethis.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 = nullinfo = 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 = falsethis.onBottomScrollClick()}}).catch((err) => {this.loading = falseNotify({ type: 'danger', message: err })})},/***@Descripttion:滚动条到底部*@Author: PengShuai*@Date: 2023-11-10 15:38:21*/onBottomScrollClick() {this.$nextTick(() => {let scroll = this.$refs.mainScrollscroll.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><!-- * @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><!-- * @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><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><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: 请求返回的数据格式为数组,一个字一个数组,需要自己进行数据整理和拼接。
© 版权声明
THE END
暂无评论内容