Files
ba_web/src/views/homeMy/components/dialog/MessageLoad.vue
2025-09-26 12:56:52 +08:00

874 lines
22 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!--
* @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"
action="/bagl/mosty-base/minio/image/upload/id"
: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([])
const baseUrl = '/bagl/mosty-base/minio/image/download/'
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) => {
axios.get(`/bagl/mosty-base/minio/file/download/${fjid}`, {params: {}}).then((res) => {
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>