uni-app官网
一、创建uni-app
我用的是vue-cli命令行创建uniapp项目。
踩坑1:执行命令报错了
npm ERR! Darwin 20.6.0
npm ERR! argv "/Users/zhuzhu/.nvm/versions/node/v6.2.0/bin/node" "/Users/zhuzhu/.nvm/versions/node/v6.2.0/bin/npm" "install"
npm ERR! node v6.2.0
npm ERR! npm v3.8.9
npm ERR! This request requires auth credentials. Run `npm login` and repeat the request.
npm ERR!
npm ERR! If you need help, you may report this error at:
npm ERR!
npm ERR! Please include the following file with any support request:
npm ERR! /Users/zhuzhu/Downloads/uni-preset-vue-vite/npm-debug.log
解决:直接访问官网的gitee,下载模板,然后npm install,之后在npm run XX运行你想要的程序就好啦。
二、开发聊天功能
实现思路
之前开发的是网页版的,现在要改成小程序,接口是算法已经写好的,直接拿来了。前端这块实现最重要的是success回调里的代码,接口返回的是流式(如图一),然后前端通过截取最后一次对话内容,通过startTyping方法实现打字机效果

上代码(样式和方法可直接copy用)
悬浮 {{ item.content }} {{item.ai_content}} 发送
.main-dislogue { height: calc(100vh - 70px); background: #f5f5f5; display: flex; flex-direction: column; } /* 头部悬浮 */ .header-suspension { width: 100rpx; height: 300rpx; /* pointer-events: none; */ z-index: 100; position: fixed; right: 10rpx; bottom: 300rpx; } .head-image { width: 74rpx; height: 74rpx; z-index: 99; background: #d4d4d4; border-radius: 50%; padding: 6rpx; box-shadow: 0px 2rpx 20rpx rgba(0, 0, 0, 0.5); } .record-btn { width: 74rpx; height: 74rpx; background: #FFFFFF; border-radius: 50%; font-size: 26rpx; text-align: center; padding: 6rpx; box-shadow: 0px 2rpx 20rpx rgba(0, 0, 0, 0.5); color: #4A90E2; margin-top: 29rpx; } /* 内容 */ .content { padding: 12rpx; padding-bottom: 100px; background: #f5f5f5; } /* #scrollpage { } */ /* 问 */ .ask { display: flex; justify-content: flex-end; width: 100%; margin-top: 6rpx; } .ask-avatar { width: 120rpx; margin-top: 20rpx; } .ask-sex { width: 100rpx; height: 100rpx; } .ask-bulge { position: relative; top: 41rpx; right: 23rpx; display: block; width: 0; height: 0; border: 15rpx solid #38a579; transform: rotate(45deg); } .ask-text { z-index: 1; } .ask-desc { background: #38a579; border-radius: 13rpx; padding: 15rpx; line-height: 58rpx; margin-top: 27rpx; white-space: pre-line; word-break: break-all; color: #fff; margin-left: 124rpx; } /* 答 */ .answer { display: flex; justify-content: flex-start; margin-top: 6rpx; } .answer-avatar { width: 120rpx; margin-top: 20rpx; } .answer-ai { width: 100rpx; height: 100rpx; } .answer-bulge { position: relative; top: 41rpx; left: 23rpx; display: block; width: 0; height: 0; border: 15rpx solid #ffffff; transform: rotate(45deg); } .answer-text { z-index: 1; } .answer-desc { margin-right: 88rpx; border-radius: 13rpx; line-height: 58rpx; background: #fff; margin-top: 27rpx; tab-size: 12rpx; padding: 15rpx; white-space: pre-wrap; box-shadow: 0rpx 5rpx 47rpx 0rpx #97979773; } /* 尾部 */ .bottom { border-top: 2rpx solid #CCCCCC; background: #f5f5f5; display: flex; padding: 10rpx; padding-bottom: 50rpx; position: fixed; bottom: 0; z-index: 99; width: 100%; } .bottom-input { flex: 1; font-size: 35rpx; border-radius: 10rpx; background: #FFFFFF; padding: 17rpx; } .bottom-button { width: 190rpx; height: 80rpx; font-size: 14px; line-height: 80rpx; margin-left: 20rpx; background: #4A90E2 !important; }
import Api from "@/utils/api.js"; import base from '@/utils/base.js'; const BASE_URL = base.baseUrl; const recorderManager = uni.getRecorderManager() export default { data() { return { sex: "", birthDate: "", generateRecordsFlag: false, dest: [], dialogue_code: "", value: "", isSend: false, scrollTop: 0, currentText: "", isSpeaking: false } }, onLoad(option) { this.sex = option.sex; this.birthDate = option.birthDate; this.dialogue_code = option.dialogue_code; }, onReady() { let _this = this; uni.getStorage({ key: 'gpt_h5_dialogue', success: function (res) { let list = res.data || ""; if (list.length) { this.dest = JSON.parse(list); if (this.dest.length >= 2) { this.generateRecordsFlag = true; } } else { setTimeout(() => { _this.handleSend(); }, 500) } } }); }, methods: { // 年龄转换 ageCalculation(date) { var today = new Date(); // 获取出生日期 var birthDate = new Date(date); // 假设出生日期为1990年1月1日 // 计算年龄 var age = today.getFullYear() - birthDate.getFullYear(); var m = today.getMonth(), d = today.getDate(); if (m { let str = JSON.stringify(res.data); // 将字符串按"data: ["分割,然后取最后一个部分 const lastDataSection = str.split("data: [").pop(); // 截取最后一个JSON对象的部分 const lastJsonString = lastDataSection.split("]")[0].replace(/\\/g, ''); // 解析JSON字符串 const lastJsonObject = JSON.parse(lastJsonString); // 获取ai_content的值 const lastAiContent = lastJsonObject.ai_content; console.log(lastAiContent, 'lastAiContent'); ai_content = lastAiContent; if (lastAiContent == "") { // 返回空,则默认提示 ai_content = "目前公司GPU服务器有限,会因为调试需要临时中断出现服务不可用,请稍后重试。"; } _this.dest[_this.dest.length - 1].ai_content = ""; if (!startFlag) { startFlag = true startTyping(); } } }); function startTyping() { let currentIndex = 0; const typingSpeed = 100; // 打字速度,单位:毫秒 const timer = setInterval(() => { _this.dest[_this.dest.length - 1].ai_content += ai_content[currentIndex]; currentIndex++; _this.scrollToBottom(); if (currentIndex >= ai_content.length) { clearInterval(timer); _this.isSend = false; } }, typingSpeed); uni.setStorage({ key: 'gpt_h5_dialogue', data: JSON.stringify(_this.dest), success: function () { } }); } }, // 滚动至聊天底部 scrollToBottom() { this.$nextTick(() => { const query = uni.createSelectorQuery(); query.select('#scrollpage').boundingClientRect(); query.exec(res => { this.scrollTop = res[0].height; uni.pageScrollTo({ scrollTop: res[0].height + 170, // 将滚动位置设置为顶部 duration: 300 // 滚动到顶部的动画时长,单位为毫秒 }); }) }) } }, }
效果图
