实时视频相关接口联调

This commit is contained in:
tdg930622 2025-01-09 17:50:56 +08:00
parent 31247fce3a
commit f44044e0bd
4 changed files with 300 additions and 115 deletions

View File

@ -0,0 +1,219 @@
<template>
<div>
<el-dialog title="邀请" :visible="true" width="600px" append-to-body :close-on-click-modal="false"
@close="handleClose">
<div class="dialog-content dialog-office-batch">
<div class="pt-8">
<el-form ref="ruleFormRepayment"
:model="repaymentObj"
:rules="rulesClientRule"
label-width="130px">
<el-row :gutter="56">
<el-col :span="24">
<el-form-item label="当事人" prop="litigants">
<el-select v-model="repaymentObj.litigants" multiple collapse-tags filterable
placeholder="请选择" class="width100">
<el-option
v-for="(item,index) in litigantsOptions"
:key="index"
:label="item.name+ ' ' +'('+ item.phone +')'"
:value="item.identity">
</el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="邀请其他人" prop="members">
<el-button
icon="el-icon-plus"
size="small"
type="text"
@click="addForm">邀请其他人
</el-button>
</el-form-item>
</el-col>
<el-col :span="24" v-for="(item,index) in repaymentObj.members" :key="index">
<el-form-item label="" prop="" label-width="0">
<el-row :gutter="20" style="background-color: rgb(245, 246, 250);margin-bottom: 5px;">
<el-col :span="7">
<div>身份</div>
</el-col>
<el-col :span="7">
<div>姓名</div>
</el-col>
<el-col :span="7">
<div>电话</div>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="7">
<el-form-item
:prop="`members[${index}].identity`"
:rules="[
{ required: true, message: '请选择', trigger: ['blur','change'],},
]">
<el-select v-model="item.identity" placeholder="请选择">
<el-option
v-for="(item,index) in identityOptions"
:key="index"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="7">
<el-form-item
:prop="`members[${index}].name`"
:rules="{ required: true, message: '请输入', trigger: ['blur','change'] }">
<el-input
v-model="item.name"
placeholder="请输入"
></el-input>
</el-form-item>
</el-col>
<el-col :span="7">
<el-form-item
:prop="`members[${index}].phone`"
:rules="[
{ required: true, message: '请输入', trigger: ['blur','change'],},
{ pattern: /^(13[0-9]{9})|(18[0-9]{9})|(14[0-9]{9})|(17[0-9]{9})|(15[0-9]{9})|(19[0-9]{9})$/, message: '请输入正确的手机号码', trigger: ['blur', 'change']}
]">
<el-input
v-model="item.phone"
placeholder="请输入"
></el-input>
</el-form-item>
</el-col>
<el-col :span="3">
<div class="">
<el-button
icon="el-icon-delete"
size="small"
type="text"
@click="deleteForm(index)">删除
</el-button>
</div>
</el-col>
</el-row>
</el-form-item>
</el-col>
</el-row>
</el-form>
</div>
</div>
<span slot="footer" class="dialog-footer">
<el-button @click="handleClose()">取消</el-button>
<el-button type="primary" @click="handleSubmit()">立即邀请</el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import videoTelephone from "@/services/videoTelephone";
export default {
components: {},
props: {
eventDialog: {
type: Object,
default: () => {
return {}
},
},
},
data() {
return {
litigantsOptions: [],
identityOptions: [
{ label: '法官', value: '法官' },
{ label: '调解专家', value: '调解专家' },
{ label: '律师', value: '律师' },
{ label: '民警', value: '民警' },
{ label: '其他', value: '其他' }
],
repaymentObj: {
litigants: [], //{name: xx, phone: xx, identity: xx}
members: [], //{name: xx, phone: xx, identity: xx}
id: '', // ID
},
rulesClientRule: {
litigants: [
{ type: 'array', required: true, message: '请选择', trigger: 'change' }
],
},
};
},
mounted() {
this.getLitigantByCaseId();
},
methods: {
getLitigantByCaseId() {
videoTelephone.getLitigantByCaseId(this.eventDialog.caseId).then(res => {
if (!res.code) {
this.litigantsOptions = res;
}
})
},
addForm() {
this.repaymentObj.members.push({ identity: '', name: '', phone: ''})
},
deleteForm(index) {
this.repaymentObj.members.splice(index, 1)
},
handleClose() {
this.$emit('update:eventDialog', null)
},
handleSubmit() {
if(!this.$clickThrottle()) { return }//
this.$refs.ruleFormRepayment.validate((valid) => {
if (valid) {
const resultArr = this.litigantsOptions
.filter(item => this.repaymentObj.litigants.includes(item.identity))
.map(({name, phone, identity}) => ({name, phone, identity}));
this.repaymentObj.id = this.eventDialog.id;
videoTelephone.videoInvite({...this.repaymentObj, litigants: resultArr}).then(res => {
this.handleClose()
this.$message.success("邀请成功");
})
}
})
}
}
};
</script>
<style scoped lang="scss">
.dialog-office-batch {
padding: 16px 30px;
.tabs__search-criteria-title {
width: 100%;
}
.case-batch-num {
background-color: rgba(236, 238, 241, 0.8196078431);
padding: 15px 20px;
border-radius: 4px;
a {
font-size: 16px;
font-weight: 500;
color: #C66A5B;
}
}
.officelist {
background-color: rgba(236, 238, 241, 0.8196078431);
padding: 20px 20px;
.el-checkbox {
width: 230px;
margin: 10px;
}
}
}
</style>

View File

@ -1,102 +0,0 @@
<template>
<div
:style="contentStyle"
class="content"
@mousedown="startDrag"
@mouseup="stopDrag"
@mouseleave="stopDrag">
<div class="drag-div" @mousedown.stop="startDrag"></div>
<div>Other content</div>
</div>
</template>
<script>
export default {
data() {
return {
position: { x: 0, y: 0 },
dragging: false,
offsetX: 0,
offsetY: 0,
mouseMoveHandler: null,
mouseUpHandler: null
};
},
computed: {
contentStyle() {
return {
left: `${this.position.x}px`,
top: `${this.position.y}px`,
};
}
},
methods: {
startDrag(event) {
event.preventDefault();
if (!event.target.closest('.drag-div')) return;
this.dragging = true;
this.offsetX = event.clientX - this.position.x;
this.offsetY = event.clientY - this.position.y;
this.removeEventListeners();
this.mouseMoveHandler = this.onMouseMove.bind(this);
this.mouseUpHandler = this.stopDrag.bind(this);
document.addEventListener('mousemove', this.mouseMoveHandler, true);
document.addEventListener('mouseup', this.mouseUpHandler, true);
},
onMouseMove(event) {
if (this.dragging) {
requestAnimationFrame(() => {
this.updatePosition(event.clientX, event.clientY);
});
}
},
updatePosition(clientX, clientY) {
this.position.x = clientX - this.offsetX;
this.position.y = clientY - this.offsetY;
this.$forceUpdate();
},
stopDrag() {
this.dragging = false;
this.removeEventListeners();
},
removeEventListeners() {
if (this.mouseMoveHandler) {
document.removeEventListener('mousemove', this.mouseMoveHandler, true);
}
if (this.mouseUpHandler) {
document.removeEventListener('mouseup', this.mouseUpHandler, true);
}
}
},
destroyed() {
this.removeEventListeners();
}
};
</script>
<style scoped>
.content {
width: 200px;
height: 200px;
position: absolute;
border: 1px solid #ccc;
box-sizing: border-box;
user-select: none;
}
.drag-div {
width: 50px;
height: 50px;
background-color: #f00;
text-align: center;
line-height: 50px;
color: white;
cursor: grab;
}
</style>

View File

@ -1,7 +1,10 @@
<template>
<div ref="videoRoom" class="room-content border-radius-8" v-vdrag :class="zoomActive ? `room-content-big` : 'room-content-small'">
<div class="room-top flex-row justify-content-between align-items-center">
<div class="f16 f-weight600">调解视频画面</div>
<div class="f16 f-weight600 flex-row">
<div class="mr-8">调解视频画面</div>
<div class="color-F53F3F" v-if="videoRecordingSts">{{ formattedTime }}</div>
</div>
<div class="room-top-btn background-color-fff flex-row align-items-center border-radius-4 cursor-pointer"
@click="toggleZoom">
<span class="mr-4">{{ zoomActive ? '缩小' : '放大' }}</span>
@ -35,9 +38,9 @@
<div v-if="zoomActive" class="room-bottom border-radius-8 background-color-fff flex-row justify-content-between align-items-center">
<div class="flex-row align-items-center cursor-pointer" @click="handleVideoRecording">
<svg-icon :icon-class="videoRecordingSts ? 'video-recording-2' : 'video-recording-1'" className="tabs-svg" />
<div class="pl-4 f14">{{ videoRecordingSts ? '录制中': '录制' }}</div>
<div class="pl-4 f14" :class="videoRecordingSts ? 'color-F53F3F' : ''">{{ videoRecordingSts ? '结束': '录制' }}</div>
</div>
<div class="flex-row align-items-center cursor-pointer">
<div class="flex-row align-items-center cursor-pointer" @click="InviteDialog={caseId:caseId,id:eventDialog.id}">
<svg-icon icon-class="Invite-room" className="tabs-svg" />
<div class="pl-4 f14">邀请</div>
</div>
@ -49,14 +52,17 @@
<i class="f24 el-icon-turn-off-microphone color-4E5969"></i>
<div class="pl-4 f14">开启语音</div>
</div>
<div class="flex-row align-items-center cursor-pointer" @click="handleOffRemoteAudio('')">
<div class="flex-row align-items-center cursor-pointer"
@click="audioRecordingSts ? handleOffRemoteAudio('') : handleOnRemoteAudio('')">
<svg-icon icon-class="mute-all" className="tabs-svg" />
<div class="pl-4 f14">全部静音</div>
<div class="pl-4 f14">{{audioRecordingSts ? '全部静音' : '全部开启'}}</div>
</div>
<div class="room-bottom-btn-finish cursor-pointer border-radius-4 f14 color-fff" @click="handleStopMediate">
结束调解
</div>
</div>
<VideoInvitation v-if="InviteDialog" :eventDialog.sync="InviteDialog" />
</div>
</template>
@ -64,6 +70,9 @@
import TRTC from 'trtc-sdk-v5';
import videoTelephone from "@/services/videoTelephone";
export default {
components: {
VideoInvitation: () => import('./VideoInvitation'),//
},
props: {
eventDialog: {
type: Object,
@ -76,6 +85,7 @@ export default {
return {
zoomActive: true,// true false
caseId: '',
arrPersonnel: [],
roomId: 0,
sdkAppId: 0,
@ -91,7 +101,13 @@ export default {
personnelNumber: 1,
remoteUsersViews: [],
videoRecordingSts: false
audioRecordingSts: true, // true false
videoRecordingSts: false, // true false
startTime: null,
timerId: null,
duration: 0, //
InviteDialog: null, //
};
},
computed: {
@ -105,7 +121,13 @@ export default {
const itemsPerRow = 4;
const rowHeight = 35.5;
const numberOfRows = Math.ceil(this.personnelNumber / itemsPerRow);
return `${numberOfRows * rowHeight + 35.5}px`;
return `${numberOfRows * rowHeight + 37.5}px`;
},
formattedTime() {
const hours = Math.floor(this.duration / 3600);
const minutes = Math.floor((this.duration % 3600) / 60);
const seconds = this.duration % 60;
return `${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}:${String(Math.floor(seconds)).padStart(2, '0')}`;
}
},
mounted() {
@ -123,7 +145,7 @@ export default {
this.$nextTick(() => {
if (this.$refs.videoRoom) {
console.log(this.defaultTop, '---this.defaultTop')
console.log(this.defaultTop, this.personnelNumber, '---this.defaultTop')
this.$refs.videoRoom.style.left = this.zoomActive ? this.zoomedStyles.left : '157px';
this.$refs.videoRoom.style.top = this.zoomActive ? this.zoomedStyles.top : this.defaultTop;
}
@ -134,7 +156,8 @@ export default {
getVideoDetail() {
videoTelephone.videoDetail({id: this.eventDialog.id}).then((res) => {
// console.log(res, '---videoDetail')
this.arrPersonnel = [...res.litigants, ...res.members]
this.caseId = res.caseId;
this.arrPersonnel = [...res.litigants, ...res.members];
});
},
//
@ -344,7 +367,7 @@ export default {
console.log(event, '---event远端退出房间')
const { streamType } = event;
this.trtc.stopRemoteVideo({ userId: event.userId, streamType });
this.personnelNumber--;
if (this.personnelNumber>1){this.personnelNumber--;}
this.remoteUsersViews = this.remoteUsersViews.filter(item => item.userId !== event.userId);
},
//
@ -356,6 +379,16 @@ export default {
async handleOffRemoteAudio(userId) {
console.log(userId, '---关闭远端声音')
try {
if (!userId){
this.audioRecordingSts = false;
this.remoteUsersViews = this.remoteUsersViews.map(item => {
item.microphone = false;
return {
...item,
microphone: false
}
})
}
let jsonString = JSON.stringify({behavior: 'offAudio', userId: userId});
let encoder = new TextEncoder();
let encoded = encoder.encode(jsonString).buffer;
@ -376,6 +409,16 @@ export default {
async handleOnRemoteAudio(userId) {
console.log(userId, '---开启远端声音')
try {
if (!userId){
this.audioRecordingSts = true;
this.remoteUsersViews = this.remoteUsersViews.map(item => {
item.microphone = true;
return {
...item,
microphone: true
}
})
}
let jsonString = JSON.stringify({behavior: 'onAudio', userId: userId});
let encoder = new TextEncoder();
let encoded = encoder.encode(jsonString).buffer;
@ -404,15 +447,34 @@ export default {
},
//
updateTimerDisplay() {
if (!this.startTime) return;
const currentTime = performance.now();
this.duration = Math.floor((currentTime - this.startTime) / 1000); //
},
handleVideoRecording(){
if (this.videoRecordingSts){
// clearInterval(this.timerId);
// this.timerId = null;
// this.videoRecordingSts = false;
// return
videoTelephone.stopRecord({id: this.eventDialog.id}).then((res) => {
console.log('---视频已停止录制')
clearInterval(this.timerId);
this.timerId = null;
this.videoRecordingSts = false;
});
}else {
// this.startTime = performance.now();
// this.updateTimerDisplay(); //
// this.timerId = setInterval(() => this.updateTimerDisplay(), 1000); //
// this.videoRecordingSts = true;
// return
videoTelephone.startRecord({id: this.eventDialog.id}).then((res) => {
console.log('---视频已开始录制')
this.startTime = performance.now();
this.updateTimerDisplay(); //
this.timerId = setInterval(() => this.updateTimerDisplay(), 1000); //
this.videoRecordingSts = true;
});
}
@ -428,12 +490,18 @@ export default {
let data = {id:this.eventDialog.id}
// if (this.videoRecordingSts){
// this.handleVideoRecording();
// }
// } //
videoTelephone.stopMediate(data).then((res) => {
this.handleClose();
this.$parent.visiblePopover = null
});
}).catch(() => {});
}
},
beforeDestroy() {
if (this.timerId) {
clearInterval(this.timerId);
}
}
};
</script>

View File

@ -27,7 +27,7 @@
<div class="f12">{{item.content}}</div>
<div class="flex-row align-items-center">
<div class="f16 mr-8 cursor-pointer"
v-if="item.roomId && item.sdkAppId && item.userId && item.userSig"
v-if="item.roomId && item.sdkAppId && item.userId && item.userSig && item.status.code === 0"
@click="handleVideoCall(item)">
<i class="el-icon-video-camera"></i>
</div>
@ -37,7 +37,7 @@
<i class="el-icon-edit-outline"></i>
</div>
<div class="f16 mr-8 cursor-pointer"
v-if="item.status.code === 1"
v-if="item.status.code === 0"
@click="handleBackCase(item)">
<i class="el-icon-delete"></i>
</div>