uniapp开发微信小程序,集成cozeapi,减少踩坑,直接可用。vue2版本

19 阅读4分钟

uniapp搭建vue2微信小程序项目,集成coze,实战版!手把手教

1. uniapp创建vue2项目,建好基础页面。
2. 安装coze依赖,
- pnpm install @coze/uniapp-api
- pnpm install @coze/api
- pnpm install reconnecting-websocket
3.获取coze的token和botId;方式如下:
-     登录coze后台,找到项目管理,没有项目去左侧新建项目,有则省略;
-     botId获取方法:点击你新建项目,浏览器地址后面的数字,都是botID;
-[     ](https://code.coze.cn/playground)
ScreenShot_2026-01-29_152034_660.png
-     token获取方法:点击左侧菜单栏:API&SDK-授权-个人访问令牌-添加-确定;
image.png
-    看不懂的根据图片进行操作就行,推荐全选或者根据自己需求而定;
image.png
4. vue2代码如下,写的很草率,手下留情!
js代码如下
<script>
	import {
		WsChatClient,
		WsChatEventNames
	} from '../../node_modules/@coze/uniapp-api/dist/ws-tool'

	const chatClient = new WsChatClient({
		token: '您的tooken',
		botId: '您的botId',
		audioMutedDefault: true,
		playbackMutedDefault: false,
	});

	export default {
		data() {
			return {
				isAiReplying: false,
				isConnected: false,
				isRecording: false,
				isPlayingVoice: false,
				voicePlaybackTimer: null,
				btnLockTimer: null
			}
		},
		onLoad() {
			this.registerChatEvents();
			// 核心修改1:页面加载时主动建立连接,不开启录音
			this.initChatConnect();
		},
		onUnload() {
			chatClient.disconnect();
			clearTimeout(this.voicePlaybackTimer);
			clearTimeout(this.btnLockTimer);
		},
		methods: {
			// 新增:初始化建立WebSocket连接(仅连接,不录音)
			async initChatConnect() {
				try {
					if (!this.isConnected) {
						await chatClient.connect();
						console.log('页面初始化:WebSocket连接建立成功');
					}
				} catch (err) {
					console.error('页面初始化:WebSocket连接建立失败', err);
				}
			},

			registerChatEvents() {
				chatClient.on(WsChatEventNames.CONNECTED, () => {
					this.isConnected = true;
				});

				chatClient.on(WsChatEventNames.DISCONNECTED, () => {
					this.isConnected = false;
				});

				chatClient.on(WsChatEventNames.PLAYBACK_STARTED, () => {
					this.isPlayingVoice = true;
					this.isAiReplying = true;
				});

				chatClient.on(WsChatEventNames.PLAYBACK_COMPLETED, () => {
					this.isPlayingVoice = false;
				});

				chatClient.on(WsChatEventNames.ERROR, (err) => {
					console.error('交互错误', err);
					this.isRecording = false;
				});
			},

			async streamChat() {
				// 仅录音中禁止重复触发,语音播报时允许
				if (this.isRecording) return;

				try {
					// 1. 中断当前语音(替代方案:静音+重置状态)
					if (this.isPlayingVoice) {
						// 用“设置播放静音”替代stopPlayback(兼容当前SDK)
						if (chatClient.setPlaybackMuted) {
							await chatClient.setPlaybackMuted(true);
						}
						// 重置状态,强制停止动画和文字显示
						this.isPlayingVoice = false;
						this.isAiReplying = false;
					}

					// 核心修改2:移除“首次连接”判断(页面加载时已连接)
					// 无需再判断 !this.isConnected,直接开启录音即可

					// 3. 开启新录音(仅用户点击时触发,符合需求)
					await chatClient.startRecord();
					this.isRecording = true;
					console.log('麦克风开启,正在听...');

					// 4. 模拟5秒录音
					setTimeout(async () => {
						await chatClient.stopRecord();
						this.isRecording = false;
						// 开启新的AI回复状态
						this.isAiReplying = true;
						this.isPlayingVoice = true;
						// 恢复播放音量(避免新语音也静音)
						if (chatClient.setPlaybackMuted) {
							await chatClient.setPlaybackMuted(false);
						}
						console.log('停止录音,进入新的AI回复状态');
					}, 5000);

				} catch (err) {
					console.error('语音交互失败', err);
					this.isRecording = false;
				}
			},

			async stopAiReply() {
				console.log('用户手动点击停止');
				const that = this;

				uni.showModal({
					title: '提示',
					content: '确定终止当前对话?',
					async success(res) {
						if (res.confirm) {
							await chatClient.disconnect();
							that.isAiReplying = false;
							that.isPlayingVoice = false;
							that.isRecording = false;
							setTimeout(() => {
								uni.navigateBack();
							}, 1000);
						}
					}
				});
			}
		}
	};
</script>
HTML代码如下:
	<view class="chat-container">
		<!-- 渐变圆形 + 小圆点 -->
		<view class="voice-circle">
			<view class="voice-dots" :class="{ 
				recording: isRecording,
				replying: isAiReplying && isPlayingVoice
			}">
				<view class="dot" v-for="i in 10" :key="i"></view>
			</view>
		</view>

		<!-- 按钮容器 -->
		<view class="btn-group">
			<view class="anniu start-btn" @click="streamChat" :class="{ 
				disabled: isRecording, // 仅录音中禁用,语音播报时可点击
				recording: isRecording
			}">
				<image src="/static/imgs/tel1.png" mode="widthFix" style="width: 40rpx;height: auto;" v-if="isRecording"></image>
				<image src="/static/imgs/tel2.png" mode="widthFix" style="width: 40rpx;height: auto;" v-if="!isRecording"></image>
			</view>

			<view class="status-text">
				{{ isRecording ? '正在听' : (isAiReplying && isPlayingVoice ? '智能回复中' : '点击麦克风开始对话') }}
			</view>

			<view class="anniu stop-btn" @click="stopAiReply">
				<image src="/static/imgs/tel.png" mode="widthFix" style="width: 60rpx;height: auto;"></image>
			</view>
		</view>
	</view>
</template>
css代码如下:
	.chat-container {
		display: flex;
		flex-direction: column;
		align-items: center;
		justify-content: center;
		min-height: 100vh;
		background-color: #ffffff;
		padding: 0 30px;
		box-sizing: border-box;
	}

	.voice-circle {
		width: 200px;
		height: 200px;
		border-radius: 50%;
		background: linear-gradient(135deg, #409EFF 0%, #13C2C2 100%);
		display: flex;
		align-items: center;
		justify-content: center;
		margin-bottom: 200px;
	}

	.voice-dots {
		display: flex;
		gap: 4px;
		align-items: center;
		opacity: 1;
	}

	.voice-dots .dot {
		width: 4px;
		height: 4px;
		border-radius: 50%;
		background-color: rgba(255, 255, 255, 0.9);
		animation: none;
	}

	.voice-dots.recording .dot {
		animation: voice-shake 1s infinite ease-in-out !important;
	}

	.voice-dots.replying .dot {
		animation: voice-shake 1.2s infinite ease-in-out !important;
	}

	@keyframes voice-shake {
		0%, 100% { transform: scale(1); opacity: 0.8; }
		50% { transform: scale(2); opacity: 1; }
	}
	
	.voice-dots .dot:nth-child(1) { animation-delay: 0s; }
	.voice-dots .dot:nth-child(2) { animation-delay: 0.1s; }
	.voice-dots .dot:nth-child(3) { animation-delay: 0.2s; }
	.voice-dots .dot:nth-child(4) { animation-delay: 0.3s; }
	.voice-dots .dot:nth-child(5) { animation-delay: 0.4s; }
	.voice-dots .dot:nth-child(6) { animation-delay: 0.5s; }
	.voice-dots .dot:nth-child(7) { animation-delay: 0.6s; }
	.voice-dots .dot:nth-child(8) { animation-delay: 0.7s; }
	.voice-dots .dot:nth-child(9) { animation-delay: 0.8s; }
	.voice-dots .dot:nth-child(10) { animation-delay: 0.9s; }

	.status-text {
		font-size: 24rpx;
		color: #333333;
		font-weight: 500;
		transition: all 0.3s ease;
		text-align: center;
	}

	.btn-group {
		display: flex;
		justify-content: space-between;
		width: 630rpx;
		align-items: center;
		position: fixed;
		bottom: 100rpx;
		left: 30px;
	}

	.anniu {
		text-align: center;
		color: #ffffff;
		border-radius: 30px;
		height: 128rpx;
		width: 128rpx;
		line-height: 128rpx;
		font-size: 16px;
		transition: all 0.3s ease;
		display: flex;
		align-items: center;
		justify-content: center;
	}

	.start-btn {
		background: rgba(240, 240, 240, 1);
		color: #000;
	}
	.stop-btn {
		background: rgba(240, 240, 240, 1);
		color: #000;
	}
	.stop-btn.active {
		background: #FF4D4F;
		color: #fff;
	}
</style>  

样式随便写的,根据您自己的需求而定;

5.踩过的坑也是很多,比如根据sdk引入,引入不成功,报错;最后还是根据我代码中而写的;

4d80175f5d2a9c8b5e27530c066b30ae.png

6.因为vue2的兼容性没有vue3的好,从而作者又写了个vue3版本的,需要代码的私信吧!