Files
APP/pages/6170/callPolice/index.vue
2025-08-16 17:37:24 +08:00

494 lines
11 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.

<template>
<view class="container">
<!-- 设备列表 -->
<view class="allSelect" @click="selectAll"> 全选</view>
<scroll-view class="device-list" scroll-y>
<view class="device-card" v-for="(item, index) in deviceList" :key="index"
@click="item.onlineStatus === 1 ? toggleSelect(index) : null">
<!-- 复选框 -->
<view class="checkbox" :class="{ checked: item.checked, disabled: item.onlineStatus !== 1 }">
<uni-icons v-if="item.checked" type="checkmarkempty" size="18" color="rgb(0, 0, 0)"></uni-icons>
</view>
<!-- 设备信息 -->
<view class="device-content">
<view class="device-header">
<view class="deviceIMG">
<image :src="item.devicePic" class="IMG" mode="aspectFit"></image>
</view>
<view class="device-name">
<view>设备:{{ item.deviceName }}</view>
<view class="ID">
<view class="ID">ID:{{ item.deviceImei }}</view>
<view class="onlines" v-if="item.onlineStatus == 1">在线</view>
<!-- 离线状态 -->
<view class="unlines" v-if="item.onlineStatus == 0">离线</view>
<view>电量{{ item.battery || '0' }}%</view>
</view>
</view>
</view>
</view>
</view>
<view class="editInfmation">
<button class="login-btn qz" @click="forceAlarm">强制报警</button>
<button class="login-btn jc" @click="cancelAlarm">解除报警</button>
</view>
</scroll-view>
<!-- 强制报警提示弹框 -->
<CustomPopup v-if="popupType === 'force'" :show="showPopupFlag" popupBorder="1rpx solid rgba(224, 52, 52, 0.3)"
:message="popupMessage" icon="/static/images/6170/bj.png" :confirm-text="popupConfirmText"
:show-cancel="false" @close="onclosePopup" @confirm="onPopupConfirm" confirmBtnBg="rgba(224, 52, 52, 1)"
confirmBtnColor="#fff" />
<!-- 解除报警 -->
<CustomPopup v-if="popupType === 'cancel'" :show="showPopupFlag" popupBorder="1rpx solid rgba(224, 52, 52, 0.3)"
:message="popupMessage" icon="/static/images/6170/svg.png" :confirm-text="popupConfirmText"
:show-cancel="false" @close="onclosePopup" @confirm="onPopupConfirm" confirmBtnBg="rgba(224, 52, 52, 1)"
confirmBtnColor="#fff" />
</view>
</template>
<script>
import CustomPopup from '@/components/CustomPopup/CustomPopup.vue'
import {
generateShortId
} from '@/utils/function.js';
import {
deviceInfo,
} from '@/api/common/index.js'
import {
deviceSendAlarmMessage
} from '@/api/6170/callPolice.js'
import {
deviceRealTimeStatus //设备状态
} from '@/api/6170/deviceControl.js'
export default {
components: {
CustomPopup
},
data() {
return {
deviceList: [],
showPopupFlag: false,
popupTitle: '',
popupMessage: '确认要对所选设备开启强制报警?',
popupConfirmText: '确认',
popupType: 'force', // 'force' or 'cancel'
pendingAlarmAction: '',
sendInfo: ''
}
},
computed: {
selectedDeviceIds() {
return this.deviceList.filter(item => item.checked).map(item => item.id);
}
},
methods: {
onclosePopup() {
this.showPopupFlag = false
},
toggleSelect(index) {
this.deviceList[index].checked = !this.deviceList[index].checked
this.$forceUpdate()
},
/**
* 获取设备状态(带自动轮询)
* @param {number} val - 功能模式
* @param {string} batchId - 批次ID
* @param {number} [interval=1000] - 轮询间隔(毫秒)
*/
async getdeviceSTatus(val, batchId, interval =500) {
const checkStatus = async () => {
try {
const data = {
functionMode: val,
batchId: batchId,
typeName: this.sendInfo.typeName,
deviceImei: this.sendInfo.deviceImei
};
const res = await deviceRealTimeStatus(data);
if (res.code !== 200) {
throw new Error(res.msg || '请求失败');
}
switch (res.data.functionAccess) {
case 'OK':
return res; // 成功完成
case 'ACTIVE':
await new Promise(r => setTimeout(r, interval));
return checkStatus(); // 继续轮询
case 'FAILED':
throw new Error('设备操作失败');
case 'TIMEOUT':
throw new Error('设备响应超时');
default:
throw new Error('未知状态');
}
} catch (error) {
throw error;
}
};
return checkStatus();
},
// 全选/取消全选
selectAll() {
console.log('123');
const allSelected = this.deviceList.every(item => item.checked);
this.deviceList.forEach(item => {
item.checked = !allSelected;
});
this.$forceUpdate();
},
// 获取设备列表
getData(deviceType) {
this.loading = true;
let data = {
pageNum: 1,
pageSize: 50,
deviceType: deviceType
}
deviceInfo(data).then((res) => {
if (res.code == 200) {
const newDevices = res.rows.map(device => ({
...device,
showConfirm: false,
checked: false
}));
this.total = res.total;
this.deviceList = newDevices
}
}).finally(() => {
this.loading = false;
});
},
// 强制报警
forceAlarm() {
const selectedDevices = this.deviceList.filter(item => item.checked)
if (selectedDevices.length === 0) {
uni.showToast({
title: '请选择一个设备',
icon: 'none'
});
return;
}
this.popupType = 'force';
this.popupMessage = '确认要对所选设备开启强制报警?';
this.showPopupFlag = true;
this.pendingAlarmAction = 1
},
// 解除报警
cancelAlarm() {
const selectedDevices = this.deviceList.filter(item => item.checked);
if (selectedDevices.length === 0) {
uni.showToast({
title: '请选择一个设备',
icon: 'none'
});
return;
}
this.popupType = 'cancel';
this.popupMessage = '确认要解除所选设备的报警状态?';
this.showPopupFlag = true;
this.pendingAlarmAction = 0
},
// 确认
async sendAlarmCommand() {
const selectedDevices = this.deviceList.filter(item => item.checked);
const deviceIds = selectedDevices.map(item => item.id);
const deviceImeiList = selectedDevices.map(item => item.deviceImei);
const isAlarming = this.pendingAlarmAction == 1;
try {
uni.showLoading({
title: isAlarming ? '设备报警中...' : '解除报警中...',
mask: true
});
// 2. 准备请求数据
const batchId = generateShortId();
const data = {
deviceIds: deviceIds,
typeName: this.sendInfo.typeName,
deviceImeiList: deviceImeiList,
batchId: batchId,
instructValue: this.pendingAlarmAction == 1 ? '1' : '0'
};
const registerRes = await deviceSendAlarmMessage(data);
if (registerRes.code !== 200) {
uni.showToast({
title: registerRes.msg,
icon: 'none'
})
return
}
// 4. 获取设备状态
const statusRes = await this.getdeviceSTatus(2, batchId);
if (statusRes.data.functionAccess === 'OK') {
uni.showToast({
title: statusRes.msg,
icon: 'none'
});
this.showPopupFlag = false
uni.$emit('deviceStatusUpdate', {});
setTimeout(() => {
uni.navigateBack()
}, 500)
}
} catch (error) {
uni.showToast({
title: error.message,
icon: 'none'
});
} finally {
uni.hideLoading();
}
},
// 点击确认状态
onPopupConfirm() {
this.sendAlarmCommand(this.popupType);
// 处理确认逻辑
},
},
onLoad(options) {
const eventChannel = this.getOpenerEventChannel();
// 监听 'deviceSend' 事件,获取传过来的数据
eventChannel.on('devicePolice', (data) => {
this.getData(data.data.id)
this.sendInfo = data.data
});
// 如果需要向调用页面返回数据,可以触发 'ack' 事件
eventChannel.emit('ack', {})
},
}
</script>
<style scoped>
.container {
min-height: 100vh;
background-color: rgb(18, 18, 18);
box-sizing: border-box;
overflow-x: hidden;
}
.allSelect {
color: rgba(255, 255, 255, 0.87);
float: right;
padding: 25rpx;
cursor: pointer;
}
.device-list {
flex: 1;
padding: 0rpx 20rpx;
clear: both;
}
.device-card {
position: relative;
display: flex;
align-items: center;
width: 95%;
margin-bottom: 20rpx;
}
.checkbox {
width: 40rpx;
height: 40rpx;
border: 2rpx solid rgba(255, 255, 255, 0.5);
margin-right: 20rpx;
border-radius: 4rpx;
display: flex;
align-items: center;
justify-content: center;
}
.checkbox.checked {
background: rgba(224, 52, 52, 1);
border-color: rgba(224, 52, 52, 1);
}
.device-content {
background-color: rgb(26, 26, 26);
border-radius: 16rpx;
position: relative;
/* display: flex; */
align-items: center;
padding: 20rpx;
width: 95%;
}
.device-header {
display: flex;
align-items: center;
margin-bottom: 15rpx;
}
.device-name {
font-size: 32rpx;
color: rgba(255, 255, 255, 0.87);
margin-left: 12rpx;
line-height: 50rpx;
width: 83%;
white-space: nowrap;
}
.ID {
color: rgba(255, 255, 255, 0.6);
font-size: 26rpx;
display: flex;
justify-content: space-between;
position: relative;
}
.device-status {
width: 122rpx;
height: 52rpx;
font-size: 26rpx;
border-radius: 0px 8px 0px 8px;
background-color: rgb(42, 42, 42);
position: absolute;
top: 0rpx;
right: 0rpx;
text-align: center;
line-height: 52rpx;
}
.online {
color: rgb(187, 230, 0);
}
.unline {
color: rgba(255, 255, 255, 0.4);
}
.device-info {
display: flex;
justify-content: space-evenly;
font-size: 26rpx;
color: rgba(255, 255, 255, 0.87);
padding-top: 10rpx;
position: relative;
}
.deviceIMG {
width: 100rpx;
height: 100rpx;
border-radius: 16rpx;
position: relative;
background-color: rgba(42, 42, 42, 0.6);
display: flex;
align-items: center;
}
.IMG {
width: 68rpx;
height: 50rpx;
margin-left: 17%;
}
.onlines {
position: relative;
}
.onlines::before {
content: '';
position: absolute;
width: 15rpx;
height: 15rpx;
background: rgb(0, 171, 103);
border-radius: 50%;
top: 20rpx;
left: -20rpx
}
.unlines {
position: relative;
}
.unlines::before {
content: '';
position: absolute;
width: 15rpx;
height: 15rpx;
background: rgba(255, 255, 255, 0.4);
border-radius: 50%;
top: 20rpx;
left: -20rpx
}
.line {
width: 2rpx;
height: 24rpx;
background: linear-gradient(90deg,
rgba(0, 0, 0, 0) 0%,
rgb(255, 255, 255) 50%,
rgba(255, 255, 255, 0) 100%);
margin-top: 12rpx;
}
.ql-editor {
color: rgba(255, 255, 255, 0.6);
font-size: 30rpx;
}
.ql-input {
width: 95.9%;
height: 200rpx;
margin-top: 30rpx;
box-sizing: border-box;
padding: 30rpx;
border-radius: 16rpx;
background: rgb(26, 26, 26);
}
.textarea {
color: rgba(255, 255, 255, 0.8);
background: rgba(42, 42, 42, 1);
border-radius: 16rpx;
padding: 10rpx;
height: 150rpx;
}
.editInfmation {
display: flex;
padding: 20rpx;
border-radius: 40rpx 40rpx 0px 0px;
width: 96%;
position: fixed;
bottom: 50rpx;
box-sizing: border-box;
}
.login-btn {
margin-top: 30rpx;
color: rgb(35, 35, 35);
border-radius: 50rpx;
width: 40%;
color: #fff;
height: 88rpx;
font-size: 30rpx;
line-height: 88rpx;
}
.qz {
background: rgba(224, 52, 52, 1);
}
.jc {
border: 1px solid rgba(255, 255, 255, 0.87);
background: rgba(18, 18, 18, 1);
}
.checkbox.disabled {
opacity: 0.5;
background-color: rgba(255, 255, 255, 0.1) !important;
border-color: rgba(255, 255, 255, 0.2) !important;
pointer-events: none;
/* 阻止点击事件 */
}
/* 可选:离线设备的卡片整体置灰 */
.device-card[data-offline="true"] {
opacity: 0.6;
}
</style>