Files
ba_web/src/views/homeMy/components/dialog/MessageLoad.vue

874 lines
22 KiB
Vue
Raw Normal View History

2025-09-22 09:01:41 +08:00
<!--
* @Author: 孙总
* @Date: 2022-10-17 15:42:41
* @LastEditTime: 2022-12-26 19:01:06
* @LastEditors: Please set LastEditors
* @Description: In User Settings Edit
-->
<template>
<!-- @click.stop="closed" -->
<div class="message" >
<div class="message_main" @click.stop>
<div class="message_title">
<span>指令详情</span>
<div class="foots">
<el-popover trigger="click" :width="320" placement="right" :visible="showVisible">
<template #reference>
<span @click="handleChoose" class="btnPromotion"><el-icon :size="16"><Promotion /></el-icon></span>
</template>
<MOSTY.Department width="100%" clearable multiple filterable v-model="zxrDtoList"/>
<div class="btnss" @click="handleDepment">确定</div>
</el-popover>
<Close @click="closed" style="width: 1.2em; height: 1.2em; cursor: pointer"/>
</div>
</div>
<div class="message_content">
<div class="zl_title">
{{ props.data.zlbt }}
</div>
<div class="zl_info">
<img v-if="props.data.zltp" :src="props.data.zltp" alt="" />
<div class="info_r">
<div class="r_title">
<span>{{ props.data.zlfqr }}</span>
<span v-if="props.data.zlfqr !== '系统'">{{ props.data.zlfqrSfzh.substr(props.data.zlfqrSfzh.length - 2,1) %2 ===0? "": ""}}</span>
<span class="block" :style="{color:props.data.zldj == 10? 'red' : props.data.zldj == 20? '#da5724': props.data.zldj == 30? 'yellow': 'blue'}">
指令等级<dict-tag :options="D_BZ_TYJB" :value="props.data.zldj" :tag="false"/>>
</span>
<span class="qszt">
<dict-tag :options="D_BZ_ZXZTAI" :value="props.data.zlzxzt" :tag="false"/>
</span>
</div>
<div v-if="props.data.zlfqr !== '系统'"> 汉族 44岁 51033656582252258 &emsp; 指令类型人员预警 </div>
<div class="block">
指令来源<dict-tag :options="D_BZ_ZLLY" :value="props.data.zlly" :tag="false" />
位置{{ props.data.zlfsdd }}
</div>
</div>
</div>
<div class="zl_nr">
内容{{ props.data.zlnr }}
<span>附近监控</span>
</div>
</div>
<div class="message_list" ref="scroll">
<div v-for="item in list" :key="item.id">
<template v-if="item.xtCjrId == userId">
<p style="text-align: right; color: #82878f"> {{ item.zxsj }} </p>
<template v-if="item.fjid">
<p v-if="item.fileFormat == 'png'" style="text-align: right">
<el-image @click="setIndex(baseUrl + item.fjid)"
style="width: 100px; height: 100px"
:src="baseUrl + item.fjid"
:preview-src-list="srcList"
:initial-index="srcIndex"
fit="cover"
preview-teleported
/>
</p>
<div class="audio-detail-msg msgRight_box" v-if="item.fileFormat == 'mp3'">
<div class="duration-seconds"> {{ item.yysc ? item.yysc : 1 }}s </div>
<div
class="audio-style msgRight"
style="margin-left: 8px"
:class="{ 'add-animation': isPlay && audioId == item.fjid }"
:style="{width: handleAudioStyleWidth(item.yysc ? item.yysc : 1)}"
@click=" playAudio( item.fjid, item.yysc ? item.yysc : 1, item.audioUrl)">
<div class="small"></div>
<div class="middle"></div>
<div class="large"></div>
</div>
<audio :id="item.fjid" style="display: none"></audio>
</div>
<div style="display: flex; position: relative" class="msgRight_box" v-if="item.fileFormat == 'mp4'">
<video :id="item.id" :src="item.videoUrl" style="width: 100px"></video>
<img class="rigth_video_play" src="@/assets/play.png" alt="" @click="onClickVideo(item.videoUrl)"/>
</div>
<div style="display: flex; justify-content: flex-end" v-if="item.fileFormat == 'file'">
<p class="wenjian" @click="downFile(item.fjid)"> {{ item.zxnr }} </p>
</div>
</template>
<div v-else style="text-align: right">
<div class="xx_item">
<span class="xx"> {{ item.zxnr }} </span>
</div>
</div>
</template>
<template v-else>
<p style="color: #82878f"> {{ item.zxsj }}&emsp;{{ item.zlzxrXm }} </p>
<template v-if="item.fjid">
<p v-if="item.fileFormat == 'png'">
<el-image @click="setIndex(baseUrl + item.fjid)"
style="width: 100px; height: 100px"
:src="baseUrl + item.fjid"
:preview-src-list="srcList"
:initial-index="srcIndex"
preview-teleported
fit="cover"
/>
</p>
<div class="audio-detail-msg" v-if="item.fileFormat == 'mp3'">
<div
class="audio-style"
style="margin-right: 8px"
:class="{ 'add-animation': isPlay && audioId == item.fjid }"
:style="{width: handleAudioStyleWidth(item.yysc ? item.yysc : 1)}"
@click=" playAudio( item.fjid, item.yysc ? item.yysc : 1, item.audioUrl)">
<div class="small"></div>
<div class="middle"></div>
<div class="large"></div>
</div>
<div class="duration-seconds">
{{ item.yysc ? item.yysc : 1 }}s
</div>
<audio :id="item.fjid" style="display: none"></audio>
</div>
<div style="display: flex; position: relative" v-if="item.fileFormat == 'mp4'">
<video :id="item.id" :src="item.videoUrl" style="width: 100px"></video>
<img class="left_video_play" src="@/assets/play.png" alt="" @click="onClickVideo(item.videoUrl)"/>
</div>
<div style="display: flex; justify-content: flex-end" v-if="item.fileFormat == 'file'">
<p class="wenjian" @click="downFile(item.fjid)">{{ item.zxnr }}</p>
</div>
<div v-if="item.fileFormat == 'file'" style="text-align: left">
<div class="xx_item">
<span @click="downFile(item.fjid)" class="wenjian_l">{{ item.zxnr }}</span>
</div>
</div>
</template>
<div v-else>
<div class="xx_item">
<span class="xx_l">{{ item.zxnr }}</span>
</div>
</div>
</template>
</div>
</div>
<div class="message_form">
<input v-model="value" placeholder="请输入你描述的内容" @keyup.enter="submit" style="width:100%;"/>
<el-upload
v-model:file-list="fileList"
:limit="1"
2025-09-26 12:56:52 +08:00
action="/bagl/mosty-base/minio/image/upload/id"
2025-09-22 09:01:41 +08:00
:on-change="upImgFile"
:on-success="upImg"
:show-file-list="false">
<CirclePlus color="#4e6e95" class="plus" style="width: 1.5em; height: 1.5em" />
</el-upload>
<el-button @click="submit">发送</el-button>
</div>
</div>
<div v-if="dialogShowVideo" class="dialogShowVideo" @click.stop="dialogShowVideo = false">
<video :src="langVideoUrl" class="video_box" controls></video>
<el-icon :size="40" color="#999" class="video_close"><CircleClose /> </el-icon>
</div>
<MjLoad v-model="visible" @choosedUsers="hanlderChooseMj" />
<XzLoad v-model="visibleJz" :Single='false' @choosedUsers="hanlderChooseJZ" />
</div>
</template>
<script setup>
import axios from "axios";
import { qcckGet, qcckPost } from "@/api/qcckApi.js";
import MjLoad from "@/components/MyComponents/ChooseJz/MjLoad.vue";
import XzLoad from "@/components/MyComponents/ChooseJz/xzLoad.vue";
import * as MOSTY from "@/components/MyComponents/index";
import { reactive, ref, defineProps, defineEmits, getCurrentInstance, onMounted, nextTick, onUnmounted, onBeforeUnmount } from "vue";
import { getZxjlList, addZxjl, downFiles } from "@/api/instructCenter.js";
import { IS_PNG, IS_MP3, IS_MP4 } from "@/utils/tools.js";
import { getItem } from "@/utils/storage.js";
import { Emitter } from "@fullcalendar/core";
const props = defineProps({
modelValue: {
type: Boolean,
required: true
},
data: {
type: Object,
required: true
}
});
const showVisible = ref(false);
const visible = ref(false);
const visibleJz = ref(false);
const zxrDtoList = ref([])
2025-09-26 12:56:52 +08:00
const baseUrl = '/bagl/mosty-base/minio/image/download/'
2025-09-22 09:01:41 +08:00
const fileList = ref([]);
const imgLx = ["png", "jpg", "jpeg", "bmp", "gif"];//图片格式
//音频格式
const radioFormat = ["wav"];
const srcIndex = ref(0);
const { proxy } = getCurrentInstance();
const { D_BZ_TYJB, D_BZ_ZLLY, D_BZ_ZLLX, D_BZ_ZXZTAI } = proxy.$dict("D_BZ_TYJB", "D_BZ_ZLLY", "D_BZ_ZLLX", "D_BZ_ZXZTAI");
const userId = getItem("USERID");
const copyVal = ref("");
const list = ref([]);//消息列表
const dialogShowVideo = ref(false); //视频放大
const langVideoUrl = ref(""); //预览视频地址
const srcList = ref([]);//图片预览列表
const timer = ref(null);
const scroll = ref(null);
const isPlay = ref(false); //开始语音播放动画
const playAudioTimer = ref(null); //语音播放定时器
const audioId = ref(""); //被选中播放的音频ID
const value = ref("");
const fjId = ref(false);
const emits = defineEmits(["update:modelValue", "upData"]);
const closed = () => {
emits("update:modelValue", false);
};
const handleChoose = () =>{
switch(props.data.zljsdx){
case '01'://人员
visible.value = true;
break;
case '02'://部门
showVisible.value = !showVisible.value;
break;
case '03'://巡组
visibleJz.value = true;
break;
}
}
//选择警组
function hanlderChooseJZ(arr) {
let list = arr.map((item) => {
return {
zxrXzid: item.id,
zxrLx: "03",
zxrXzmc: item.jzMc
};
});
addXfzl(list)
}
//选择民警
function hanlderChooseMj(arr) {
let list = arr.map((item) => {
return {
zxrDh: item.lxdh,
zxrId: item.ryid,
zxrJllx: item.fl,
zxrLx: "01",
zxrSfz: item.sfzh,
zxrXm: item.xm
};
});
addXfzl(list)
}
//选择部门
function handleDepment() {
let list = zxrDtoList.value.map(item=>{
return {
ssbmid: item,
zxrLx: "02"
}
})
addXfzl(list)
}
// 新增下发指令
function addXfzl(val) {
let params = { zlId:props.data.id, zxrDtoList:val}
qcckPost(params,'/mosty-jmxf/tbZl/addZlZxr').then(res=>{
zxrDtoList.value = []
proxy.$message({ type: "success", message: "指令下发成功" });
})
}
//点击视频放大
function onClickVideo(url) {
langVideoUrl.value = url;
dialogShowVideo.value = true;
}
//播放语音
function playAudio(id, yysc, url) {
let au = document.getElementById(id);
au.src = url;
audioId.value = id;
if (isPlay.value) {
isPlay.value = false;
au.pause();
return;
}
isPlay.value = true;
playAudioTimer.value = setTimeout(() => {
isPlay.value = false;
}, parseInt(yysc * 1000));
au.play();
}
// 设置语音条宽度样式
function handleAudioStyleWidth(yycs) {
if (yycs === 1) {
return "38px";
} else if (yycs > 1 && yycs < 20) {
return `${38 + (yycs / 10) * 36}px`;
} else if (yycs >= 20) {
return `${106.39 + (yycs / 10) * 18.935}px`;
}
}
//获取消息记录
function getZxjl() {
let params = {zlid: props.data.id,time: list.value.length > 0 ? list.value[list.value.length - 1].zxsj : ""}
getZxjlList(params).then(async (res) => {
if (res && res.length > 0) {
for (let i = 0; i < res.length; i++) {
let item = res[i];
if (item.zxnr && item.fjid) {
//判断是那种文件类型.
let fjIndex = item.zxnr.lastIndexOf(".");
if (fjIndex != -1) {
let file_Format = item.zxnr.substring(
fjIndex + 1,
item.zxnr.length
);
if (IS_PNG(file_Format)) {
item.fileFormat = "png";
} else if (IS_MP3(file_Format)) {
await _getAudioAndVideoUrl(item.fjid).then((res) => {
item.audioUrl = res;
});
item.fileFormat = "mp3";
} else if (IS_MP4(file_Format)) {
item.fileFormat = "mp4";
await _getAudioAndVideoUrl(item.fjid).then((res) => {
item.videoUrl = res;
});
} else {
item.fileFormat = "file";
}
}
}
list.value.push(item);
}
srcList.value = list.value.filter((item) => isImg(item.zxnr) && item.fjid).map((item) => baseUrl + item.fjid);
nextTick(() => {
try {
scroll.value.scrollTop = scroll.value.scrollHeight;
} catch (error) {
return;
}
});
}
});
}
//获取视频音频地址
function _getAudioAndVideoUrl(fjid) {
return new Promise((ok) => {
2025-09-26 12:56:52 +08:00
axios.get(`/bagl/mosty-base/minio/file/download/${fjid}`, {params: {}}).then((res) => {
2025-09-22 09:01:41 +08:00
if (res) ok(res.data.data.url);
});
});
}
function setIndex(e) {
srcIndex.value = srcList.value.indexOf(e);
}
//判断是否为图片
function isImg(fileName) {
let suffix = fileName.substr(fileName.lastIndexOf(".") + 1, 4);
return imgLx.includes(suffix);
}
//下载文件
function downFile(id) {
let el = document.createElement("a");
el.href = baseUrl+`${id}`;
el.click();
el.remove();
}
//发送消息
function submit() {
if (!value.value) return false;
const data = {
zxnr: value.value,
jd: props.data.jd,
wd: props.data.wd,
zlId: props.data.id
};
value.value = "";
addZxjl(data).then(() => {
fileList.value = [];
});
}
function upImg(row) {
fjId.value = row.data;
const data = {
zxnr: copyVal.value,
jd: props.data.jd,
wd: props.data.wd,
zlId: props.data.id,
fjid: fjId.value
};
addZxjl(data).then(() => {
fileList.value = [];
fjId.value = false;
});
}
function upImgFile(row) {
copyVal.value = row.name;
}
onMounted(() => {
getZxjl();
timer.value = setInterval(() => {
getZxjl();//轮询
}, 2e3);
});
onUnmounted(() => {
clearInterval(timer.value);
});
onBeforeUnmount(() => {
clearTimeout(playAudioTimer.value);
playAudioTimer.value = null;
});
</script>
<style lang="scss" scoped>
p {
margin: 0;
padding: 0;
}
.message {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
background: rgba(0, 0, 0, 0.4);
display: flex;
justify-content: center;
align-items: center;
z-index: 999;
.message_main {
width: 820px;
background: #091e33;
border-radius: 8px;
overflow: hidden;
min-height: 650px;
.message_title {
display: flex;
justify-content: space-between;
padding: 6px 16px;
background: #14427e;
font-size: 18px;
}
.message_content {
background: #052342;
border-bottom: 2px solid #193b62;
.zl_title {
font-size: 16px;
line-height: 1.5em;
padding: 6px 12px 0;
}
.zl_nr {
color: #56759c;
line-height: 1.8em;
position: relative;
border-top: 1px solid #193b62;
padding: 4px 0;
padding-right: 55px;
margin: 0 12px;
> span {
position: absolute;
text-decoration: underline;
cursor: pointer;
bottom: 4px;
right: 0;
color: #027ce6;
}
}
.zl_info {
display: flex;
padding: 6px 12px;
color: #5a79a0;
> img {
width: 60px;
height: 60px;
margin-right: 12px;
}
.block div {
display: inline-block;
margin-right: 16px;
}
.info_r {
flex: 1;
line-height: 1.9em;
.r_title {
color: #fff;
font-size: 18px;
position: relative;
font-weight: 300;
span {
margin-right: 18px;
}
.qszt {
position: absolute;
right: 0px;
top: 0;
color: #027ce6;
font-size: 14px;
margin: 0;
}
}
.zxdt {
color: #027ce6;
}
}
}
}
.message_list {
height: 400px;
background: #091e33;
padding: 0 8px;
margin: 4px;
overflow: auto;
p {
line-height: 2em;
}
.xx_item {
text-align: right;
background: #12294c;
max-width: 500px;
width: auto;
display: inline-block;
padding: 4px 12px;
word-wrap: break-word;
border-radius: 4px;
}
.wenjian {
display: inline-block;
background: #12294c;
color: #00a0e0;
line-height: 2em;
padding: 0 12px;
border-radius: 4px;
cursor: pointer;
text-decoration: underline;
}
.wenjian_l {
display: inline-block;
background: #12294c;
color: #00a0e0;
line-height: 2em;
padding: 0 12px;
border-radius: 4px;
cursor: pointer;
text-decoration: underline;
}
.radio {
background: #052342;
display: inline-block;
padding: 4px 12px;
border-radius: 4px;
.sc {
display: inline-block;
padding-left: 12px;
}
}
}
.message_form {
background: #052342;
padding: 8px;
display: flex;
position: relative;
.el-button {
margin-left: 12px;
box-shadow: inset 0px 0px 8px 4px #0e3054;
}
.el-button:hover {
background: #0c376e;
}
.el-button:focus {
background: #0c376e;
border-color: #0c376e;
}
.plus {
position: absolute;
right: 83px;
top: 14px;
}
input {
outline: none;
flex: 1;
color: #fff;
background: #0d2a48;
border: none;
border-radius: 2px;
text-indent: 8px;
padding-right: 36px;
}
}
}
}
// 语音条
.audio-detail-msg {
display: flex;
align-items: center;
.msgRight {
transform: rotate(180deg);
}
.audio-style {
display: flex;
align-items: center;
height: 32px;
padding: 0 10px;
border-radius: 4px;
background: rgba(149, 236, 105, 0.5);
.small {
border: 4px solid #fff;
border-top-color: transparent;
border-left-color: transparent;
border-bottom-color: transparent;
}
.middle {
width: 16px;
height: 16px;
margin-left: -11px;
opacity: 1;
}
.large {
width: 24px;
height: 24px;
margin-left: -19px;
opacity: 1;
}
& > div {
border: 2px solid #fff;
border-top-color: transparent;
border-left-color: transparent;
border-bottom-color: transparent;
border-radius: 50%;
box-sizing: border-box;
}
&.add-animation {
.middle {
animation: show2 1.2s ease-in-out infinite;
}
.large {
animation: show3 1.2s ease-in-out infinite;
}
}
}
// 语音播放动画
@keyframes show2 {
0% {
opacity: 0;
}
10% {
opacity: 1;
}
100% {
opacity: 0;
}
}
@keyframes show3 {
0% {
opacity: 0;
}
50% {
opacity: 1;
}
60% {
opacity: 0;
}
100% {
opacity: 0;
}
}
// 语音录制动画
@keyframes backgroundInfinite2 {
0% {
background: #666;
}
20% {
background: #f5f5f5;
}
95% {
background: #f5f5f5;
}
100% {
background: #666;
}
}
@keyframes backgroundInfinite3 {
0% {
background: #666;
}
30% {
background: #f5f5f5;
}
85% {
background: #f5f5f5;
}
100% {
background: #666;
}
}
@keyframes backgroundInfinite4 {
0% {
background: #666;
}
55% {
background: #f5f5f5;
}
75% {
background: #f5f5f5;
}
100% {
background: #666;
}
}
@keyframes backgroundInfinite5 {
0% {
background: #666;
}
45% {
background: #666;
}
60% {
background: #f5f5f5;
}
75% {
background: #f5f5f5;
}
100% {
background: #666;
}
}
@keyframes backgroundInfinite6 {
0% {
background: #666;
}
65% {
background: #666;
}
85% {
background: #f5f5f5;
}
100% {
background: #666;
}
}
@keyframes backgroundInfinite7 {
0% {
background: #666;
}
75% {
background: #666;
}
95% {
background: #f5f5f5;
}
100% {
background: #666;
}
}
}
.msgRight_box {
justify-content: flex-end;
}
.rigth_video_play {
position: absolute;
width: 40px;
height: 40px;
top: 50%;
right: 50px;
margin-top: -20px;
margin-right: -20px;
}
.left_video_play {
position: absolute;
width: 40px;
height: 40px;
top: 50%;
left: 50px;
margin-top: -20px;
margin-left: -20px;
}
.dialogShowVideo {
position: fixed;
left: 0;
right: 0;
top: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.9);
padding: 80px 0;
text-align: center;
.video_box {
height: 100%;
}
.video_close {
position: absolute;
top: 80px;
right: 50px;
}
}
.foots{
position: relative;
.btnPromotion{
position: absolute;
color: #5593d4;
top: 41px;
right: -1px;
}
}
.btnss{
text-align: center;
margin-top: 4px;
background: #234687;
color: #fff;
border-radius: 4px;
border: 1px solid #2868ab;
padding: 4px 0;
box-sizing: border-box;
cursor: pointer;
}
</style>