This commit is contained in:
lcw
2025-11-22 21:59:58 +08:00
parent ea3022c3f6
commit 93c49dff27
661 changed files with 195357 additions and 2160 deletions

View File

@ -0,0 +1,136 @@
<template>
<el-dialog width="1400px" :draggable="true" custom-class="bgData" :model-value="modelValue" append-to-body
@close="close" :show-close="false">
<template #title>
<div class="title">
<div class="titleName">测试数据</div>
<div>
<el-icon :size="25" class="titleIcon">
<BottomLeft />
</el-icon>
<el-icon :size="25" class="titleIcon">
<FullScreen />
</el-icon>
<el-icon @click="close" :size="25" class="titleIcon">
<Close />
</el-icon>
</div>
</div>
<div class="statusBar">
<div>00:04</div>
<div>正在讲话...</div>
<div>
<el-icon :size="25" class="titleIcon">
<InfoFilled />
</el-icon>
<el-icon :size="25" class="titleIcon">
<CircleCheckFilled />
</el-icon>
<el-icon @click="close" :size="25" class="titleIcon">
<Unlock />
</el-icon>
</div>
</div>
</template>
<div class="content"></div>
<template #footer>
<Footer @close="close" />
</template>
</el-dialog>
</template>
<script setup>
import { ref, watch, reactive, toRaw, computed, getCurrentInstance } from "vue";
import Footer from './footer.vue'
// import useCallModule from '../sdk/call.js'
const { proxy } = getCurrentInstance();
// const Call = useCallModule()
lemon.login.login({
username: "linzhigongan1",
password: "linzhigongan1",
realm: "puc.com",
webpucUrl: "https://192.168.0.1:16888",
}).then(res => {
console.log(res,"登录成功");
}).catch(err => {
console.log(err,"登录失败");
})
const props = defineProps({
modelValue: {
type: Boolean,
default: false
},
titleValue: {
type: String,
default: ''
}
})
const emit = defineEmits(['update:modelValue']);
const close = () => {
emit('update:modelValue', false);
}
</script>
<style lang="scss" scoped>
@import "@/assets/css/layout.scss";
@import "@/assets/css/element-plus.scss";
.title {
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid #e4e7ed;
padding: 10px;
.titleName {
font-size: 20px;
margin-bottom: 5px;
width: 40%;
}
.titleIcon {
margin-left: 15px;
}
}
.statusBar {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px 6px;
}
.content {
padding: 0 10px;
height: 60vh;
overflow: auto;
background-color: #000;
}
.tabBoxRadio .el-checkbox__inner {
border-radius: 50% !important;
}
.tabBoxRadio .el-table__header-wrapper .el-checkbox {
display: none;
}
.bgData {
padding: 10px;
}
::v-deep .el-dialog__body {
padding: 0 !important;
}
</style>
<style>
.bgData .el-dialog__header,
.el-dialog__body {
padding: 0;
}
</style>

View File

@ -0,0 +1,88 @@
<template>
<div class="footer">
<div class="footerWidth">
<div>
<div class="icon"><img src="@/assets/images/icon/jyy-k.png" alt=""></div>
<div>麦克风</div>
</div>
<div>
<div class="icon"><img src="@/assets/images/icon/lb-k.png" alt=""></div>
<div>喇叭</div>
</div>
<div>
<div class="icon"><img src="@/assets/images/icon/sp-k.png" alt=""></div>
<div>摄像头</div>
</div>
<div>
<div class="icon"><img src="@/assets/images/icon/fx.png" alt=""></div>
<div>共享</div>
</div>
<div>
<div class="icon"><img src="@/assets/images/icon/hyyq.png" alt=""></div>
<div>邀请参会</div>
</div>
<div>
<div class="icon"><img src="@/assets/images/icon/glcy.png" alt=""></div>
<div>管理成员</div>
</div>
<div>
<div class="icon"><img src="@/assets/images/icon/jt.png" alt=""></div>
<div>截图</div>
</div>
<div>
<div class="icon"><img src="@/assets/images/icon/gb.png" alt=""></div>
<div>视频广播</div>
</div>
<div>
<div class="icon"><img src="@/assets/images/icon/yyjl.png" alt=""></div>
<div>语音激励</div>
</div>
<div>
<div class="icon"><img src="@/assets/images/icon/fz.png" alt=""></div>
<div>画面反转</div>
</div>
<div>
<div class="icon"><img src="@/assets/images/icon/yy.png" alt=""></div>
<div>应用</div>
</div>
</div>
<div class="footerBtn">
<el-button type="danger">结束会议</el-button>
</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
const emit=defineEmits(['close'])
const close=()=>{
emit('close')
}
</script>
<style scoped lang="scss">
.footer {
display: flex;
justify-content: space-between;
align-items: center;
height: 50px;
.footerWidth {
display: flex;
justify-content: space-between;
align-items: center;
flex: 1;
.icon{
width: 25px;
margin: 0 auto;
img{
width: 100%;
}
}
}
.footerBtn {
width: 100px;
margin-left: 20px;
}
}
</style>

View File

@ -0,0 +1,139 @@
<template>
<el-dialog width="1400px" title="选择参会人员" :draggable="true" :model-value="modelValue" append-to-body @close="close"
:show-close="false">
<div class="content">
<div class="left">
<el-tabs v-model="activeName" @tab-click="handleClick" :stretch="true">
<el-tab-pane label="全网设备" name="first">
<el-tree-v2 style="max-width: 600px" :data="data" :props="propsData" :height="530" />
</el-tab-pane>
<el-tab-pane label="调度台" name="second">Config</el-tab-pane>
<el-tab-pane label="动态警组" name="third">Role</el-tab-pane>
<el-tab-pane label="第三方会议" name="fourth">Task</el-tab-pane>
<el-tab-pane label="车载" name="fourth">Task</el-tab-pane>
</el-tabs>
<!-- <el-tree-v2
style="max-width: 600px"
:data="data"
:props="propsData"
:height="600"
/> -->
</div>
<div class="right"></div>
</div>
</el-dialog>
</template>
<script setup>
import { ref, watch, reactive, toRaw, computed, getCurrentInstance, onMounted } from "vue";
const props = defineProps({
modelValue: {
type: Boolean,
default: false
},
titleValue: {
type: String,
default: ''
}
})
// Tabs 组件
const activeName = ref('first')
const handleClick = (tab) => {
console.log(tab)
}
onMounted(() => {
lemon.basedata.fetchSystemOrg({
flat: true,
key_word: ""
}).then(res => {
console.log(res);
})
})
const getJgList = () => {
}
const getKey = (prefix, id) => {
return `${prefix}-${id}`
}
const createData = (
maxDeep,
maxChildren,
minNodesNumber,
deep = 1,
key = 'node'
) => {
let id = 0
return Array.from({ length: minNodesNumber })
.fill(deep)
.map(() => {
const childrenNumber =
deep === maxDeep ? 0 : Math.round(Math.random() * maxChildren)
const nodeKey = getKey(key, ++id)
return {
id: nodeKey,
label: nodeKey,
children: childrenNumber
? createData(maxDeep, maxChildren, childrenNumber, deep + 1, nodeKey)
: undefined,
}
})
}
const propsData = {
value: 'id',
label: 'label',
children: 'children',
}
const data = createData(4, 30, 40)
const emit = defineEmits(['update:modelValue']);
const close = () => {
emit('update:modelValue', false);
}
</script>
<style lang="scss" scoped>
@import "@/assets/css/layout.scss";
@import "@/assets/css/element-plus.scss";
.content {
padding: 0 10px;
height: 60vh;
overflow: auto;
display: flex;
.left {
width: 20%;
height: 100%;
// background-color: #3c8aff;
margin-right: 5px;
}
.right {
flex: 1;
height: 99%;
border: 1px solid #ccc;
}
}
::v-deep .el-tabs__nav-wrap::after {
background-color: #ccc;
}
::v-deep .el-tabs__nav-next,
.el-tabs__nav-prev {
color: #000;
}
::v-deep .el-tabs__item {
color: #000;
}
::v-deep .el-tabs__item.is-active {
color: #3c8aff;
}
</style>

View File

@ -13,11 +13,12 @@
<el-input placeholder="请选择关联线索" @click="chooseVisiblexS = true" readonly v-model="listQuery.glxsmc"></el-input>
</template>
<template #chryList>
<el-input placeholder="请选择参会人员" @click="chooseVisible = true" readonly v-model="listQuery.rymc"></el-input>
<el-input placeholder="请选择参会人员" @click="openMeetings = true" readonly v-model="listQuery.rymc"></el-input>
</template>
</FormMessage>
</div>
</div>
<Personnel v-model="openMeetings" />
<ChooseUser v-model="chooseVisible" :Single="false" @choosedUsers="handleUserSelected" :roleIds="roleIds" />
<Xslist v-model="chooseVisiblexS" @choosed="choosed" :roleIds="roleIdsxs"></Xslist>
</template>
@ -27,6 +28,7 @@ import ChooseUser from "@/components/ChooseList/ChooseUser/index.vue";
import Xslist from '@/components/ChooseList/ChooseXs/index.vue'
import FormMessage from "@/components/aboutTable/FormMessage.vue";
import { qcckGet, qcckPost } from "@/api/qcckApi.js";
import Personnel from '../components/Communications/personnel.vue'
import { ref, defineExpose, reactive, defineEmits, getCurrentInstance, } from "vue";
const emit = defineEmits(["updateDate"]);
@ -109,6 +111,8 @@ const close = () => {
dialogForm.value = false;
loading.value = false;
};
// 选择参会人员
const openMeetings=ref(false)
defineExpose({ init });
</script>

View File

@ -66,6 +66,7 @@
<!-- 详情 -->
<DetailForm ref="detailDiloag" @updateDate="getList" />
<RoomDetail />
<ConferenceRoom v-model="conferenceRoomVisible" titleValue="会议详情" />
</template>
<script setup>
@ -77,7 +78,8 @@
import RoomDetail from "./components/roomDetail.vue";
import { useRoute, useRouter } from 'vue-router';
import { qcckGet, qcckPost, qcckDelete } from "@/api/qcckApi.js";
import { reactive, ref, onMounted, getCurrentInstance } from "vue";
import { reactive, ref, onMounted, getCurrentInstance } from "vue";
import ConferenceRoom from "./components/Communications/conferenceRoom.vue";
const route = useRoute();
const { proxy } = getCurrentInstance();
const detailDiloag = ref();
@ -165,10 +167,11 @@
})
};
// 加入会议
const joinMeeting = (item) => {
qcckPost({id:item.id},'/mosty-gsxt/wshs/addWshyRy').then((res)=>{
getList();
})
const joinMeeting = (item) => {
conferenceRoomVisible.value = true;
// qcckPost({id:item.id},'/mosty-gsxt/wshs/addWshyRy').then((res)=>{
// getList();
// })
};
// 删除
@ -192,7 +195,12 @@ const delDictItem = (id) =>{
window.onresize = function () {
tabHeightFn();
};
};
};
// 会议详情弹窗
const conferenceRoomVisible = ref(false);
</script>
<style lang="scss" scoped>

View File

@ -0,0 +1,714 @@
// import { Notification } from '@arco-design/web-vue';
import { ElNotification, ElMessage } from 'element-plus'
import emitter from "@/utils/eventBus.js";
// import handleCallingNotification from '../hooks/callingNotification';
let call_id;
let hangup_event_cb_id;
let force_hangup_event_cb_id;
let answer_ack_cb_id;
let incoming_cb_id;
let floor_grant_cb_id;
// Whether it is a pushVideoFile call
// 是否为视频推送的呼叫
let pushing_video = false;
// monitor call list
// 值守呼叫列表
const monitor_call_list = [];
export default function useCallModule() {
// we allow only one call exist in demo.but actually,sdk supports more
/**
* make private vioce call,return nothing,receive result in callback
* This interface has been expanded to support makeGroupVoiceCall、makeDispatcherVoiceCall、makeConferenceVoiceCall、makeCrosspatchVoiceCall
* 这个接口已扩展,除了支持语音单呼外,还支持语音组呼、语音呼叫调度员、语音会议、群组呼叫。
* @param record who you wanna call
* @returns
*/
const makeVoiceCall = (record) => {
if (call_id) {
console.warn('we allow only one call exist,please hangup first');
return;
}
window.lemon.call
.makeVoiceCall({ basedata_id: record.basedata_id, hook_flag: 0 })
?.then((resp) => {
call_id = resp.call_id;
console.info('makeVoiceCall succ,registerCallEstablishEvent', call_id);
});
// test
// window.lemon.call.changeCamera({ call_id, target_camera: 0 });
// also can use this makecall method,the method provides the most comprehensive call capability
// For example, Voice private call, ensure that the basedata_id is a private
// window.lemon.call
// .makeCall({
// callee_guid: record.basedata_id,
// attribute: {
// call_type: 0,
// call_mode: 0,
// duplex_flag: 0,
// },
// })
// ?.then((resp) => {
// console.info('makeCall succ,resp', resp);
// call_id = resp.call_id;
// });
// For example, Voice group call,ensure that the basedata_id is a group
// window.lemon.call
// .makeCall({
// callee_guid: record.basedata_id,
// attribute: {
// call_type: 1,
// call_mode: 0,
// duplex_flag: 0, // group call ,the value must be 0
// },
// })
// ?.then((resp) => {
// console.info('makeCall succ,resp', resp);
// call_id = resp.call_id;
// });
};
/**
* make private video call,call with vioce and video,return nothing,receive result in callback
* This interface has been expanded to support makeGroupVideoCall、makeDispatcherVideoCall、makeConferenceVideoCall
* 这个接口已扩展,除了支持视频单呼外,还支持视频组呼、视频呼叫调度员、视频会议
* @param record who you wanna call
* @returns
*/
const makeVideoCall = (record) => {
if (call_id) {
console.warn('we allow only one call exist,please hangup first');
return;
}
window.lemon.call
.makeVideoCall({ basedata_id: record.basedata_id, video_frame_size: 3 })
?.then((resp) => {
call_id = resp.call_id;
console.warn('makeVideoCall succ,call_id=', call_id);
});
// also can use this makecall method,the method provides the most comprehensive call capability
// For example, Video private call (with voice), ensure that the basedata_id is a private
// window.lemon.call
// .makeCall({
// callee_guid: record.basedata_id,
// attribute: {
// call_type: 11,
// call_mode: 2,
// duplex_flag: 1,
// },
// video_frame_size: window.lemon.call.getMaxFrameSize(),
// })
// ?.then((resp) => {
// console.info('makeCall succ,resp', resp);
// call_id = resp.call_id;
// });
// For example, Video group call,ensure that the basedata_id is a group
// window.lemon.call
// .makeCall({
// callee_guid: record.basedata_id,
// attribute: {
// call_type: 13,
// call_mode: 2,
// duplex_flag: 0, // group call ,the value must be 0
// },
// video_frame_size: window.lemon.call.getMaxFrameSize(),
// })
// ?.then((resp) => {
// console.info('makeCall succ,resp', resp);
// call_id = resp.call_id;
// });
};
const makeGroupVideoCall = (record) => {
if (call_id) {
console.warn('we allow only one call exist,please hangup first');
return;
}
window.lemon.call
.makeGroupVideoCall({
basedata_id: record.basedata_id,
video_frame_size: 3,
})
?.then((resp) => {
console.log('makeGroupVideoCall call_id = ', call_id);
call_id = resp.call_id;
});
};
const makeGroupVoiceCall = (record) => {
if (call_id) {
console.warn('we allow only one call exist,please hangup first');
return;
}
window.lemon.call
.makeGroupVoiceCall({
basedata_id: record.basedata_id,
})
?.then((resp) => {
call_id = resp.call_id;
});
};
const makeCrosspatchVoiceCall = (record) => {
window.lemon.call
.makeCrosspatchVoiceCall({ basedata_id: record.basedata_id })
?.then((resp) => {
call_id = resp.call_id;
});
};
/**
* 视频上拉
* make pure video call,only video, no voice
* @param record who you wanna call
* @returns
*/
const pureVideoCall = (record) => {
if (call_id) {
console.warn('we allow only one call exist,please hangup first');
return;
}
window.lemon.call
.pullVideo({ basedata_id: record.basedata_id, hook_flag: 0 })
?.then((resp) => {
call_id = resp.call_id;
});
};
/**
* 环境监听
* @param record
*/
const voiceDetect = (record) => {
window.lemon.call
.makeCall({
callee_guid: record.basedata_id,
attribute: {
call_type: 0,
call_mode: 0,
duplex_flag: 0,
ambience_flag: 1,
},
})
?.then((resp) => {
call_id = resp.call_id;
});
};
/**
* 环境监视
* @param record
*/
const videoDetect = (record) => {
window.lemon.call
.makeCall({
callee_guid: record.basedata_id,
attribute: {
call_type: 11,
call_mode: 2,
duplex_flag: 0,
ambience_flag: 1,
},
})
?.then((resp) => {
call_id = resp.call_id;
});
};
/**
* 音频会议
* @param record
*/
const makeConferenceVoiceCall = (record) => {
window.lemon.call
.makeConferenceVoiceCall({
basedata_id: record.basedata_id,
})
?.then((resp) => {
call_id = resp.call_id;
});
};
/**
* 音视频会议
* @param record
*/
const makeConferenceVideoCall = (record) => {
window.lemon.call
.makeConferenceVideoCall({
basedata_id: record.basedata_id,
})
?.then((resp) => {
call_id = resp.call_id;
});
};
// 广播呼叫
// Broadcast Call
const makeBroadcastCall = (record) => {
window.lemon.call
.makeCall({
callee_guid: record.basedata_id,
attribute: {
call_type: 3,
call_mode: 0,
},
})
?.then((resp) => {
call_id = resp.call_id;
});
};
/**
* hangup call
*/
const hangup = (id) => {
console.info('try hangup,call_id=', call_id);
console.info('try hangup,id=', id);
window.lemon.call
.hangupCall({ call_id: id || call_id })
?.then(() => {
call_id = undefined;
window.calling_conference = undefined;
// console.log('22222registerCallEstablishEven');
})
.catch((err) => {
console.error('hangup call error,', err);
});
};
/**
* 接听呼叫
* answer call
*/
const answerCall = () => {
ElNotification.close(call_id);
window.lemon.call
.answerCall({ call_id })
?.then(() => {
// display alert ui
ElNotification.close(call_id);
})
.catch((err) => {
console.error('answer call error,', err);
});
};
// 禁麦
// mute the microphone
const setMuteMic = (mute) => {
return window.lemon.call.muteMic({
call_id,
is_mute: mute,
});
};
// 禁摄像头
// disable the camera
const setMuteCamera = (mute) => {
return window.lemon.call.muteCamera({
call_id,
is_mute: mute,
});
};
// 静音
// mute call
const setMuteCall = (mute) => {
return window.lemon.call.muteCall({
call_id,
is_mute: mute,
});
};
const removeMonitorCall = (callId) => {
const index = monitor_call_list.findIndex(
(item) => item.call_id === callId
);
if (index >= 0) {
monitor_call_list.splice(index, 1);
emitter.emit('monitorUpdate');
}
};
const getMonitorCallList = () => {
return monitor_call_list;
};
// 添加 挂断 回调
// After hanging up the call, you can receive a notification and process the style of the current page based on the notification
const registerhangupEvent = () => {
if (hangup_event_cb_id !== undefined) {
return;
}
hangup_event_cb_id = window.lemon.call.addHangupEvt((data) => {
console.info('receive hangup event');
console.info(
'receive hangup event receive call id',
String(data.call_id)
);
console.info('receive hangup event local call id', String(call_id));
if (data.call_id === call_id) {
// remove display alert ui
ElNotification.close(call_id);
call_id = undefined;
window.calling_conference = undefined;
// console.log('2222211111registerCallEstablishEvent');
}
// pushVideoFile uninit
// 如果是视频推送的,挂断是反初始化一些东西。
if (pushing_video) {
window.video.src = undefined;
window.showVideo.value = false;
pushing_video = false;
window.videoViewRoot.style.display = 'none';
}
removeMonitorCall(data.call_id);
});
};
// 添加强拆事件
const registerForceHangupEvent = () => {
if (force_hangup_event_cb_id !== undefined) {
return;
}
force_hangup_event_cb_id = window.lemon.call.addForceHangupEvt((data) => {
// 目前强拆成功,都是走挂断通知事件。 当失败了,才走强拆事件通知。
if (data.result === 0) {
if (data.call_id === call_id) {
ElNotification.close(call_id);
call_id = undefined;
window.calling_conference = undefined;
}
removeMonitorCall(data.call_id);
} else {
// 强拆失败 fail
ElMessage.error("强拆失败");
}
});
};
// 添加接听回调通知 代表呼叫建立
// receive the answer acknowledgement asynchronously after the call is established
const registerCallEstablishEvent = () => {
if (answer_ack_cb_id !== undefined) {
return;
}
answer_ack_cb_id = window.lemon.call.addAnswerAckEvt((data) => {
// console.info('registerCallEstablishEvent0', call_id);
console.info('registerCallEstablishEvent', String(data.call_id));
console.info('registerCallEstablishEvent', JSON.stringify(data));
call_id = data.call_id;
// pushVideoFile init
// 接通后,视频推送的文件开始播放
if (pushing_video) {
window.videoViewRoot.style.display = 'unset';
window.showVideo.value = true;
window.video.play();
}
});
};
// 注册来电通知回调
// add the incoming call callback
const registerReceiveCallEvent = () => {
if (incoming_cb_id !== undefined) {
return;
}
incoming_cb_id = window.lemon.call.addIncomingEvt((session) => {
console.log('receive call------>', session);
call_id = session.call_id;
const callingReq = {
callInfo: session,
equipment: undefined,
text: {
calling: '队列',
answer: '接听',
hangUp: '挂断',
},
};
console.log('receive this------>', useCallModule());
if (session.listen_flag) {
// monitor call,auto answer call
// 值守的来电呼叫,自动接听
window.lemon.call.answerCall({ call_id: session.call_id });
monitor_call_list.push(session);
emitter.emit('monitorUpdate');
} else {
// normal call
// 正常来电,弹框询问接听
// handleCallingNotification(callingReq);
}
});
};
// 申请话权
// apply floor
const applyFloor = () => {
console.info('try applyFloor,call_id=', call_id);
if (!call_id) {
console.warn('no call exits');
return undefined;
}
return window.lemon.floor
.applySpeak({ call_id })
?.then((resp) => {
if (resp.result === 0) {
console.log('applyFloor request sucess');
} else {
ElMessage.error("申请话权失败");
}
})
.catch((err) => {
console.error('applyFloor error,', err);
});
};
// 释放话权
// release floor
const releaseFloor = () => {
console.info('try releaseFloor,call_id=', call_id);
return window.lemon.floor
.releaseSpeak({ call_id })
?.then((resp) => {
if (resp.result === 0) {
console.log('releaseSpeak request sucess');
} else {
ElMessage.error("释放话权失败");
}
})
.catch((err) => {
console.error('releaseSpeak error,', err);
});
};
// add floor event
// 添加话权通知事件
const registerFloorGrantEvent = () => {
if (floor_grant_cb_id !== undefined) {
return;
}
floor_grant_cb_id = window.lemon.floor.addGrantEvt((grantInfo) => {
console.log('registerFloorGrantEvent', grantInfo);
if (grantInfo.result !== 0) {
ElMessage.error("话权申请失败");
}
});
};
// 强拆
// override the call
const forceHangupCall = (callId) => {
return window.lemon.call
.forceHangupCall({ call_id: callId || call_id })
?.then((resp) => {
if (resp.result === 0) {
console.log('forceHangupCall request sucess');
} else {
ElMessage.error("强拆失败");
}
call_id = undefined;
window.calling_conference = undefined;
// console.log('2222233registerCallEstablishEven');
})
.catch((err) => {
console.error('forceHangupCall error,', err);
});
};
// 强插
// interrupt the call during monitoring
const forceApplySpeak = (callId) => {
return window.lemon.floor
.forceApplySpeak({ call_id: callId || call_id })
?.then((resp) => {
if (resp.result === 0) {
console.log('forceApplySpeak request sucess');
call_id = callId;
} else {
ElMessage.error("强插失败");
}
})
.catch((err) => {
console.error('forceApplySpeak error,', err);
});
};
/**
* 会议成员禁言
* mute or unmute a conference member
* @param param
* @param param.conference_basedata_id
*/
const muteMemberInConference = (
conference_basedata_id,
member_basedata_id,
mute
) => {
return window.lemon.call.conferenceMemberSpeakSet({
conference_call_id: call_id,
conference_basedata_id,
member_setting: {
member_basedata_id,
forbid: mute ? 1 : 0,
},
});
};
/**
* 视频推送
* select a local video file and play then push the stream to remote
* @param record
* @param file
* @returns
*/
const pushVideoFile = (record, url) => {
console.info('try pushVideoFile,call_id=', call_id);
if (call_id) {
console.warn('call exits');
return;
}
console.warn('window.video', window.video);
// set the file to video tag
if (window.video) {
const canPlayListener = () => {
window.video.removeEventListener('canplay', canPlayListener);
// capture stream
const stream = (window.video)?.captureStream();
console.info('pushVideoFile,inited stream=', stream);
// register hook listner ,play the video when remote hooked
registerCallEstablishEvent();
// init the call
window.lemon.call
.pushVideoFile({
basedata_id: record?.basedata_id,
stream,
})
?.then((resp) => {
pushing_video = true;
call_id = resp.call_id;
});
};
const endListener = () => {
console.info('pushVideoFile,video play end');
window.video.removeEventListener('ended', endListener);
hangup();
};
// maybe did't finish the video,so that we has listener already,remove it
window.video.removeEventListener('ended', endListener);
console.info('pushVideoFile,add canplay Event Listener', canPlayListener);
window.video.addEventListener('canplay', canPlayListener);
// hangup the call when video finished
console.info('pushVideoFile,add ended Event Listener', endListener);
window.video.addEventListener('ended', endListener);
window.video.autoplay = false;
window.video.src = url;
}
};
/**
* 视频转发
* share a exist call to another device
* @param record
* @param stop
* @returns
*/
const forwardVideoCall = (record, stop) => {
console.info('try forwardVideoCall,call_id=', call_id);
if (!call_id) {
console.warn('no call exits,can not forward');
ElMessage.warning('no call exits');
return undefined;
}
return window.lemon.call
.forwardVideoCall({
basedata_id: record?.basedata_id,
call_id,
action: stop ? 1 : 0,
})
?.then((resp) => {
if (resp.result === 0) {
console.log('forwardVideoCall request sucess');
} else {
ElMessage.error("视频转发失败");
}
})
.catch((err) => {
console.error('forwardVideoCall error,', err);
});
};
const getEmitter = () => {
return emitter;
};
// call sdk unInit
const unInit = () => {
call_id = undefined;
if (hangup_event_cb_id) {
window.lemon.call.removeHangupEvt(hangup_event_cb_id);
hangup_event_cb_id = undefined;
}
if (force_hangup_event_cb_id) {
window.lemon.call.removeForceHangupEvt(force_hangup_event_cb_id);
force_hangup_event_cb_id = undefined;
}
if (answer_ack_cb_id) {
window.lemon.call.removeAnswerAckEvt(answer_ack_cb_id);
answer_ack_cb_id = undefined;
}
if (incoming_cb_id) {
window.lemon.call.removeIncomingEvt(incoming_cb_id);
incoming_cb_id = undefined;
}
if (floor_grant_cb_id) {
window.lemon.floor.removeGrantEvt(floor_grant_cb_id);
floor_grant_cb_id = undefined;
}
monitor_call_list.length = 0;
};
return {
makeVoiceCall,
makeVideoCall,
pureVideoCall,
registerhangupEvent,
registerReceiveCallEvent,
answerCall,
makeGroupVoiceCall,
makeGroupVideoCall,
makeBroadcastCall,
hangup,
voiceDetect,
videoDetect,
applyFloor,
releaseFloor,
setMuteCamera,
setMuteMic,
setMuteCall,
muteMemberInConference,
makeConferenceVoiceCall,
makeConferenceVideoCall,
makeCrosspatchVoiceCall,
registerCallEstablishEvent,
pushVideoFile,
forwardVideoCall,
forceHangupCall,
getMonitorCallList,
getEmitter,
forceApplySpeak,
registerFloorGrantEvent,
registerForceHangupEvent,
unInit,
};
}

View File

@ -0,0 +1,37 @@
// import { h } from 'vue';
// import { Notification } from '@arco-design/web-vue';
// import { IconClose } from '@arco-design/web-vue/es/icon';
// import Content from '@/views/webPuc/callingNotification/components/content.vue';
// import Footer from '@/views/webPuc/callingNotification/components/footer.vue';
// import { formatShowNameFin } from './format-show-name';
// export default function handleCallingNotification(propsObj) {
// const { callInfo, text } = propsObj;
// const { calling, answer = 'answer', hangUp = 'hang up' } = text;
// Notification.info({
// id: callInfo.call_id,
// showIcon: false,
// position: 'bottomLeft',
// closable: true,
// closeIconElement: () => {
// return h(IconClose);
// },
// title: () =>
// h(Content, {
// callInfo,
// calling,
// getName: formatShowNameFin(
// callInfo.caller_alias,
// callInfo.caller_number
// ),
// }),
// content: () =>
// h(Footer, {
// callInfo,
// answer,
// hangUp,
// }),
// duration: 0,
// style: { width: '260px' },
// });
// }

View File

@ -18,17 +18,18 @@
<el-option v-for="item in dictItemList" :key="item.dm" :label="item.zdmc" :value="item.dm" />
</el-select>
</template>
</Search>
</div> -->
</Search>
</div> -->
<div style="background-color: #fff;color: black;padding: 15px;overflow: auto;" :style="{ height: tabHeight + 'px' }"
ref="tableBox">
<div style="border-bottom: 1px #ccc solid;padding-bottom: 30px;">
<h1 class="headline">{{ nd }}年度西藏公安战术研判报告</h1>
<div style="display: flex;align-items: center;justify-content: space-between; color: red;margin-top: 30px;padding: 0 30px;font-size: 18px;font-weight: 700;">
<div>{{ deptId.name }}</div>
<div>{{ deptId?.ord }}</div>
<div>{{ deptId?.time }}</div>
</div>
<h1 class="headline">{{ nd }}年度林芝市公安战术研判报告</h1>
<div
style="display: flex;align-items: center;justify-content: space-between; color: red;margin-top: 30px;padding: 0 30px;font-size: 18px;font-weight: 700;">
<div>{{ deptId.name }}</div>
<div>{{ deptId?.ord }}</div>
<div>{{ deptId?.time }}</div>
</div>
</div>
<p>为全面客观准确掌握全区公安机关的执法状况自治区
@ -72,7 +73,7 @@
</span>
</span>
</p>
<Histogram title="地市分布" :xAxisData="dataList.dyTj.xAxisData" :seriesData="dataList.dyTj.seriesData" />
<Histogram title="地市分布" :xAxisData="dataList.dyTj.xAxisData" :seriesData="dataList.dyTj.seriesData" />
<h2>1.1.4时间维度</h2>
<p>
我们将所有警情按照月份划分进行统计发现每月警情分布
@ -103,10 +104,13 @@
:chartData="dataList.CljgfTj" :innerRadius="0" :outerRadius="150" :padAngle="2" />
<h2>1.2.2效率维度</h2>
<p>从处警效率来看{{ sortingRatioValue.Czlfx[1]?.name }}处警的占到{{ sortingRatioValue.Czlfx[0]?.ratio
}}表明我区公安机关在接警之后能够在第一时间处警
}}表明我区公安机关在接警之后能够在第一时间处警
</p>
<MaleNightingalePicture :title="{ text: '效率维度', left: 'center' }" style="height: 550px;"
:chartData="dataList.withinTj" :innerRadius="0" :outerRadius="150" :padAngle="0" />
<h2>2.总结分析</h2>
<p style="min-height: 100px;" v-loading="loading">{{ textContent }} </p>
</div>
</div>
@ -114,14 +118,12 @@
</template>
<script setup>
import PageTitle from "@/components/aboutTable/PageTitle.vue";
import Search from "@/components/aboutTable/Search.vue";
import MaleNightingalePicture from './components/maleNightingalePicture.vue'
import Histogram from './components/histogram.vue'
import { timeValidate } from '@/utils/tools.js'
import { getItem, setItem } from '@/utils/storage'
import { fxbgDywdtj, getDictItem, fxbgJqlxtj, fxbgJqlytj, fxbgYdfx, fxbgXsfx, fxgbCljgf, fxgbCzlfx, fxbgTj } from '@/api/semanticAnalysis'
import { qcckGet, qcckPost } from "@/api/qcckApi.js";
import { completions } from '@/api/semanticAnalysis'
import { reactive, ref, onMounted, getCurrentInstance, nextTick, computed, watch } from "vue";
const props = defineProps({
// 数据
@ -130,6 +132,7 @@ const props = defineProps({
default: null
}
})
const loading = ref(false);
const { proxy } = getCurrentInstance();
const { D_GS_XS_LX } = proxy.$dict("D_GS_XS_LX"); //获取字典数据
const dictItemList = ref([])
@ -214,7 +217,7 @@ const getfxbgDywdtj = () => {
let params = {
...pageData.parameter,
}
fxbgDywdtj(params).then(res => {
return fxbgDywdtj(params).then(res => {
dataList.dyTj.xAxisData = res.map(it => it.ssbm)
dataList.dyTj.seriesData = [];
for (let i = 0; i < res.length; i++) {
@ -242,7 +245,7 @@ const getfxbgJqlxtj = () => {
bjlbList: dictItemList.value.map(item => item.dm).join(',')
}
}
fxbgJqlxtj(params).then(res => {
return fxbgJqlxtj(params).then(res => {
sortingRatioValue.Jqlx = sortingRatio(res)
dataList.jqlxTj = res.map(item => {
return {
@ -257,7 +260,7 @@ const getfxbgJqlytj = () => {
let params = {
...pageData.parameter,
}
fxbgJqlytj(params).then(res => {
return fxbgJqlytj(params).then(res => {
sortingRatioValue.Lyfx = sortingRatio(res)
dataList.jqlyTj = res.map(item => {
return {
@ -273,7 +276,7 @@ const getfxbgYdfx = () => {
let params = {
...pageData.parameter,
}
fxbgYdfx(params).then(res => {
return fxbgYdfx(params).then(res => {
sortingRatioValue.Ydfx = sortingRatio(res)
dataList.ydTj = res.map(item => {
return {
@ -289,7 +292,7 @@ const getfxgbCljgf = () => {
...pageData.parameter,
}
fxgbCljgf(params).then(res => {
return fxgbCljgf(params).then(res => {
sortingRatioValue.Cljgf = sortingRatio(res)
dataList.CljgfTj = res.map(item => {
return {
@ -304,7 +307,7 @@ const getfxgbCzlfx = () => {
let params = {
...pageData.parameter,
}
fxgbCzlfx(params).then(res => {
return fxgbCzlfx(params).then(res => {
dataList.withinTj = [{
name: "超时分流(超过24小时)",
value: res.within24h ? res.within24h : 0
@ -320,7 +323,7 @@ const getfxbgTj = () => {
let params = {
...pageData.parameter,
}
fxbgTj(params).then(res => {
return fxbgTj(params).then(res => {
console.log(res, "xxx");
dataList.XsfxTj = res
})
@ -360,41 +363,46 @@ const Time = () => {
deptId.value.name = devt[0].deptName
nd.value = timeValidate('', 'yd')
deptId.value.time = timeValidate('', 'ydm')
deptId.value.ord=timeValidate('', 'mm')
deptId.value.ord = timeValidate('', 'mm')
TimeValue.value.startTime = `${year}-${month}-${day}`
TimeValue.value.endTime = `${endYear}-${endMonth}-${endDay}`
}
Time()
const funAll = () => {
getfxbgDywdtj()
getfxbgJqlxtj()
getfxbgJqlytj()
getfxgbCljgf()
getfxbgYdfx()
getfxgbCzlfx()
getfxbgTj()
// 使用Promise.all确保所有数据获取函数执行完成
Promise.all([
getfxbgDywdtj(),
getfxbgJqlxtj(),
getfxbgJqlytj(),
getfxgbCljgf(),
getfxbgYdfx(),
getfxgbCzlfx(),
getfxbgTj()
]).then(() => {
extractTextContent()
})
}
const getDictItemList = () => {
const promes = {
startTime: props.search.startTime ,
endTime: props.search.endTime,
startTime: props.search.startTime,
endTime: props.search.endTime,
}
if (props.search.lx) {
const bjlbLists = props.search.lx ? props.search.lx.join(',') : ""
pageData.parameter = { bjlbList: bjlbLists, ...promes }
const bjlbLists = props.search.lx ? props.search.lx.join(',') : ""
pageData.parameter = { bjlbList: bjlbLists, ...promes }
funAll()
} else {
const promesing = {
dictCode: "00000000"
}
dictCode: "00000000"
}
getDictItem(promesing).then(res => {
const bjlbLists = res.map(item=>item.dm).join(',')?res.map(item=>item.dm).join(','):""
pageData.parameter = { bjlbList: bjlbLists, ...promes }
funAll()
})
}
const bjlbLists = res.map(item => item.dm).join(',') ? res.map(item => item.dm).join(',') : ""
pageData.parameter = { bjlbList: bjlbLists, ...promes }
funAll()
})
}
}
@ -404,6 +412,49 @@ watch(() => dictItemList.value, (val) => {
}, { deep: true })
getDictItemList()
const tableBox = ref(null);
const textContent = ref('')
// 获取p标签的所有文字内容去除所有<>包裹的内容
const extractTextContent = () => {
loading.value = true
// 获取当前组件的HTML内容
const componentHtml = tableBox.value ? tableBox.value.innerHTML : '';
// 存储提取的文本内容
const extractedTexts = [];
// 提取p标签的文本内容
const pRegex = /<p[^>]*>(.*?)<\/p>/gs;
let pMatch;
while ((pMatch = pRegex.exec(componentHtml)) !== null) {
let pContent = pMatch[1];
// 去除所有<>包裹的内容
pContent = pContent.replace(/<[^>]*>/g, '');
// 去除HTML注释
pContent = pContent.replace(/<!--[\s\S]*?-->/g, '');
// 清理文本,去除多余空白字符
const cleanText = pContent.replace(/\s+/g, ' ').trim();
if (cleanText) {
extractedTexts.push(cleanText);
}
}
// 合并为一个字符串
const result = extractedTexts.join(' ');
console.log('提取的文本内容:', result);
completions({
model: "deepseek-32b",
prompt: `# 角色定位\n你是一名资深警务人员尤其擅长对警情、案件、线索等非结构化文本数据进行阅读理解并总结各种对象之间的关联关系,对下面数据进行一个分析总结给出一个总结报告。\n${result}`,
max_tokens: 1000,
}).then(res => {
textContent.value = res.choices[0].text
}).finally(() => {
loading.value = false
})
// return result;
};
</script>

View File

@ -602,6 +602,7 @@ const tabHeightFn = () => {
::v-deep .el-table .el-table__cell {
padding: 0;
}
::v-deep .el-table th.el-table__cell>.cell {

View File

@ -37,7 +37,7 @@
<script setup>
import YpModel from '@/components/ypModel/index.vue'
import emitter from "@/utils/eventBus.js";
import { getYjxxPageList,getSsyjpzMxgzxl,mxglYjxqSelectList } from "@/api/model";
import { getYjxxPageList,getSsyjpzMxgzxl,mxglJqxqGetPageList } from "@/api/model";
import PageTitle from "@/components/aboutTable/PageTitle.vue";
import MyTable from "@/components/aboutTable/MyTable.vue";
import Pages from "@/components/aboutTable/Pages.vue";
@ -67,7 +67,7 @@ const SaveReport = () => {
const opneModel = (row) => {
showModel.value = true
textContent.value = ''
mxglYjxqSelectList({
mxglJqxqGetPageList({
yjid: row.id,
}).then((res) => {
let str = {
@ -75,13 +75,13 @@ const opneModel = (row) => {
prompt: `# 角色定位\n你是一名资深警务人员尤其擅长对警情、案件、线索等非结构化文本数据进行阅读理解并从中提取各种对象特征信息进行结构化并总结各种对象之间的关联关系。\n`,
max_tokens: 1000,
}
res.forEach(item => {
const lx = props.dict.D_BZ_YJLX.find(items => {
res.forEach(item => {
const lx = props.dict.D_BZ_JQLY.find(items => {
return items.value == item.yjlx
}).zdmc
str.prompt+=`预警名称:${item.yjBt},警人姓名:${item.yjRyxm},身份证号:${item.yjRysfzh},车牌号:${item.yjClcph},警地址:${item.yjDz},警类型:${lx},预警内容:${item.yjnr},预警时间:${item.yjsj}\n`
str.prompt+=`警情内容:${item.bjnr},警人姓名:${item.bjrmc},警地址:${item.bjdz},警类型:${lx},警时间:${item.bjsj}\n`
})
str.prompt += `根据以上警信息,总结出一个解决方案`
str.prompt += `根据以上警信息,总结出一个解决方案`
let jsonMatch
completions(str).then(reslve => {
jsonMatch = reslve.choices[0].text
@ -90,7 +90,7 @@ const opneModel = (row) => {
time: timeValidate()
}) + fbtool(`同类事件发生${res.length}次以上预警`)
res.forEach((item,index) => {
const lx = props.dict.D_BZ_YJLX.find((items) => {
const lx = props.dict.D_BZ_JQLY.find((items) => {
return items.value == item.yjlx
}).zdmc
textContent.value +=Subheading(`${index+1}.警情内容`)+textStyle(`预警名称:${item.yjBt},预警人员姓名:${item.yjRyxm},身份证号:${item.yjRysfzh},车牌号:${item.yjClcph},预警地址:${item.yjDz},预警类型:${lx},预警内容:${item.yjnr},预警时间:${item.yjsj}\n`)

View File

@ -30,7 +30,8 @@ const props = defineProps({
item: {
type: Object,
default: () => {},
}
},
})
const { proxy } = getCurrentInstance();
const { D_BB_AJLB,D_BZ_WPLX,D_BZ_RYBQ} = proxy.$dict("D_BB_AJLB","D_BZ_WPLX","D_BZ_RYBQ")

View File

@ -1,8 +1,9 @@
<template>
<div>
<YpHome v-if="showModel == '研判首页'"></YpHome>
<List v-if="showModel == '四色预警规则' || showModel == '预警规则'" :item="itemData"></List>
<YjDetail v-if="showModel == '预警列表'" :item="itemData" :dict="{ D_BZ_TPYJLX,D_BZ_YJLX }"></YjDetail>
<List v-if="showModel == '四色预警规则' || showModel == '预警规则'" :item="itemData" :dict="{ D_BZ_TPYJLX}"></List>
<YjDetail v-if="showModel == '预警列表'" :item="itemData" :dict="{ D_BZ_TPYJLX,D_BZ_YJLX,D_BZ_JQLY }"></YjDetail>
<WarningList v-if="showModel == '预警详情'" :item="itemData" :dict="{ D_BZ_YJLX }"/>
</div>
</template>
@ -14,7 +15,7 @@ import WarningList from "./components/AddModel/warningList.vue"
const { proxy } = getCurrentInstance();
import emitter from "@/utils/eventBus.js";
import { onMounted, ref, getCurrentInstance } from "vue";
const { D_BZ_TPYJLX,D_BZ_YJLX } = proxy.$dict("D_BZ_TPYJLX","D_BZ_YJLX")
const { D_BZ_TPYJLX,D_BZ_YJLX ,D_BZ_JQLY} = proxy.$dict("D_BZ_TPYJLX","D_BZ_YJLX","D_BZ_JQLY")
const showModel = ref('研判首页')
const itemData = ref({})