This commit is contained in:
田岩
2025-11-28 14:13:53 +08:00
parent fa3d24c9d9
commit c06671b9ea
7 changed files with 396 additions and 57 deletions
+3 -3
View File
@@ -5,7 +5,7 @@ ENV = 'development'
# VITE_APP_BASE_API_Url = 'https://aits.jlbank.com.cn:7001'
# VITE_APP_BASE_API_Url = 'https://aitstest.jlbank.com.cn:7001'
VITE_APP_BASE_API_Url = 'https://aitstest.jlbank.com.cn:7001'
# VITE_APP_BASE_API_Url = 'https://aitstest.jlbank.com.cn:7002'
@@ -16,14 +16,14 @@ ENV = 'development'
# dev
# VITE_APP_BASE_API_Url = 'http://25.18.122.65:7001'
# sit
VITE_APP_BASE_API_Url = 'http://25.18.122.91:9786'
#VITE_APP_BASE_API_Url = 'http://25.18.122.91:9786'
# UAT
# VITE_APP_BASE_API_Url = 'http://25.18.122.78:9786'
# DEV
# app入口
VITE_APP_BASE_API_Url = 'http://25.18.122.65:7001'
# VITE_APP_BASE_API_Url = 'http://25.18.122.65:7001'
# VITE_APP_BASE_API_Url = 'http://25.16.122.91:9786'
# VITE_APP_BASE_API_Url = 'http://25.18.122.66:9786'
+9
View File
@@ -411,6 +411,15 @@
}
}
},
{
"path": "pages/test/testChat",
"style": {
"navigationBarTitleText": "",
"app-plus": {
"titleNView": false
}
}
},
{
"path": "pages/course/index",
"style": {
+1 -54
View File
@@ -32,68 +32,15 @@
import showDialog from '@/common/dialogMessage'
const bbbb = () => {
// showDialog({
// title: '提示信息',
// content: '检测到您已离开,即将退出学习页面。',
// confirmText: '确认',
// showCancel: false,
// cancelText: '再想想',
// success: (res) => {
// if (res.confirm) {
// console.log('用户点击确定');
// }
// },
// cancel: () => {
// console.log('用户点击取消');
// }
// });
showDialog({
title: '提示',
content: '检测到您已存在学习记录,要继续学习吗?',
confirmText: '重新学习',
showCancel: true,
cancelText: '继续学习',
success: (res) => {
if (res.confirm) {
console.log('用户点击确定');
}
},
cancel: () => {
console.log('用户点击取消');
}
});
}
const aaaa = () => {
common.show('提示','检测到您已存在学习记录,要继续学习吗?','继续学习', '重新学习').then(res => {
})
// function show(title, msg, showCancel = true, cancelText = '取消', confirmText = '确定') {
// showDialog({
// title: '提示',
// content: '检测到您已存在学习记录,要继续学习吗?',
// confirmText: '重新学习',
// showCancel: true,
// cancelText: '继续学习',
// success: (res) => {
// if (res.confirm) {
// console.log('用户点击确定');
// }
// },
// cancel: () => {
// console.log('用户点击取消');
// }
// });
common.navigateTo('/pages/test/testChat')
}
</script>
<style lang="scss" scoped>
$bg-margin-left: 50rpx;
.detail-body {
margin: 30rpx $bg-margin-left 0 $bg-margin-left;
background: #ffffff;
+223
View File
@@ -0,0 +1,223 @@
<template>
<view class="ai-chat-container">
<scroll-view
class="chat-list"
scroll-y
scroll-bottom="{{scrollBottom}}"
:style="{ height: `calc(100vh - ${inputAreaHeight}px)` }"
>
<view class="chat-item ai">
<view class="chat-avatar ai-avatar">
<text class="iconfont icon-ai"></text>
</view>
<view class="chat-content">
<view class="content-text">{{ asrResult }}</view>
</view>
</view>
</scroll-view>
<view class="input-area" :style="{ height: inputAreaHeight + 'px' }">
<view class="send-btn-area">
<button class="hold-send-btn" @touchstart="startRecord" @touchend="stopRecord" @touchcancel="handleTouchEnd">
<text class="btn-text">{{ isHolding ? '松开发送' : '按住 说话/发送' }}</text>
</button>
</view>
</view>
</view>
</template>
<script>
import Recorder from 'recorder-core';
export default {
data() {
return {
scrollBottom: 0,
inputAreaHeight: 80,
isHolding: false,
asrResult: '',
recorderManager: null, // 原生录音管理器
ws: null, // WebSocket连接
isRecording: false,
CHUNK_SIZE: 6400, // 200ms/块
recordFilePath: '', // 录音文件路径
fileReaderTimer: null, // 分片读取定时器
recorder: null, // 录音实例
audioStream: [], // 实时音频流缓存
sampleRate: 16000, // 采样率(常用 16000/44100
bitRate: 16, // 位深
channels: 1 // 声道(单声道)
};
},
onLoad() {
this.initRecorder()
},
onUnload() {
// 页面卸载时销毁实例
this.destroyRecorder();
},
methods: {
// 初始化录音器
initRecorder() {
console.log('plus.audio', plus.audio);
// 确保 plus.audio 存在
if (!plus.audio) {
console.error('5+ API 音频模块未加载');
uni.showToast({ title: '录音功能初始化失败', icon: 'none' });
return;
}
// 原初始化逻辑
const options = {
filename: '_doc/audio/temp_recorder.amr',
format: 'amr',
sampleRate: 16000,
bitRate: 16,
channels: 1,
bufferSize: 4096,
onRecorded: (buffer) => {
this.handleRealTimeStream(buffer);
}
};
try {
this.recorder = plus.audio.getRecorder(options);
console.log('录音器初始化成功');
} catch (e) {
console.error('创建录音实例失败:', e);
uni.showToast({ title: '录音功能初始化失败', icon: 'none' });
}
},
// 开始录音
startRecord() {
console.log('下·', this.isRecording, !this.recorder);
if (this.isRecording || !this.recorder) return;
this.isRecording = true;
this.audioStream = []; // 清空历史流
this.recorder.record(
() => {
console.log('录音开始成功');
},
(err) => {
console.error('录音启动失败:', err);
this.isRecording = false;
}
);
},
// 停止录音
stopRecord() {
if (!this.isRecording || !this.recorder) return;
this.isRecording = false;
this.recorder.stop(
(filePath) => {
console.log('录音停止,文件路径:', filePath);
// 最终完整文件(可选)
this.handleFinalFile(filePath);
},
(err) => {
console.error('录音停止失败:', err);
}
);
}
}
};
</script>
<style scoped>
.ai-chat-container {
width: 100%;
height: 100vh;
background-color: #f5f7fa;
box-sizing: border-box;
padding-bottom: env(safe-area-inset-bottom);
}
.chat-list {
width: 100%;
padding: 20rpx 16rpx;
box-sizing: border-box;
}
.chat-item {
display: flex;
margin-bottom: 30rpx;
max-width: 85%;
}
.ai-avatar {
width: 80rpx;
height: 80rpx;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
margin-right: 20rpx;
background-color: #9f7aea;
}
.icon-ai {
font-size: 40rpx;
color: #fff;
}
.chat-content .content-text {
padding: 24rpx 28rpx;
border-radius: 20rpx 20rpx 20rpx 8rpx;
line-height: 1.6;
font-size: 32rpx;
color: #333;
word-wrap: break-word;
word-break: break-all;
background-color: #fff;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.08);
}
.input-area {
width: 100%;
background-color: #fff;
padding: 16rpx 20rpx;
box-sizing: border-box;
border-top: 1rpx solid #eee;
}
.send-btn-area {
width: 100%;
display: flex;
justify-content: center;
}
.hold-send-btn {
width: 80%;
height: 90rpx;
line-height: 90rpx;
border-radius: 45rpx;
background-color: #4299e1;
color: #fff;
font-size: 34rpx;
font-weight: 500;
box-shadow: 0 4rpx 12rpx rgba(66, 153, 225, 0.3);
transition: all 0.2s ease;
padding: 0;
margin: 0;
}
.hold-send-btn:active {
background-color: #3182ce;
transform: scale(0.98);
}
.iconfont {
font-family: 'iconfont' !important;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.icon-ai::before {
content: '\e602';
}
</style>
+147
View File
@@ -0,0 +1,147 @@
<template>
<view>
<video :is-live="true" id="myVideo" :src="videoSrc" :autoplay="true"></video>
<live-pusher id="livePusher" ref="livePusher" class="livePusher" url="rtmp://25.18.122.148:9605/voice_in/aa" mode="SD" :muted="true" :enable-camera="false" :auto-focus="true" :beauty="1" whiteness="2" aspect="9:16" @statechange="statechange" @netstatus="netstatus" @error="error"></live-pusher>
<view class="btn" @click="prepare"><text>预热</text></view>
<view class="btn" @click="start"><text>开始推流</text></view>
<view class="btn" @click="pause"><text>暂停推流</text></view>
<view class="btn" @click="resume"><text>恢复推流</text></view>
<view class="btn" @click="stop"><text>停止推流</text></view>
<view class="btn" @click="snapshot"><text>快照</text></view>
<view class="btn" @click="startPreview"><text>开启摄像头预览</text></view>
<view class="btn" @click="stopPreview"><text>关闭摄像头预览</text></view>
<view class="btn" @click="switchCamera"><text>切换摄像头</text></view>
</view>
</template>
<script>
import { prepareApi } from '@/api/talk.js'
var pusher
export default {
data() {
return {
context: null,
baseName: 'aaa'
}
},
computed: {
videoSrc() {
return 'rtmp://rtmp.home.tymas.cn:1935/voice_in/' + this.baseName
}
},
onReady() {
const videoContext = uni.createVideoContext('myVideo')
videoContext.setVolume(1.0)
// 注意:需要在onReady中 或 onLoad 延时
// pusher = uni.createLivePusherContext('livePusher', this)
// console.log(pusher)
},
methods: {
statechange(e) {
console.log('statechange:', e, JSON.stringify(e))
},
netstatus(e) {
console.log('netstatus:' + JSON.stringify(e))
},
error(e) {
console.log('error:' + JSON.stringify(e))
},
start: function () {
console.log('statechange:', pusher.start)
pusher.start({
success: a => {
console.log('livePusher.start:' + JSON.stringify(a))
}
})
},
prepare: function () {
this.initPusher()
// prepareApi(this.baseName)
// .then(res => {
// console.log(res)
// })
// .catch(err => {
// console.log(err)
// if (err === 'prepare') {
// this.initPusher()
// }
// })
},
close: function () {
pusher.close({
success: a => {
console.log('livePusher.close:' + JSON.stringify(a))
}
})
},
snapshot: function () {
pusher.snapshot({
success: e => {
console.log(JSON.stringify(e))
}
})
},
resume: function () {
pusher.resume({
success: a => {
console.log('livePusher.resume:' + JSON.stringify(a))
}
})
},
pause: function () {
pusher.pause({
success: a => {
console.log('livePusher.pause:' + JSON.stringify(a))
}
})
},
stop: function () {
pusher.stop({
success: a => {
console.log(JSON.stringify(a))
}
})
},
switchCamera: function () {
pusher.switchCamera({
success: a => {
console.log('livePusher.switchCamera:' + JSON.stringify(a))
}
})
},
startPreview: function () {
pusher.startPreview({
success: a => {
console.log('livePusher.startPreview:' + JSON.stringify(a))
}
})
},
stopPreview: function () {
pusher.stopPreview({
success: a => {
console.log('livePusher.stopPreview:' + JSON.stringify(a))
}
})
},
initPusher() {
pusher = new plus.video.LivePusher('pusher-box', {
url: 'rtmp://rtmp.home.tymas.cn:1935/voice_in/' + this.baseName,
'enable-camera': true
})
pusher.addEventListener('statechange', this.statechange)
pusher.addEventListener('netstatus', this.netstatus)
pusher.addEventListener('error', this.error)
console.log(pusher)
}
}
}
</script>
<style>
.btn {
width: 100%;
height: 80rpx;
background-color: #ccc;
margin-bottom: 5rpx;
}
</style>
+8
View File
@@ -346,6 +346,14 @@
"titleNView": false
}
}
},{
"path": "pages/test/testChat",
"style": {
"navigationBarTitleText": "",
"app-plus": {
"titleNView": false
}
}
}
]
}
+5
View File
@@ -10,5 +10,10 @@ export default defineConfig({
port: 5174,
strictPort: true, // 可选:端口被占用时直接报错(避免自动切换端口)
open: false // 可选:启动后自动打开浏览器(按需开启)
},
base: './', // 关键:设置基础路径为相对路径
build: {
outDir: 'dist',
assetsDir: 'assets' // 和打包后的目录一致
}
})