Merge branch 'develop' of http://25.13.9.101:9000/K17_AITS/tra-app into develop

This commit is contained in:
杨航
2025-11-28 16:54:49 +08:00
13 changed files with 463 additions and 98 deletions
+3 -1
View File
@@ -16,7 +16,9 @@ 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'
# UAT
# VITE_APP_BASE_API_Url = 'http://25.18.122.78:9786'
@@ -28,7 +30,7 @@ VITE_APP_BASE_API_Url = 'http://25.18.122.91:9786'
# VITE_APP_BASE_API_Url = 'http://25.18.122.66:9786'
# h5专用地址x2
# VITE_APP_BASE_H5_API_Url_TRAAPP = 'http://25.64.32.150:9601'
# dev
#VITE_APP_BASE_H5_API_Url = 'http://25.18.122.65:7001'
# sit
+29 -18
View File
@@ -20,7 +20,8 @@
<view class="img-div">
<image-preview :src="item.pointsBadgeImg" class="img" mode="aspectFit"></image-preview>
</view>
<view class="points-msg-div">{{ item.pointsBadgeName }}</view>
<view class="points-title-div">{{ item.pointsBadgeName }}</view>
<view class="points-msg-div" v-if="item.remark">{{ item.remark }}</view>
</swiper-item>
</swiper>
<view class="doct_icon right" @click="doct_click(1)" v-show="multiple">
@@ -30,7 +31,6 @@
</view>
</view>
</view>
<view class="button-div">
<uv-button
class="button"
@@ -56,16 +56,14 @@
const popup = ref(null);
const current = ref(0);
const opacity = ref(1);
let CloseComplete
let CloseComplete;
const badgesList = ref([]);
const pointsList = ref([]);
const multiple = computed(() => badgesList.value.length > 1);
const titleNumberText = ['', '两', '三', '四', '五', '六', '七', '八', '九', '十'];
const title = computed(() =>
badgesList.value.length === 1
? '恭喜你获得徽章'
: `恭喜你获得${titleNumberText[badgesList.value.length - 1]}枚徽章`
badgesList.value.length === 1 ? '恭喜你获得徽章' : `恭喜你获得${titleNumberText[badgesList.value.length - 1]}枚徽章`
);
const buttonColor = 'linear-gradient( 270deg, #F81758 0%, #FFA062 100%)';
const customStyle = {
@@ -101,7 +99,7 @@
// 点击
const clickFun = (status = true) => {
emits('confirm', status, pointsList.value, badgesList.value, () => {});
CloseComplete(status, pointsList.value, badgesList.value)
CloseComplete(status, pointsList.value, badgesList.value);
};
const open = (points = [], badges = [], complete = () => {}) => {
@@ -110,7 +108,7 @@
return;
}
console.log('badges', badges);
CloseComplete = complete
CloseComplete = complete;
badgesList.value = badges;
pointsList.value = points;
opacity.value = 1;
@@ -118,8 +116,7 @@
uni.$emit('update_me_data', true);
popup.value.open('center');
};
onUnmounted(() => {
});
onUnmounted(() => {});
defineExpose({ open, close });
</script>
@@ -130,20 +127,28 @@
align-items: center;
justify-content: space-between;
.swiper {
height: 660rpx;
height: 700rpx;
flex: 1;
margin: 0 50rpx;
// background-color: red;
.swiper-item-div {
display: flex;
align-items: center;
justify-content: center;
// justify-content: center;
flex-direction: column;
.points-title-div {
margin-top: 40rpx;
// position: absolute;
// bottom: 60rpx;
}
.points-msg-div {
position: absolute;
bottom: -30rpx;
margin: 30rpx 20rpx 0 20rpx;
// position: absolute;
// bottom: 0rpx;
}
.img-div {
margin-top: 60rpx;
position: relative;
display: flex;
align-items: center;
@@ -215,15 +220,21 @@
flex-direction: column;
}
}
.points-msg-div {
height: 120rpx;
.points-title-div {
// height: 120rpx;
font-family: PingFangSC, PingFang SC;
font-weight: 500;
font-size: 36rpx;
color: #dec38c;
line-height: 120rpx;
text-align: center;
font-style: normal;
}
.points-msg-div {
// height: 120rpx;
font-family: PingFangSC, PingFang SC;
font-weight: 500;
font-size: 26rpx;
color: #dec38c;
text-align: center;
}
.button-div {
height: 80rpx;
+9
View File
@@ -411,6 +411,15 @@
}
}
},
{
"path": "pages/test/testChat",
"style": {
"navigationBarTitleText": "",
"app-plus": {
"titleNView": false
}
}
},
{
"path": "pages/course/index",
"style": {
+1 -1
View File
@@ -522,7 +522,7 @@
top: 14rpx;
background-image: url(@/static/images/analytics/den-tips.png);
background-repeat: no-repeat;
background-size: cover;
background-size: 100% 100%;
}
&.study2-tip{
&::before {
+3 -1
View File
@@ -75,6 +75,8 @@
<view class="org">
{{ data_info.isAnony === 'N' ? data_info.orgIdName : '机构不显示' }}
</view>
<view class="fl1"></view>
</view>
</view>
<view class="right animate-right">
@@ -195,7 +197,6 @@
comId
});
const pkData = pkDataBody.body;
// 下面两行是给后端擦屁股,后端传回的数值为空
pkData.orgId = orgId;
pkData.papersId = papersId;
pkData.userId = data_info.userId;
@@ -214,6 +215,7 @@
});
// 2. 执行匹配成功动画(等待1.5秒)
await new Promise((resolve) => setTimeout(resolve, 600));
// 3. 清除计时器
if (practiceTimeTimer) {
clearInterval(practiceTimeTimer);
+16 -13
View File
@@ -8,7 +8,6 @@
</view>
</view>
<view class="title-view">
<!-- <view class="title-info">考试排行榜</view> -->
<image
class="title-image"
src="@/static/images/competition/ranking/ranking_title.png"
@@ -226,6 +225,8 @@
height: 108rpx;
border-bottom: 2rpx solid #efefef;
overflow: hidden;
display: flex;
&.list-header {
height: 54rpx;
@@ -262,7 +263,7 @@
font-size: 25rpx;
}
}
&.item-1 {
border-bottom: 0;
margin-bottom: 8rpx;
@@ -312,9 +313,9 @@
color: #06f;
}
}
.rank-column {
width: 170rpx;
width: 130rpx;
padding-left: 40rpx;
float: left;
font-weight: 600;
@@ -327,7 +328,9 @@
}
.user-column {
width: 300rpx;
// background-color: red;
// width: 330rpx;
flex: 1;
float: left;
padding: 15rpx 0;
@@ -338,7 +341,6 @@
float: left;
background-color: #eee;
border-radius: 50%;
.avatar-image {
width: 77rpx;
height: 77rpx;
@@ -346,8 +348,8 @@
}
.user-name {
float: left;
width: calc(100% - 77rpx);
// float: left;
// width: calc(100% - 77rpx);
padding-left: 18rpx;
padding-right: 18rpx;
overflow: hidden;
@@ -361,10 +363,7 @@
}
.user-org {
float: left;
width: calc(100% - 77rpx);
padding-left: 18rpx;
padding-right: 18rpx;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
@@ -376,7 +375,9 @@
}
.score-column {
width: 126rpx;
// background-color: yellow;
width: 88rpx;
padding-right: 10rpx;
text-align: right;
float: left;
font-weight: 600;
@@ -386,7 +387,9 @@
}
.time-column {
width: 100rpx;
width: 130rpx;
padding-right: 26rpx;
// background-color: #06f;
text-align: right;
float: left;
font-weight: 600;
+1 -1
View File
@@ -215,7 +215,7 @@
console.log('调用它');
pointAndBadgesRun('11');
}
}, delayTime);
}, delayTime * 1000);
});
}, mode.value === 'pk_result'?0:1000);
});
@@ -148,7 +148,8 @@ $primary-color: #ffe271; // 文字颜色
display: flex;
flex-direction: column;
align-items: center;
// background-color: red;
width: 60%;
.avatar-img-div {
width: 120rpx;
height: 120rpx;
@@ -163,32 +164,39 @@ $primary-color: #ffe271; // 文字颜色
.name {
font-size: 30rpx;
margin-top: 20rpx;
margin-bottom: 100rpx;
margin-bottom: 50rpx;
font-weight: 600;
font-size: 36rpx;
color: #ffffff;
// background-color: red;
}
.org {
// background-color: red;
font-weight: 400;
font-size: 30rpx;
font-size: 25rpx;
color: #ffffff;
text-align: center;
white-space: nowrap;
// white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
width: 160rpx;
// text-overflow: ellipsis;
// width: 160rpx;
}
}
.information-div-left {
position: absolute;
top: 0%;
left: 0%;
transform: translate(24%, -20%);
transform: translate(10%, -20%);
.org {
text-align: left;
}
}
.information-div-right {
position: absolute;
right: 0%;
top: 0%;
transform: translate(-24%, -20%);
transform: translate(-10%, -20%);
.org {
text-align: right;
}
}
}
+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' // 和打包后的目录一致
}
})