合并更改
This commit is contained in:
+5
-5
@@ -5,11 +5,11 @@ ENV = 'development'
|
|||||||
|
|
||||||
# VITE_APP_BASE_API_Url = 'https://aits.jlbank.com.cn:7001'
|
# 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:7002'
|
# VITE_APP_BASE_API_Url = 'https://aitstest.jlbank.com.cn:7002'
|
||||||
|
|
||||||
# VITE_APP_BASE_API_Url = 'https://aitstest.jlbank.com.cn:7001'
|
|
||||||
|
|
||||||
# VITE_APP_BASE_API_Url = 'http://192.168.247.200'
|
# VITE_APP_BASE_API_Url = 'http://192.168.247.200'
|
||||||
# VITE_APP_BASE_API_Url = 'http://aitscdn.jlbank.com.cn:7001'
|
# VITE_APP_BASE_API_Url = 'http://aitscdn.jlbank.com.cn:7001'
|
||||||
# VITE_APP_BASE_API_Url = 'http://192.168.108.129'
|
# VITE_APP_BASE_API_Url = 'http://192.168.108.129'
|
||||||
@@ -23,13 +23,13 @@ VITE_APP_BASE_API_Url = 'http://25.18.122.65:7001'
|
|||||||
# DEV
|
# DEV
|
||||||
# app入口
|
# 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.16.122.91:9786'
|
||||||
# VITE_APP_BASE_API_Url = 'http://25.18.122.66:9786'
|
# VITE_APP_BASE_API_Url = 'http://25.18.122.66:9786'
|
||||||
|
|
||||||
# h5专用地址x2
|
# h5专用地址x2
|
||||||
|
|
||||||
# dev
|
# dev
|
||||||
# VITE_APP_BASE_H5_API_Url = 'http://25.18.122.65:7001'
|
#VITE_APP_BASE_H5_API_Url = 'http://25.18.122.65:7001'
|
||||||
# sit
|
# sit
|
||||||
#VITE_APP_BASE_H5_API_Url = 'http://25.18.122.91:9786'
|
# VITE_APP_BASE_H5_API_Url = 'http://25.18.122.91:9786'
|
||||||
@@ -202,8 +202,16 @@
|
|||||||
console.log('声音加载完毕', e.duration);
|
console.log('声音加载完毕', e.duration);
|
||||||
// 计算原始时长
|
// 计算原始时长
|
||||||
const originalTime = Math.ceil(e.duration) || 1;
|
const originalTime = Math.ceil(e.duration) || 1;
|
||||||
// 判断是否在118-123秒范围内
|
let displayTime
|
||||||
const displayTime = originalTime >= 118 && originalTime <= 124 ? 120 : originalTime;
|
if(originalTime >= 118 && originalTime <= 122) {
|
||||||
|
// 判断是否在118-123秒范围内
|
||||||
|
displayTime = 120
|
||||||
|
} else if(originalTime >= 58 && originalTime <= 62) {
|
||||||
|
// 判断是否在58-62秒范围内
|
||||||
|
displayTime = 60
|
||||||
|
} else {
|
||||||
|
displayTime = originalTime
|
||||||
|
}
|
||||||
// 赋值显示
|
// 赋值显示
|
||||||
console.log('displayTime', displayTime);
|
console.log('displayTime', displayTime);
|
||||||
voiceTime.value = displayTime + 's';
|
voiceTime.value = displayTime + 's';
|
||||||
|
|||||||
@@ -39,7 +39,6 @@
|
|||||||
mode=""
|
mode=""
|
||||||
@click="changeBtn"
|
@click="changeBtn"
|
||||||
></image>
|
></image>
|
||||||
|
|
||||||
<image
|
<image
|
||||||
class="talk-btn-icon"
|
class="talk-btn-icon"
|
||||||
v-show="keyboardIsShow && !!inputText"
|
v-show="keyboardIsShow && !!inputText"
|
||||||
@@ -48,7 +47,6 @@
|
|||||||
@click.stop="showKeyboard"
|
@click.stop="showKeyboard"
|
||||||
></image>
|
></image>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<uni-popup ref="popupRef" class="popup">
|
<uni-popup ref="popupRef" class="popup">
|
||||||
<view class="overlay-box">
|
<view class="overlay-box">
|
||||||
<view class="circle-bg-box">
|
<view class="circle-bg-box">
|
||||||
@@ -147,7 +145,7 @@
|
|||||||
console.log('延迟开始');
|
console.log('延迟开始');
|
||||||
recordingStatus.value = 'preparing';
|
recordingStatus.value = 'preparing';
|
||||||
popupRef.value.open();
|
popupRef.value.open();
|
||||||
recorderManager.start({ duration: 120000 });
|
recorderManager.start({ duration: 59000 });
|
||||||
startTime = Date.now();
|
startTime = Date.now();
|
||||||
}, 80);
|
}, 80);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -89,15 +89,17 @@
|
|||||||
const expand = ref(false); // 展开状态
|
const expand = ref(false); // 展开状态
|
||||||
const isSigned = ref(false); // 今日签到状态
|
const isSigned = ref(false); // 今日签到状态
|
||||||
const continuousDay = ref(0); // 连续签到
|
const continuousDay = ref(0); // 连续签到
|
||||||
|
const continuousDay30 = computed(() => continuousDay.value % 30); // 连续签到取模30的天数
|
||||||
// 判断一下当前的签到显示在第几周
|
// 判断一下当前的签到显示在第几周
|
||||||
const nowLine = computed(() => {
|
const nowLine = computed(() => {
|
||||||
// 计算当前需要显示图标的天数(已签到则是连续天数,未签到则是下一天)
|
// 计算当前需要显示图标的天数(已签到则是连续天数,未签到则是下一天)
|
||||||
const currentDay = isSigned.value ? continuousDay.value : continuousDay.value + 1;
|
const currentDay = isSigned.value ? continuousDay30.value : continuousDay30.value + 1;
|
||||||
|
|
||||||
// 按每周7天分行,行数从0开始(例如:1-7天 → 第0行,8-14天 → 第1行...)
|
// 按每周7天分行,行数从0开始(例如:1-7天 → 第0行,8-14天 → 第1行...)
|
||||||
// 注意:如果天数从1开始(不是0),需要减1再计算
|
// 注意:如果天数从1开始(不是0),需要减1再计算
|
||||||
|
console.log('nowLine', Math.floor((currentDay - 1) / 7));
|
||||||
return Math.floor((currentDay - 1) / 7);
|
return Math.floor((currentDay - 1) / 7);
|
||||||
});
|
});
|
||||||
|
|
||||||
const emits = defineEmits(['checkInFun']);
|
const emits = defineEmits(['checkInFun']);
|
||||||
// 切换显示状态
|
// 切换显示状态
|
||||||
const switchStatus = () => {
|
const switchStatus = () => {
|
||||||
@@ -174,7 +176,7 @@
|
|||||||
|
|
||||||
// 计算当前是第几天(以"前X天"为第1天)
|
// 计算当前是第几天(以"前X天"为第1天)
|
||||||
const dayNumber = i - -X + 1; // 核心计算:转换为从1开始的序列
|
const dayNumber = i - -X + 1; // 核心计算:转换为从1开始的序列
|
||||||
|
|
||||||
// 计算text字段
|
// 计算text字段
|
||||||
let text = '1'; // 默认显示1
|
let text = '1'; // 默认显示1
|
||||||
if (dayNumber === 30) {
|
if (dayNumber === 30) {
|
||||||
@@ -208,8 +210,9 @@
|
|||||||
|
|
||||||
const open = (isSignedStatus, days, onCheckInClick = () => {}, onCheckInSuccess = () => {}) => {
|
const open = (isSignedStatus, days, onCheckInClick = () => {}, onCheckInSuccess = () => {}) => {
|
||||||
isSigned.value = isSignedStatus; // 今日签到状态
|
isSigned.value = isSignedStatus; // 今日签到状态
|
||||||
// isSigned.value = false; // 今日签到状态
|
|
||||||
continuousDay.value = days; // 连续签到
|
continuousDay.value = days; // 连续签到
|
||||||
|
// isSigned.value = false; // 今日签到状态
|
||||||
|
// continuousDay.value = 1; // 连续签到
|
||||||
checkInClickCallback = onCheckInClick;
|
checkInClickCallback = onCheckInClick;
|
||||||
checkInSuccessCallback = onCheckInSuccess;
|
checkInSuccessCallback = onCheckInSuccess;
|
||||||
// 示例:X=10时,以"10天前"为第1天,生成35天列表
|
// 示例:X=10时,以"10天前"为第1天,生成35天列表
|
||||||
@@ -218,7 +221,8 @@
|
|||||||
// const X = 10;
|
// const X = 10;
|
||||||
// console.log(`以${continuousDay.value}天`);
|
// console.log(`以${continuousDay.value}天`);
|
||||||
// console.log(`以${X}天前为第1天的35天分组日期:`);
|
// console.log(`以${X}天前为第1天的35天分组日期:`);
|
||||||
dateGroups.value = generateGroupedDateList(X);
|
console.log('取模', continuousDay30.value);
|
||||||
|
dateGroups.value = generateGroupedDateList(continuousDay30.value);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err.message);
|
console.error(err.message);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,12 +2,12 @@
|
|||||||
<view class="main_div max_page">
|
<view class="main_div max_page">
|
||||||
<template v-if="!isMatchingSuccess">
|
<template v-if="!isMatchingSuccess">
|
||||||
<!-- 顶部一些按钮 -->
|
<!-- 顶部一些按钮 -->
|
||||||
<view class="top-button-div">
|
<view class="top-button-div" v-if="!challengeLoading">
|
||||||
<view class="back_div" @click="common.navigateBack()">
|
<view class="back_div" @click="common.navigateBack()">
|
||||||
<view class="back-icon"></view>
|
<view class="back-icon"></view>
|
||||||
</view>
|
</view>
|
||||||
<view class="fl1"></view>
|
<view class="fl1"></view>
|
||||||
<view class="top-icon-div" @click="click_top_icon(1)">
|
<view class="top-icon-div" @click="click_top_icon(1)" >
|
||||||
<image class="img1" :src="data_info.isAnony === 'N' ? pkAnonymousOpen() : pkAnonymousClose()"></image>
|
<image class="img1" :src="data_info.isAnony === 'N' ? pkAnonymousOpen() : pkAnonymousClose()"></image>
|
||||||
<view class="text">{{ data_info.isAnony === 'N' ? '不匿名' : '匿名' }}</view>
|
<view class="text">{{ data_info.isAnony === 'N' ? '不匿名' : '匿名' }}</view>
|
||||||
</view>
|
</view>
|
||||||
@@ -20,6 +20,7 @@
|
|||||||
<view class="text">排行榜</view>
|
<view class="text">排行榜</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
<view class="top-button-div" v-else></view>
|
||||||
<view class="title-div" :class="{ hidden: challengeLoading }">
|
<view class="title-div" :class="{ hidden: challengeLoading }">
|
||||||
{{ data_info.comName }}
|
{{ data_info.comName }}
|
||||||
</view>
|
</view>
|
||||||
|
|||||||
@@ -124,7 +124,6 @@
|
|||||||
import { competitionAnswer, queryCompetitionPracticeAnswerResult, competitionAnswerEnd } from '@/api/competition.js';
|
import { competitionAnswer, queryCompetitionPracticeAnswerResult, competitionAnswerEnd } from '@/api/competition.js';
|
||||||
import { startExamByCrs } from '@/api/examination.js';
|
import { startExamByCrs } from '@/api/examination.js';
|
||||||
import { startPracticeByCrs } from '@/api/practice.js';
|
import { startPracticeByCrs } from '@/api/practice.js';
|
||||||
|
|
||||||
const feedbackRef = ref(null);
|
const feedbackRef = ref(null);
|
||||||
const dialogRef = ref(null);
|
const dialogRef = ref(null);
|
||||||
const touchBtnRef = ref(null);
|
const touchBtnRef = ref(null);
|
||||||
|
|||||||
@@ -31,13 +31,18 @@
|
|||||||
</view>
|
</view>
|
||||||
<view class="user-column">
|
<view class="user-column">
|
||||||
<view class="avatar-box">
|
<view class="avatar-box">
|
||||||
|
<image
|
||||||
|
v-if="item.isAnony !== 'N'"
|
||||||
|
class="avatar-image"
|
||||||
|
:src="defaultAnonymousAvatar"
|
||||||
|
></image>
|
||||||
<image-preview
|
<image-preview
|
||||||
v-if="item.ossAddr"
|
v-else-if="item.ossAddr"
|
||||||
class="avatar-image"
|
class="avatar-image"
|
||||||
:src="item.ossAddr"
|
:src="item.ossAddr"
|
||||||
mode="aspectFit"
|
mode="aspectFit"
|
||||||
></image-preview>
|
></image-preview>
|
||||||
<image v-else class="avatar-image" :src="defaultAnonymousAvatar"></image>
|
<image v-else class="avatar-image" :src="defaultAvatar"></image>
|
||||||
</view>
|
</view>
|
||||||
<view class="user-name">
|
<view class="user-name">
|
||||||
{{ item.isAnony === 'N' ? item.userName : formatName(item.userName) }}
|
{{ item.isAnony === 'N' ? item.userName : formatName(item.userName) }}
|
||||||
@@ -66,13 +71,13 @@
|
|||||||
:src="userRankObj.ossAddr"
|
:src="userRankObj.ossAddr"
|
||||||
mode="aspectFit"
|
mode="aspectFit"
|
||||||
></image-preview>
|
></image-preview>
|
||||||
<image class="avatar-image" v-else :src="defaultAnonymousAvatar"></image>
|
<image class="avatar-image" v-else :src="defaultAvatar"></image>
|
||||||
</view>
|
</view>
|
||||||
<view class="user-name">{{ userRankObj.isAnony === 'N' ? userRankObj.userName : formatName(userRankObj.userName) }}</view>
|
<view class="user-name">{{ userRankObj.userName }}</view>
|
||||||
<view class="user-org">{{ userRankObj.orgName }}</view>
|
<view class="user-org">{{ userRankObj.orgName }}</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="score-column">{{ userRankObj.examGrade }}</view>
|
<view class="score-column">{{ userRankObj.examGrade }}</view>
|
||||||
<view class="time-column">{{ userRankObj.time }}s</view>
|
<view class="time-column">{{ userRankObj.time ? userRankObj.time + 's' : '--' }}</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@@ -81,13 +86,12 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { queryTraCompetitionRankingList } from '@/api/competition.js';
|
import { queryTraCompetitionRankingList } from '@/api/competition.js';
|
||||||
import { defaultAvatar } from '@/enum.js';
|
|
||||||
import common from '@/common/common';
|
import common from '@/common/common';
|
||||||
import { onLoad } from '@dcloudio/uni-app';
|
import { onLoad } from '@dcloudio/uni-app';
|
||||||
import { formatSecondsToMS } from './js/tool';
|
import { formatSecondsToMS } from './js/tool';
|
||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
import { formatName } from '@/common/common';
|
import { formatName } from '@/common/common';
|
||||||
import { defaultAnonymousAvatar } from '@/enum';
|
import { defaultAnonymousAvatar, defaultAvatar } from '@/enum';
|
||||||
const papersId = ref('');
|
const papersId = ref('');
|
||||||
const orgId = ref('');
|
const orgId = ref('');
|
||||||
const userList = ref([]);
|
const userList = ref([]);
|
||||||
|
|||||||
@@ -74,7 +74,6 @@
|
|||||||
|
|
||||||
// 跳转到详情
|
// 跳转到详情
|
||||||
const click_list_item = (list_item) => {
|
const click_list_item = (list_item) => {
|
||||||
|
|
||||||
const pkData = {
|
const pkData = {
|
||||||
isAnony: list_item.isAnony,
|
isAnony: list_item.isAnony,
|
||||||
}
|
}
|
||||||
@@ -82,8 +81,6 @@
|
|||||||
common.navigateTo(
|
common.navigateTo(
|
||||||
`/pages/competition/result?mode=pk_result&execId=${list_item.execId}&orgId=${list_item.orgId}&papersId=${list_item.papersId}`
|
`/pages/competition/result?mode=pk_result&execId=${list_item.execId}&orgId=${list_item.orgId}&papersId=${list_item.papersId}`
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
defineExpose({clear})
|
defineExpose({clear})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -42,6 +42,7 @@
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
const refresherTouchend = () => {
|
const refresherTouchend = () => {
|
||||||
|
console.log('refresherTouchend');
|
||||||
clearAll();
|
clearAll();
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ export function useItemList(fetchFunction, item) {
|
|||||||
const show_list_state = ref(false);
|
const show_list_state = ref(false);
|
||||||
|
|
||||||
const list_div_height = computed(() => {
|
const list_div_height = computed(() => {
|
||||||
|
console.log('小仙仙', status.value, data_list.length);
|
||||||
const statusHeight = status.value === 'nomore' ? 0 : 100;
|
const statusHeight = status.value === 'nomore' ? 0 : 100;
|
||||||
if (!show_list_state.value) {
|
if (!show_list_state.value) {
|
||||||
return '0';
|
return '0';
|
||||||
@@ -66,6 +67,7 @@ export function useItemList(fetchFunction, item) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const getData = (from = '') => {
|
const getData = (from = '') => {
|
||||||
|
console.log('getData');
|
||||||
if (from === 'first') {
|
if (from === 'first') {
|
||||||
if (total.value !== -1) {
|
if (total.value !== -1) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -61,7 +61,7 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { onLoad } from '@dcloudio/uni-app';
|
import { onLoad } from '@dcloudio/uni-app';
|
||||||
import { onMounted, ref, onUnmounted } from 'vue';
|
import { onMounted, ref, onUnmounted, nextTick } from 'vue';
|
||||||
import common from '@/common/common';
|
import common from '@/common/common';
|
||||||
import { setPageCache } from '@/common/common';
|
import { setPageCache } from '@/common/common';
|
||||||
import { startExamByCrs } from '@/api/examination.js';
|
import { startExamByCrs } from '@/api/examination.js';
|
||||||
@@ -174,6 +174,7 @@
|
|||||||
console.log('提交练习状态成功');
|
console.log('提交练习状态成功');
|
||||||
commit.value = true;
|
commit.value = true;
|
||||||
pointAndBadgesRun('04', crsId.value, (status, msg, { points, pointsBadges, message }) => {
|
pointAndBadgesRun('04', crsId.value, (status, msg, { points, pointsBadges, message }) => {
|
||||||
|
console.log(status, msg, points, pointsBadges, message );
|
||||||
if(!status) return;
|
if(!status) return;
|
||||||
if (points?.length > 0) {
|
if (points?.length > 0) {
|
||||||
showData.value.num = points[0]['changePoints'];
|
showData.value.num = points[0]['changePoints'];
|
||||||
@@ -183,7 +184,9 @@
|
|||||||
if (pointsBadges.length > 0) {
|
if (pointsBadges.length > 0) {
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
badgeRef.value.open([], pointsBadges);
|
badgeRef.value.open([], pointsBadges, () => {
|
||||||
|
badgeRef.value.close()
|
||||||
|
});
|
||||||
}, pop_up_time);
|
}, pop_up_time);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
+161
-124
@@ -1,145 +1,182 @@
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<view>
|
<view class="container">
|
||||||
<video :is-live="true" id="myVideo" :src="videoSrc" :autoplay="true"></video>
|
<button @click="startPush" v-if="!isPushing">开始语音推流</button>
|
||||||
<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>
|
<button @click="stopPush" v-else>停止语音推流</button>
|
||||||
|
|
||||||
<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>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { prepareApi } from "@/api/talk.js"
|
|
||||||
var pusher
|
|
||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
context:null,
|
isPushing: false, // 推流状态
|
||||||
baseName: 'zhangsan'
|
pusher: null, // 推流实例
|
||||||
|
currentWebview: null // 当前 Webview 实例
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed:{
|
|
||||||
videoSrc(){
|
|
||||||
return 'rtmp://25.18.122.148:9605/voice_out/' + this.baseName
|
|
||||||
},
|
|
||||||
},
|
|
||||||
onReady() {
|
onReady() {
|
||||||
const videoContext = uni.createVideoContext('myVideo')
|
// 改用 onReady(页面渲染完成) + 延迟
|
||||||
videoContext.setVolume(1.0)
|
if (uni.getSystemInfoSync().platform === 'android') {
|
||||||
// 注意:需要在onReady中 或 onLoad 延时
|
// 安卓端延迟 1000ms,且重试 3 次,确保获取到 Webview
|
||||||
// pusher = uni.createLivePusherContext('livePusher', this)
|
let retryCount = 0
|
||||||
// console.log(pusher)
|
const getWebview = () => {
|
||||||
|
this.currentWebview = this.$mp.page.$getAppWebview()
|
||||||
|
if (this.currentWebview) {
|
||||||
|
console.log('Webview 获取成功:', this.currentWebview)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
retryCount++
|
||||||
|
if (retryCount < 3) {
|
||||||
|
setTimeout(getWebview, 500) // 每次重试间隔 500ms
|
||||||
|
} else {
|
||||||
|
uni.showToast({ title: 'Webview 初始化失败', icon: 'none' })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setTimeout(getWebview, 1000) // 初始延迟 1 秒
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onLoad() {
|
||||||
|
// // 仅 App 端初始化 Webview
|
||||||
|
// if (uni.getSystemInfoSync().platform !== 'app-plus') {
|
||||||
|
// uni.showToast({ title: '仅支持 App 端推流', icon: 'none' });
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// this.currentWebview = this.$mp.page.$getAppWebview();
|
||||||
|
},
|
||||||
|
onUnload() {
|
||||||
|
// 页面销毁时停止推流并销毁组件
|
||||||
|
this.stopPush()
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
statechange(e) {
|
// 申请麦克风权限
|
||||||
console.log('statechange:', e, JSON.stringify(e))
|
async requestMicPermission() {
|
||||||
},
|
return new Promise((resolve, reject) => {
|
||||||
netstatus(e) {
|
plus.android.requestPermissions(
|
||||||
console.log('netstatus:' + JSON.stringify(e))
|
['android.permission.RECORD_AUDIO'], // Android 权限
|
||||||
},
|
res => {
|
||||||
error(e) {
|
const granted = res.filter(item => item.granted).length === 1
|
||||||
console.log('error:' + JSON.stringify(e))
|
granted ? resolve() : reject('麦克风权限申请失败')
|
||||||
},
|
},
|
||||||
start: function () {
|
err => reject(err)
|
||||||
console.log('statechange:',pusher.start)
|
)
|
||||||
pusher.start({
|
// iOS 权限由系统自动弹窗,无需手动申请,此处可统一处理
|
||||||
success: a => {
|
|
||||||
console.log('livePusher.start:' + JSON.stringify(a))
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
prepare: function () {
|
async startPush() {
|
||||||
prepareApi(this.baseName).then(res=>{
|
if (uni.getSystemInfoSync().platform !== 'app-plus') return
|
||||||
console.log(res)
|
|
||||||
}).catch(err=>{
|
try {
|
||||||
console.log(err)
|
await this.requestMicPermission()
|
||||||
if(err === 'prepare'){
|
|
||||||
this.initPusher()
|
// 优先用原生 API 获取 Webview(兜底)
|
||||||
}
|
this.currentWebview = plus.webview.currentWebview() || this.$mp.page.$getAppWebview()
|
||||||
})
|
if (!this.currentWebview) {
|
||||||
},
|
throw new Error('无法获取页面 Webview 实例')
|
||||||
close: function () {
|
|
||||||
pusher.close({
|
|
||||||
success: a => {
|
|
||||||
console.log('livePusher.close:' + JSON.stringify(a))
|
|
||||||
}
|
}
|
||||||
})
|
|
||||||
|
// 后续创建 pusher + append 逻辑
|
||||||
|
this.pusher = plus.video.createLivePusher('', {
|
||||||
|
/* 配置 */
|
||||||
|
})
|
||||||
|
this.currentWebview.append(this.pusher) // 此时不会再为 null
|
||||||
|
// ... 启动推流
|
||||||
|
} catch (err) {
|
||||||
|
console.error('推流启动失败:', err)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
snapshot: function () {
|
// 开始推流
|
||||||
pusher.snapshot({
|
async startPush2() {
|
||||||
success: e => {
|
console.log('11', uni.getSystemInfoSync().platform)
|
||||||
console.log(JSON.stringify(e))
|
// 1. 端判断
|
||||||
}
|
if (uni.getSystemInfoSync().platform !== 'android') return
|
||||||
})
|
console.log('22')
|
||||||
|
try {
|
||||||
|
// 2. 申请麦克风权限
|
||||||
|
// await this.requestMicPermission();
|
||||||
|
console.log('33')
|
||||||
|
// 3. 创建 LivePusher 实例(仅音频,隐藏组件)
|
||||||
|
this.pusher = plus.video.createLivePusher('', {
|
||||||
|
url: 'rtmp://25.18.122.148:9605/voice_in/aa', // 你的 RTMP 推流地址
|
||||||
|
top: '0px',
|
||||||
|
left: '0px',
|
||||||
|
width: '1px', // 隐藏组件(仅音频无需显示)
|
||||||
|
height: '1px',
|
||||||
|
position: 'static',
|
||||||
|
// 核心:仅音频配置
|
||||||
|
video: {
|
||||||
|
enable: false // 关闭视频采集
|
||||||
|
},
|
||||||
|
audio: {
|
||||||
|
enable: true, // 开启音频采集
|
||||||
|
sampleRate: 16000, // 采样率(AI 语音识别常用 16k)
|
||||||
|
bitrate: 64000, // 音频码率
|
||||||
|
codec: 'aac' // 编码格式(RTMP 主流)
|
||||||
|
},
|
||||||
|
// 推流模式:仅音频直播
|
||||||
|
mode: 'rtmp',
|
||||||
|
autopush: false // 不自动推流,手动控制
|
||||||
|
})
|
||||||
|
console.log('44')
|
||||||
|
// 4. 挂载到 Webview
|
||||||
|
this.currentWebview.append(this.pusher)
|
||||||
|
|
||||||
|
// 5. 监听推流状态
|
||||||
|
this.pusher.addEventListener('statechange', e => {
|
||||||
|
console.log('推流状态变化:', e)
|
||||||
|
switch (e.state) {
|
||||||
|
case 'connecting':
|
||||||
|
uni.showToast({ title: '正在连接推流服务器', icon: 'none' })
|
||||||
|
break
|
||||||
|
case 'connected':
|
||||||
|
this.isPushing = true
|
||||||
|
uni.showToast({ title: '推流成功', icon: 'success' })
|
||||||
|
break
|
||||||
|
case 'disconnected':
|
||||||
|
this.isPushing = false
|
||||||
|
uni.showToast({ title: '推流断开', icon: 'none' })
|
||||||
|
break
|
||||||
|
case 'error':
|
||||||
|
this.isPushing = false
|
||||||
|
uni.showToast({ title: `推流错误:${e.error}`, icon: 'none' })
|
||||||
|
break
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 6. 启动预览 + 推流
|
||||||
|
this.pusher.startPreview() // 启动音视频采集(必须调用)
|
||||||
|
this.pusher.start() // 开始推流
|
||||||
|
} catch (err) {
|
||||||
|
uni.showToast({ title: err, icon: 'none' })
|
||||||
|
console.error('推流启动失败:', err)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
resume: function () {
|
|
||||||
pusher.resume({
|
// 停止推流
|
||||||
success: a => {
|
stopPush() {
|
||||||
console.log('livePusher.resume:' + JSON.stringify(a))
|
if (!this.pusher) return
|
||||||
}
|
// 停止推流 + 预览
|
||||||
})
|
this.pusher.stop()
|
||||||
},
|
this.pusher.stopPreview()
|
||||||
pause: function () {
|
// 销毁组件
|
||||||
pusher.pause({
|
this.pusher.close()
|
||||||
success: a => {
|
this.pusher = null
|
||||||
console.log('livePusher.pause:' + JSON.stringify(a))
|
this.isPushing = false
|
||||||
}
|
uni.showToast({ title: '推流已停止', icon: 'none' })
|
||||||
})
|
}
|
||||||
},
|
|
||||||
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(){
|
|
||||||
console.log(1)
|
|
||||||
pusher = new plus.video.LivePusher('pusher-box',{
|
|
||||||
url: 'rtmp://25.18.122.148:9605/voice_in/' + this.baseName,
|
|
||||||
"enable-camera": false
|
|
||||||
})
|
|
||||||
pusher.addEventListener('statechange', this.statechange)
|
|
||||||
pusher.addEventListener('netstatus', this.netstatus)
|
|
||||||
pusher.addEventListener('error', this.error)
|
|
||||||
console.log(pusher)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<style>
|
|
||||||
.btn{
|
<style scoped>
|
||||||
width: 100%;
|
.container {
|
||||||
height: 80rpx;
|
padding: 20px;
|
||||||
background-color: #ccc;
|
}
|
||||||
margin-bottom: 5rpx;
|
button {
|
||||||
}
|
height: 80rpx;
|
||||||
|
line-height: 80rpx;
|
||||||
|
background: #007aff;
|
||||||
|
color: #fff;
|
||||||
|
border-radius: 10rpx;
|
||||||
|
margin-bottom: 20rpx;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,29 +1,41 @@
|
|||||||
|
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<view>
|
<view>
|
||||||
<video :is-live="true" :src="videoSrc" :autoplay="true" @error="error"></video>
|
<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>
|
<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>
|
||||||
<button class="btn" @click="start">开始推流</button>
|
|
||||||
<button class="btn" @click="pause">暂停推流</button>
|
<view class="btn" @click="prepare"><text>预热</text></view>
|
||||||
<button class="btn" @click="resume">resume</button>
|
<view class="btn" @click="start"><text>开始推流</text></view>
|
||||||
<button class="btn" @click="stop">停止推流</button>
|
<view class="btn" @click="pause"><text>暂停推流</text></view>
|
||||||
<button class="btn" @click="snapshot">快照</button>
|
<view class="btn" @click="resume"><text>恢复推流</text></view>
|
||||||
<button class="btn" @click="startPreview">开启摄像头预览1</button>
|
<view class="btn" @click="stop"><text>停止推流</text></view>
|
||||||
<button class="btn" @click="stopPreview">关闭摄像头预览</button>
|
<view class="btn" @click="snapshot"><text>快照</text></view>
|
||||||
<button class="btn" @click="switchCamera">切换摄像头</button>
|
<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>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import { prepareApi } from '@/api/talk.js'
|
||||||
|
var pusher
|
||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
videoSrc: 'rtmp://25.18.122.148:9605/voice_out/aa'
|
context: null,
|
||||||
|
baseName: 'zhangsan'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
videoSrc() {
|
||||||
|
return 'rtmp://25.18.122.148:9605/voice_out/' + this.baseName
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onReady() {
|
onReady() {
|
||||||
|
const videoContext = uni.createVideoContext('myVideo')
|
||||||
|
videoContext.setVolume(1.0)
|
||||||
// 注意:需要在onReady中 或 onLoad 延时
|
// 注意:需要在onReady中 或 onLoad 延时
|
||||||
this.context = uni.createLivePusherContext('livePusher', this)
|
// pusher = uni.createLivePusherContext('livePusher', this)
|
||||||
|
// console.log(pusher)
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
statechange(e) {
|
statechange(e) {
|
||||||
@@ -36,69 +48,100 @@ export default {
|
|||||||
console.log('error:' + JSON.stringify(e))
|
console.log('error:' + JSON.stringify(e))
|
||||||
},
|
},
|
||||||
start: function () {
|
start: function () {
|
||||||
this.context.start({
|
console.log('statechange:', pusher.start)
|
||||||
|
pusher.start({
|
||||||
success: a => {
|
success: a => {
|
||||||
console.log('livePusher.start:' + JSON.stringify(a))
|
console.log('livePusher.start:' + JSON.stringify(a))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
prepare: function () {
|
||||||
|
prepareApi(this.baseName)
|
||||||
|
.then(res => {
|
||||||
|
console.log(res)
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
console.log(err)
|
||||||
|
if (err === 'prepare') {
|
||||||
|
this.initPusher()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
close: function () {
|
close: function () {
|
||||||
this.context.close({
|
pusher.close({
|
||||||
success: a => {
|
success: a => {
|
||||||
console.log('livePusher.close:' + JSON.stringify(a))
|
console.log('livePusher.close:' + JSON.stringify(a))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
snapshot: function () {
|
snapshot: function () {
|
||||||
this.context.snapshot({
|
pusher.snapshot({
|
||||||
success: e => {
|
success: e => {
|
||||||
console.log(JSON.stringify(e))
|
console.log(JSON.stringify(e))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
resume: function () {
|
resume: function () {
|
||||||
this.context.resume({
|
pusher.resume({
|
||||||
success: a => {
|
success: a => {
|
||||||
console.log('livePusher.resume:' + JSON.stringify(a))
|
console.log('livePusher.resume:' + JSON.stringify(a))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
pause: function () {
|
pause: function () {
|
||||||
this.context.pause({
|
pusher.pause({
|
||||||
success: a => {
|
success: a => {
|
||||||
console.log('livePusher.pause:' + JSON.stringify(a))
|
console.log('livePusher.pause:' + JSON.stringify(a))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
stop: function () {
|
stop: function () {
|
||||||
this.context.stop({
|
pusher.stop({
|
||||||
success: a => {
|
success: a => {
|
||||||
console.log(JSON.stringify(a))
|
console.log(JSON.stringify(a))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
switchCamera: function () {
|
switchCamera: function () {
|
||||||
this.context.switchCamera({
|
pusher.switchCamera({
|
||||||
success: a => {
|
success: a => {
|
||||||
console.log('livePusher.switchCamera:' + JSON.stringify(a))
|
console.log('livePusher.switchCamera:' + JSON.stringify(a))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
startPreview: function () {
|
startPreview: function () {
|
||||||
this.context.startPreview({
|
pusher.startPreview({
|
||||||
success: a => {
|
success: a => {
|
||||||
console.log('livePusher.startPreview:' + JSON.stringify(a))
|
console.log('livePusher.startPreview:' + JSON.stringify(a))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
stopPreview: function () {
|
stopPreview: function () {
|
||||||
this.context.stopPreview({
|
pusher.stopPreview({
|
||||||
success: a => {
|
success: a => {
|
||||||
console.log('livePusher.stopPreview:' + JSON.stringify(a))
|
console.log('livePusher.stopPreview:' + JSON.stringify(a))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
initPusher() {
|
||||||
|
console.log(1)
|
||||||
|
pusher = new plus.video.LivePusher('pusher-box', {
|
||||||
|
url: 'rtmp://25.18.122.148:9605/voice_in/' + this.baseName,
|
||||||
|
'enable-camera': false
|
||||||
|
})
|
||||||
|
pusher.addEventListener('statechange', this.statechange)
|
||||||
|
pusher.addEventListener('netstatus', this.netstatus)
|
||||||
|
pusher.addEventListener('error', this.error)
|
||||||
|
console.log(pusher)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<style></style>
|
<style>
|
||||||
|
.btn {
|
||||||
|
width: 100%;
|
||||||
|
height: 80rpx;
|
||||||
|
background-color: #ccc;
|
||||||
|
margin-bottom: 5rpx;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user