Compare commits
23 Commits
1eeb8e7f7c
...
main
Author | SHA1 | Date | |
---|---|---|---|
589a1eafed | |||
8a65f4690c | |||
496c40f415 | |||
6a85ea01da | |||
84335de7c4 | |||
a6cd6a5b3c | |||
c818e7607a | |||
95b020d389 | |||
b03d287dac | |||
92e22812d7 | |||
c95c0e65d2 | |||
a31318aa1a | |||
520d6b2b1a | |||
4749cdce3c | |||
df3da71c59 | |||
2da26d5f47 | |||
d80aa54c8e | |||
3463bc7993 | |||
f3c5e1924b | |||
aed63a26ac | |||
6557b79f0c | |||
78771e9551 | |||
f17e878e0c |
@ -5,7 +5,10 @@ VITE_APP_TITLE = 云平台管理系统
|
||||
VITE_APP_ENV = 'development'
|
||||
|
||||
# 开发环境
|
||||
VITE_APP_BASE_API = 'http://47.120.79.150/backend'
|
||||
# VITE_APP_BASE_API = 'http://47.120.79.150/backend'
|
||||
VITE_APP_BASE_API = 'http://192.168.2.23:8000'
|
||||
# VITE_APP_BASE_API = 'http://localhost:8000'
|
||||
|
||||
|
||||
# 应用访问路径 例如使用前缀 /admin/
|
||||
VITE_APP_CONTEXT_PATH = '/'
|
||||
@ -16,7 +19,7 @@ VITE_APP_MONITOR_ADMIN = 'http://localhost:9090/admin/applications'
|
||||
# SnailJob 控制台地址
|
||||
VITE_APP_SNAILJOB_ADMIN = 'http://localhost:8800/snail-job'
|
||||
|
||||
VITE_APP_PORT = 80
|
||||
VITE_APP_PORT = 9000
|
||||
|
||||
# 接口加密功能开关(如需关闭 后端也必须对应关闭)
|
||||
VITE_APP_ENCRYPT = true
|
||||
|
@ -37,6 +37,7 @@
|
||||
"jsencrypt": "3.3.2",
|
||||
"mitt": "^3.0.1",
|
||||
"nprogress": "0.2.0",
|
||||
"paho-mqtt": "^1.1.0",
|
||||
"pinia": "3.0.2",
|
||||
"qrcode-vue3": "^1.7.1",
|
||||
"screenfull": "6.0.2",
|
||||
|
@ -3,14 +3,14 @@ export interface deviceQuery {
|
||||
pageNum: number;
|
||||
deviceId: string;
|
||||
deviceName: string;
|
||||
deviceStatus: string;
|
||||
onlineStatus: string;
|
||||
deviceMac: string;
|
||||
deviceImei: string;
|
||||
personnelBy: string;
|
||||
communicationMode: string;
|
||||
pageSize: Number;
|
||||
deviceType: string;
|
||||
content:string
|
||||
content: string
|
||||
|
||||
}
|
||||
export interface deviceVO {
|
||||
@ -20,7 +20,7 @@ export interface deviceVO {
|
||||
onlineStatus: 0 | 1; // 设备状态(0=失效/离线,1=正常/在线,对应子组件的device.status)
|
||||
lng?: number; // 经度(地图打点用)
|
||||
lat?: number; // 纬度(地图打点用
|
||||
|
||||
|
||||
// 其他字段...
|
||||
}
|
||||
// 1. 定义设备详情的类型(根据接口返回字段调整!关键:和后端返回的字段名一致)
|
||||
@ -42,7 +42,7 @@ export interface DeviceDetail {
|
||||
name: string; // 姓名
|
||||
code: string; // ID(身份证/工号)
|
||||
};
|
||||
|
||||
chargeState: string
|
||||
}
|
||||
// 定义灯光模式的类型接口
|
||||
export interface LightMode {
|
||||
|
86
src/api/equipmentManagement/device/shareManage.ts
Normal file
@ -0,0 +1,86 @@
|
||||
import request from '@/utils/request';
|
||||
import { resolve } from 'path';
|
||||
|
||||
|
||||
|
||||
function DelShare(ids) {
|
||||
return request({
|
||||
url: '/api/equipment/share/' + ids,
|
||||
method: 'delete'
|
||||
});
|
||||
}
|
||||
//添加分享数据
|
||||
function SaveShare(data,type){
|
||||
|
||||
let promise=null;
|
||||
if(type=='add'){
|
||||
promise=addShare(data);
|
||||
}else{
|
||||
promise=powerSet(data);
|
||||
}
|
||||
return promise;
|
||||
}
|
||||
//添加
|
||||
function addShare(data) {
|
||||
return request({
|
||||
url: '/api/equipment/share/deviceShare',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
}
|
||||
|
||||
//修改
|
||||
function powerSet(data) {
|
||||
return request({
|
||||
url: '/api/equipment/share/permission',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
|
||||
}
|
||||
//查询
|
||||
function searchShare(params) {
|
||||
return request({
|
||||
url: '/api/equipment/share/deviceShareList',
|
||||
method: 'get',
|
||||
params: params
|
||||
})
|
||||
}
|
||||
|
||||
//发送验证码
|
||||
function sendSms(phoneNumber) {
|
||||
return request({
|
||||
url: '/api/equipment/share/sms/code',
|
||||
method: 'get',
|
||||
params: {
|
||||
phonenumber: phoneNumber
|
||||
}
|
||||
});
|
||||
|
||||
// return new Promise((resolve,reject)=>{
|
||||
// resolve({
|
||||
// code:200
|
||||
// });
|
||||
// });
|
||||
}
|
||||
|
||||
function getUsrs(){
|
||||
return request({
|
||||
url: '/WebApp/user/list',
|
||||
method: 'get',
|
||||
params: {
|
||||
pageNum:1,
|
||||
pageSize:9999
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
export default {
|
||||
powerSet: powerSet,
|
||||
DelShare: DelShare,
|
||||
SaveShare: SaveShare,
|
||||
searchShare: searchShare,
|
||||
sendSms:sendSms,
|
||||
getUsrs:getUsrs
|
||||
}
|
@ -1,6 +1,4 @@
|
||||
import { func } from 'vue-types';
|
||||
import request from '@/utils/request';
|
||||
import { AxiosPromise } from 'axios';
|
||||
|
||||
//左侧节点的数据源
|
||||
function getTreeData(para: any) {
|
||||
@ -40,7 +38,7 @@ function getTreeData(para: any) {
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
Promise.allSettled([promise2]).then(res => {
|
||||
debugger;
|
||||
|
||||
if (res[0].status == 'fulfilled') {
|
||||
let json = res[0].value;
|
||||
|
||||
@ -370,7 +368,7 @@ function getNodeDevice(para) {
|
||||
Promise.allSettled([promise2]).then(res => {
|
||||
|
||||
if (res[0].status == 'fulfilled') {
|
||||
debugger;
|
||||
|
||||
resolve(res[0].value);
|
||||
}
|
||||
});
|
||||
|
137
src/api/equipmentManagement/repairRecords/index.ts
Normal file
@ -0,0 +1,137 @@
|
||||
import request from '@/utils/request';
|
||||
|
||||
//修改设备维修记录
|
||||
function updateRepair(data) {
|
||||
return request({
|
||||
url: '/equipment/repairRecords',
|
||||
method: 'put',
|
||||
data:data
|
||||
// data: {
|
||||
// "recordId": data.recordId,
|
||||
// "deviceId": data.deviceId,
|
||||
// "createTime": data.createTime,
|
||||
// "updateTime": data.updateTime,
|
||||
// "repairTime": data.repairTime,
|
||||
// "repairPart": data.repairPart,
|
||||
// "repairReason": data.repairReason,
|
||||
// "repairPerson": data.repairPerson,
|
||||
// // "beforeFile": data.beforeFile,
|
||||
// // "afterFile": data.afterFile
|
||||
// }
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
//新增设备维修记录
|
||||
function addRepir(data) {
|
||||
return request({
|
||||
url: '/equipment/repairRecords',
|
||||
method: 'post',
|
||||
data:data
|
||||
// data: {
|
||||
// "recordId": data.recordId,
|
||||
// "deviceId": data.deviceId,
|
||||
// "createTime": data.createTime,
|
||||
// "updateTime": data.updateTime,
|
||||
// "repairTime": data.repairTime,
|
||||
// "repairPart": data.repairPart,
|
||||
// "repairReason": data.repairReason,
|
||||
// "repairPerson": data.repairPerson,
|
||||
// // "beforeFile": data.beforeFile,
|
||||
// // "afterFile": data.afterFile
|
||||
// }
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
//导出设备维修记录
|
||||
function exportRepir(data) {
|
||||
return request({
|
||||
url: '/equipment/repairRecords/export',
|
||||
method: 'post',
|
||||
data: {
|
||||
"recordId": data.recordId,
|
||||
"deviceId": data.deviceId,
|
||||
"createTime": data.createTime,
|
||||
"updateTime": data.updateTime,
|
||||
"repairTime": data.repairTime,
|
||||
"repairPart": data.repairPart,
|
||||
"repairReason": data.repairReason,
|
||||
"repairPerson": data.repairPerson
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
//获取明细设备维修记录
|
||||
function getRepirDetail(id) {
|
||||
return request({
|
||||
url: '/equipment/repairRecords/' + id,
|
||||
method: 'get'
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
//获取列表
|
||||
function getRepairList(data) {
|
||||
return request({
|
||||
url: '/equipment/repairRecords/list',
|
||||
method: 'get',
|
||||
params: {
|
||||
"recordId": data.recordId,
|
||||
"deviceId": data.deviceId,
|
||||
"repairPart": data.repairPart,
|
||||
"repairReason": data.repairReason,
|
||||
"repairPerson": data.repairPerson,
|
||||
"repairBeginTime": data.repairBeginTime,
|
||||
"repairEndTime": data.repairBeginTime,
|
||||
"pageNum": data.pageNum,
|
||||
"pageSize": data.pageSize
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
//删除记录
|
||||
function dropRepir(id) {
|
||||
if (Array.isArray(id)) {
|
||||
id = id.join(",");
|
||||
}
|
||||
return request({
|
||||
url: '/equipment/repairRecords/' + id,
|
||||
method: 'delete'
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
//获取设备类型
|
||||
function getDeviceTypeAll() {
|
||||
return new Promise((resolve, reject) => {
|
||||
request({
|
||||
url: '/api/deviceType/all',
|
||||
method: 'get',
|
||||
|
||||
}).then((res) => {
|
||||
resolve(res);
|
||||
}).catch((ex) => {
|
||||
reject(ex);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export default {
|
||||
updateRepair: updateRepair,
|
||||
addRepir: addRepir,
|
||||
exportRepir: exportRepir,
|
||||
getRepirDetail: getRepirDetail,
|
||||
getRepairList: getRepairList,
|
||||
dropRepir: dropRepir,
|
||||
getDeviceTypeAll: getDeviceTypeAll
|
||||
}
|
43
src/api/home/index.ts
Normal file
@ -0,0 +1,43 @@
|
||||
import request from '@/utils/request';
|
||||
import { AxiosPromise } from 'axios';
|
||||
// 获取 数据总览 DataOverview
|
||||
export const getDataOverview = (params) => {
|
||||
return request({
|
||||
url: '/api/device/homepage/getDataOverview',
|
||||
method: 'get',
|
||||
params: params
|
||||
});
|
||||
};
|
||||
// 设备分类
|
||||
export const getEquipmentClassification = (params) => {
|
||||
return request({
|
||||
url: '/api/device/homepage/getEquipmentClassification',
|
||||
method: 'get',
|
||||
params: params
|
||||
});
|
||||
};
|
||||
// 获取设备使用情况
|
||||
export const getEquipmentUsageData = (range, params = {}) => {
|
||||
return request({
|
||||
url: `/api/device/homepage/getEquipmentUsageData/${range}`,
|
||||
method: 'get',
|
||||
params: params
|
||||
});
|
||||
};
|
||||
|
||||
// 获取报警信息
|
||||
export const getAlarmInformation = (params) => {
|
||||
return request({
|
||||
url: `/api/device/homepage/getAlarmInformation`,
|
||||
method: 'get',
|
||||
params: params
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
export default {
|
||||
getDataOverview,
|
||||
getEquipmentClassification,
|
||||
getEquipmentUsageData,
|
||||
getAlarmInformation
|
||||
}
|
6
src/api/home/types.ts
Normal file
@ -0,0 +1,6 @@
|
||||
export interface DataOverviewType {
|
||||
devicesNumber: number | string;
|
||||
equipmentOnline: number | string;
|
||||
bindingNew: number | string;
|
||||
equipmentAbnormal: number | string;
|
||||
}
|
BIN
src/assets/images/path.png
Normal file
After Width: | Height: | Size: 408 B |
BIN
src/assets/images/position_ico.png
Normal file
After Width: | Height: | Size: 2.7 KiB |
BIN
src/assets/index/add.png
Normal file
After Width: | Height: | Size: 35 KiB |
BIN
src/assets/index/conton.png
Normal file
After Width: | Height: | Size: 7.5 KiB |
BIN
src/assets/index/device_add.png
Normal file
After Width: | Height: | Size: 6.1 KiB |
BIN
src/assets/index/device_group.png
Normal file
After Width: | Height: | Size: 7.1 KiB |
BIN
src/assets/index/device_type.png
Normal file
After Width: | Height: | Size: 6.6 KiB |
BIN
src/assets/index/device_yc.png
Normal file
After Width: | Height: | Size: 36 KiB |
BIN
src/assets/index/devices_online.png
Normal file
After Width: | Height: | Size: 36 KiB |
BIN
src/assets/index/online.png
Normal file
After Width: | Height: | Size: 29 KiB |
@ -163,6 +163,7 @@ aside {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.components-container {
|
||||
margin: 30px 50px;
|
||||
position: relative;
|
||||
@ -214,4 +215,4 @@ aside {
|
||||
vertical-align: middle;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
}
|
||||
}
|
@ -28,7 +28,7 @@
|
||||
// 使用CSS变量定义边框样式,支持动态修改
|
||||
border-right: var(--sidebarBorderWidth) var(--sidebarBorderStyle) var(--sidebarBorderColor);
|
||||
box-shadow: var(--sidebarBoxShadow);
|
||||
|
||||
|
||||
// 备用方案:如果CSS变量不支持,使用默认样式
|
||||
@supports not (border-right: var(--sidebarBorderWidth) var(--sidebarBorderStyle) var(--sidebarBorderColor)) {
|
||||
border-right: none;
|
||||
@ -81,11 +81,14 @@
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
.el-menu-item,
|
||||
.el-sub-menu,
|
||||
.menu-title {
|
||||
overflow: hidden !important;
|
||||
text-overflow: ellipsis !important;
|
||||
white-space: nowrap !important;
|
||||
font-family: Microsoft YaHei;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.el-menu-item .el-menu-tooltip__trigger {
|
||||
@ -100,6 +103,7 @@
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
}
|
||||
|
||||
.sub-menu-title-noDropdown,
|
||||
.el-sub-menu__title {
|
||||
&:hover {
|
||||
@ -108,7 +112,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
& .theme-dark .is-active > .el-sub-menu__title {
|
||||
& .theme-dark .is-active>.el-sub-menu__title {
|
||||
color: #ffffff !important;
|
||||
background: var(--menuActiveGradient) !important; // 使用正确的渐变颜色
|
||||
border-radius: 4px;
|
||||
@ -116,16 +120,17 @@
|
||||
box-shadow: 0 2px 4px rgba(64, 158, 255, 0.3);
|
||||
}
|
||||
|
||||
& .nest-menu .el-sub-menu > .el-sub-menu__title,
|
||||
& .nest-menu .el-sub-menu>.el-sub-menu__title,
|
||||
& .el-sub-menu .el-menu-item {
|
||||
min-width: $base-sidebar-width !important;
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(0, 0, 0, 0.1) !important;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
}
|
||||
|
||||
& .theme-dark .nest-menu .el-sub-menu > .el-sub-menu__title,
|
||||
& .theme-dark .nest-menu .el-sub-menu>.el-sub-menu__title,
|
||||
& .theme-dark .el-sub-menu .el-menu-item {
|
||||
background-color: $base-sub-menu-background !important;
|
||||
|
||||
@ -135,7 +140,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
& .theme-dark .nest-menu .el-sub-menu > .el-sub-menu__title,
|
||||
& .theme-dark .nest-menu .el-sub-menu>.el-sub-menu__title,
|
||||
& .theme-dark .el-menu-item {
|
||||
&:hover {
|
||||
// you can use $sub-menuHover
|
||||
@ -143,7 +148,8 @@
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
}
|
||||
& .nest-menu .el-sub-menu > .el-sub-menu__title,
|
||||
|
||||
& .nest-menu .el-sub-menu>.el-sub-menu__title,
|
||||
& .el-menu-item {
|
||||
&:hover {
|
||||
// you can use $sub-menuHover
|
||||
@ -178,7 +184,7 @@
|
||||
.el-sub-menu {
|
||||
overflow: hidden;
|
||||
|
||||
& > .el-sub-menu__title {
|
||||
&>.el-sub-menu__title {
|
||||
padding: 0 !important;
|
||||
|
||||
.svg-icon {
|
||||
@ -189,15 +195,16 @@
|
||||
|
||||
.el-menu--collapse {
|
||||
.el-sub-menu {
|
||||
& > .el-sub-menu__title {
|
||||
& > span {
|
||||
&>.el-sub-menu__title {
|
||||
&>span {
|
||||
height: 0;
|
||||
width: 0;
|
||||
overflow: hidden;
|
||||
visibility: hidden;
|
||||
display: inline-block;
|
||||
}
|
||||
& > i {
|
||||
|
||||
&>i {
|
||||
height: 0;
|
||||
width: 0;
|
||||
overflow: hidden;
|
||||
@ -234,6 +241,7 @@
|
||||
}
|
||||
|
||||
.withoutAnimation {
|
||||
|
||||
.main-container,
|
||||
.sidebar-container {
|
||||
transition: none;
|
||||
@ -243,7 +251,7 @@
|
||||
|
||||
// when menu collapsed
|
||||
.el-menu--vertical {
|
||||
& > .el-menu {
|
||||
&>.el-menu {
|
||||
.svg-icon {
|
||||
margin-right: 16px;
|
||||
}
|
||||
@ -286,7 +294,7 @@
|
||||
}
|
||||
|
||||
// 子菜单激活状态
|
||||
.el-sub-menu.is-active > .el-sub-menu__title {
|
||||
.el-sub-menu.is-active>.el-sub-menu__title {
|
||||
background: var(--menuActiveGradient) !important; // 使用正确的渐变颜色
|
||||
color: #ffffff !important;
|
||||
border-radius: 4px;
|
||||
@ -299,7 +307,7 @@
|
||||
.sidebar-container {
|
||||
// 确保背景色与图片一致
|
||||
background: #2c3e50 !important;
|
||||
|
||||
|
||||
.el-menu-item {
|
||||
&.is-active {
|
||||
background: var(--menuActiveGradient) !important; // 使用正确的渐变颜色
|
||||
@ -310,9 +318,9 @@
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.el-sub-menu {
|
||||
&.is-active > .el-sub-menu__title {
|
||||
&.is-active>.el-sub-menu__title {
|
||||
background: var(--menuActiveGradient) !important; // 使用正确的渐变颜色
|
||||
color: #ffffff !important;
|
||||
border-radius: 4px;
|
||||
@ -321,12 +329,12 @@
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 子菜单项激活状态(只有文字颜色变化,无背景)
|
||||
.el-sub-menu .el-menu-item {
|
||||
&.is-active {
|
||||
background: transparent !important;
|
||||
color: #409eff !important;
|
||||
color: rgba(1, 159, 228, 1) !important;
|
||||
border-radius: 0;
|
||||
margin: 0;
|
||||
box-shadow: none;
|
||||
@ -341,35 +349,35 @@
|
||||
box-shadow: 1px 0 4px rgba(0, 0, 0, 0.1) !important;
|
||||
border-right: none !important;
|
||||
}
|
||||
|
||||
|
||||
.sidebar-logo-container {
|
||||
background: #2c3e50 !important;
|
||||
}
|
||||
|
||||
|
||||
.el-menu {
|
||||
background: #2c3e50 !important;
|
||||
}
|
||||
|
||||
|
||||
// 非激活菜单项样式
|
||||
.el-menu-item,
|
||||
.el-sub-menu__title {
|
||||
color: #bfcbd9 !important;
|
||||
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(0, 0, 0, 0.1) !important;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 激活的主菜单项(正确的渐变背景)
|
||||
.el-menu-item.is-active,
|
||||
.el-sub-menu.is-active > .el-sub-menu__title {
|
||||
.el-sub-menu.is-active>.el-sub-menu__title {
|
||||
background: var(--menuActiveGradient) !important; // 使用正确的渐变颜色
|
||||
color: #ffffff !important;
|
||||
border-radius: 4px;
|
||||
margin: 2px 8px;
|
||||
box-shadow: 0 2px 4px rgba(64, 158, 255, 0.3);
|
||||
}
|
||||
|
||||
|
||||
// 激活的子菜单项(只有蓝色文字,无背景)
|
||||
.el-sub-menu .el-menu-item.is-active {
|
||||
background: transparent !important;
|
||||
@ -378,17 +386,17 @@
|
||||
margin: 0;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
|
||||
// 子菜单背景色
|
||||
.el-sub-menu .el-menu {
|
||||
background: #1f2d3d !important;
|
||||
background: linear-gradient(0.00deg, rgba(0, 87, 159, 0.15), rgba(0, 87, 159, 0) 100%) !important;
|
||||
}
|
||||
|
||||
|
||||
// 子菜单项悬停效果
|
||||
.el-sub-menu .el-menu-item:hover {
|
||||
background-color: rgba(0, 0, 0, 0.1) !important;
|
||||
}
|
||||
|
||||
|
||||
// 主菜单项悬停效果
|
||||
.el-menu-item:hover,
|
||||
.el-sub-menu__title:hover {
|
||||
@ -399,28 +407,29 @@
|
||||
// 全局菜单样式匹配设计UI
|
||||
#app .sidebar-container {
|
||||
// 确保背景色一致
|
||||
background: #2c3e50 !important;
|
||||
|
||||
background: rgba(11, 24, 40, 1) !important;
|
||||
//background: #2c3e50 !important;
|
||||
|
||||
// 菜单项基础样式
|
||||
.el-menu-item,
|
||||
.el-sub-menu__title {
|
||||
color: #bfcbd9 !important;
|
||||
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(0, 0, 0, 0.1) !important;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 激活的主菜单项
|
||||
.el-menu-item.is-active,
|
||||
.el-sub-menu.is-active > .el-sub-menu__title {
|
||||
.el-sub-menu.is-active>.el-sub-menu__title {
|
||||
background: var(--menuActiveGradient) !important; // 使用正确的渐变颜色
|
||||
color: #ffffff !important;
|
||||
border-radius: 4px;
|
||||
margin: 2px 8px;
|
||||
box-shadow: 0 2px 4px rgba(64, 158, 255, 0.3);
|
||||
}
|
||||
|
||||
|
||||
// 激活的子菜单项
|
||||
.el-sub-menu .el-menu-item.is-active {
|
||||
background: transparent !important;
|
||||
@ -429,19 +438,19 @@
|
||||
margin: 0;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
|
||||
// 子菜单背景
|
||||
.el-sub-menu .el-menu {
|
||||
background: #1f2d3d !important;
|
||||
}
|
||||
|
||||
|
||||
// 图标颜色
|
||||
.svg-icon {
|
||||
color: inherit !important;
|
||||
}
|
||||
|
||||
|
||||
// 箭头图标颜色
|
||||
.el-sub-menu__icon-arrow {
|
||||
color: inherit !important;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
// 全局SCSS变量
|
||||
:root {
|
||||
--menuBg: #2c3e50; // 精确匹配图片中的深色背景
|
||||
--menuBg: linear-gradient(0.00deg, rgba(0, 87, 159, 0.15),rgba(0, 87, 159, 0) 100%);; // 精确匹配图片中的深色背景
|
||||
--menuColor: #bfcbd9; // 非激活菜单项的文字颜色
|
||||
--menuActiveText: #ffffff; // 激活菜单项的文字颜色
|
||||
--menuHover: #34495e; // 悬停时的背景色
|
||||
@ -11,7 +11,7 @@
|
||||
--subMenuTitleHover: #293444; // 子菜单标题悬停背景色
|
||||
|
||||
// 激活菜单项的渐变颜色 - 根据设计UI调整
|
||||
--menuActiveGradient: linear-gradient(90deg, #0080EA 0%, #000F27 100%);
|
||||
--menuActiveGradient:linear-gradient(90.00deg, rgba(0, 128, 234, 1),rgba(0, 15, 39, 0) 100%);
|
||||
|
||||
--fixedHeaderBg: #ffffff;
|
||||
--tableHeaderBg: #f8f8f9;
|
||||
@ -130,7 +130,7 @@ $--color-warning: #e6a23c;
|
||||
$--color-danger: #f56c6c;
|
||||
$--color-info: #909399;
|
||||
|
||||
$base-sidebar-width: 280px;
|
||||
$base-sidebar-width: 250px;
|
||||
|
||||
// the :export directive is the magic sauce for webpack
|
||||
// https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass
|
||||
|
77
src/utils/common.ts
Normal file
@ -0,0 +1,77 @@
|
||||
function DateFormat(date, format) {
|
||||
if (!date) {
|
||||
return '';
|
||||
}
|
||||
if (!format) {
|
||||
format = 'yyyy-MM-dd HH:mm:ss';
|
||||
}
|
||||
// 处理参数默认值
|
||||
if (typeof date === 'string' || typeof date === 'number') {
|
||||
date = new Date(date);
|
||||
}
|
||||
date = date instanceof Date ? date : new Date();
|
||||
format = format || 'yyyy-MM-dd';
|
||||
|
||||
// 检查日期是否有效
|
||||
if (isNaN(date.getTime())) {
|
||||
return 'Invalid Date';
|
||||
}
|
||||
|
||||
// 定义格式化映射
|
||||
const formatMap = {
|
||||
'yyyy': date.getFullYear(),
|
||||
'MM': String(date.getMonth() + 1).padStart(2, '0'),
|
||||
'dd': String(date.getDate()).padStart(2, '0'),
|
||||
'HH': String(date.getHours()).padStart(2, '0'),
|
||||
'mm': String(date.getMinutes()).padStart(2, '0'),
|
||||
'ss': String(date.getSeconds()).padStart(2, '0'),
|
||||
'SSS': String(date.getMilliseconds()).padStart(3, '0'),
|
||||
'M': date.getMonth() + 1,
|
||||
'd': date.getDate(),
|
||||
'H': date.getHours(),
|
||||
'm': date.getMinutes(),
|
||||
's': date.getSeconds(),
|
||||
'S': date.getMilliseconds()
|
||||
};
|
||||
|
||||
// 替换格式字符串中的占位符
|
||||
return format.replace(/(yyyy|MM|dd|HH|mm|ss|SSS|M|d|H|m|s|S)/g, (match) => {
|
||||
return formatMap[match];
|
||||
});
|
||||
}
|
||||
|
||||
function DateAdd(datePart, number, date) {
|
||||
// 创建日期的副本,避免修改原日期对象
|
||||
const newDate = new Date(date.getTime());
|
||||
|
||||
// 根据不同的时间单位添加相应的值
|
||||
switch (datePart.toLowerCase()) {
|
||||
case 'year':
|
||||
newDate.setFullYear(newDate.getFullYear() + number);
|
||||
break;
|
||||
case 'month':
|
||||
newDate.setMonth(newDate.getMonth() + number);
|
||||
break;
|
||||
case 'day':
|
||||
newDate.setDate(newDate.getDate() + number);
|
||||
break;
|
||||
case 'hour':
|
||||
newDate.setHours(newDate.getHours() + number);
|
||||
break;
|
||||
case 'minute':
|
||||
newDate.setMinutes(newDate.getMinutes() + number);
|
||||
break;
|
||||
case 'second':
|
||||
newDate.setSeconds(newDate.getSeconds() + number);
|
||||
break;
|
||||
default:
|
||||
throw new Error('不支持的datePart参数。支持的参数: Year, Month, Day, Hour, Minute, Second');
|
||||
}
|
||||
|
||||
return newDate;
|
||||
}
|
||||
|
||||
export default{
|
||||
DateFormat:DateFormat,
|
||||
DateAdd:DateAdd
|
||||
}
|
265
src/utils/mqtt.ts
Normal file
@ -0,0 +1,265 @@
|
||||
import { ref, onUnmounted } from 'vue'; // 修复:导入必要的Vue API
|
||||
import * as Paho from 'paho-mqtt';
|
||||
|
||||
// MQTT消息类型定义
|
||||
export interface MqttMessage {
|
||||
topic: string;
|
||||
payload: string | ArrayBuffer;
|
||||
qos: number;
|
||||
retained: boolean;
|
||||
time: Date;
|
||||
}
|
||||
|
||||
// 订阅选项
|
||||
export interface SubscribeOptions {
|
||||
qos: 0 | 1 | 2;
|
||||
}
|
||||
|
||||
// MQTT配置信息
|
||||
const MQTT_CONFIG = {
|
||||
// 连接地址(添加协议类型)
|
||||
protocol: 'ws', // 关键:明确协议(ws或wss)
|
||||
host: '47.120.79.150',
|
||||
port: 9083,
|
||||
// 认证信息
|
||||
username: 'admin',
|
||||
password: '#YtvpSfCNG',
|
||||
// 客户端ID(添加时间戳确保唯一性)
|
||||
clientId: `vue3-mqtt-client-${Date.now()}-${Math.random().toString(36).substring(2, 10)}`,
|
||||
// 连接选项
|
||||
cleanSession: true,
|
||||
keepAliveInterval: 60,
|
||||
reconnect: true,
|
||||
};
|
||||
|
||||
// MQTT客户端组合式API
|
||||
export function useMqtt() {
|
||||
// 客户端实例
|
||||
let client: Paho.Client | null = null;
|
||||
|
||||
// 状态管理(修复:已导入ref)
|
||||
const connected = ref(false);
|
||||
const connecting = ref(false);
|
||||
const error = ref<Error | null>(null);
|
||||
const messages = ref<MqttMessage[]>([]);
|
||||
const subscribedTopics = ref<string[]>([]);
|
||||
|
||||
// 事件回调
|
||||
const connectCallbacks: (() => void)[] = [];
|
||||
const messageCallbacks: ((message: MqttMessage) => void)[] = [];
|
||||
const errorCallbacks: ((err: Error) => void)[] = [];
|
||||
const disconnectCallbacks: (() => void)[] = [];
|
||||
|
||||
// 修复:移除无用的p0参数,connect方法不接受回调(通过onConnect注册)
|
||||
const connect = () => {
|
||||
if (connected.value || connecting.value) return;
|
||||
connecting.value = true;
|
||||
error.value = null;
|
||||
|
||||
try {
|
||||
// 创建客户端实例(添加协议参数)
|
||||
client = new Paho.Client(
|
||||
MQTT_CONFIG.host,
|
||||
MQTT_CONFIG.port,
|
||||
MQTT_CONFIG.clientId
|
||||
);
|
||||
|
||||
// 设置连接选项
|
||||
const connectOptions: Paho.ConnectOptions = {
|
||||
userName: MQTT_CONFIG.username,
|
||||
password: MQTT_CONFIG.password,
|
||||
cleanSession: MQTT_CONFIG.cleanSession,
|
||||
keepAliveInterval: MQTT_CONFIG.keepAliveInterval,
|
||||
reconnect: MQTT_CONFIG.reconnect,
|
||||
|
||||
// 连接成功回调
|
||||
onSuccess: () => {
|
||||
console.log('MQTT连接成功');
|
||||
connected.value = true;
|
||||
connecting.value = false;
|
||||
connectCallbacks.forEach(cb => cb()); // 触发所有连接成功回调
|
||||
},
|
||||
|
||||
// 连接失败回调
|
||||
onFailure: (err) => {
|
||||
console.error('MQTT连接失败:', err);
|
||||
error.value = new Error(err.errorMessage || '连接失败');
|
||||
connected.value = false;
|
||||
connecting.value = false;
|
||||
errorCallbacks.forEach(cb => cb(error.value!));
|
||||
}
|
||||
};
|
||||
|
||||
// 设置客户端回调
|
||||
client.onConnectionLost = (responseObject) => {
|
||||
if (responseObject.errorCode !== 0) {
|
||||
console.error('连接丢失:', responseObject.errorMessage);
|
||||
error.value = new Error(responseObject.errorMessage || '连接丢失');
|
||||
errorCallbacks.forEach(cb => cb(error.value!));
|
||||
}
|
||||
|
||||
connected.value = false;
|
||||
connecting.value = false;
|
||||
disconnectCallbacks.forEach(cb => cb());
|
||||
};
|
||||
|
||||
// 消息接收回调
|
||||
client.onMessageArrived = (message) => {
|
||||
const newMessage: MqttMessage = {
|
||||
topic: message.destinationName,
|
||||
payload: message.payloadString || message.payloadBytes,
|
||||
qos: message.qos,
|
||||
retained: message.retained,
|
||||
time: new Date()
|
||||
};
|
||||
|
||||
messages.value.push(newMessage);
|
||||
messageCallbacks.forEach(cb => cb(newMessage));
|
||||
};
|
||||
|
||||
// 连接服务器
|
||||
client.connect(connectOptions);
|
||||
} catch (err) {
|
||||
console.error('MQTT连接异常:', err);
|
||||
error.value = err as Error;
|
||||
connected.value = false;
|
||||
connecting.value = false;
|
||||
errorCallbacks.forEach(cb => cb(error.value!));
|
||||
}
|
||||
};
|
||||
|
||||
// 断开连接
|
||||
const disconnect = () => {
|
||||
if (!client || !connected.value) return;
|
||||
|
||||
client.disconnect();
|
||||
client = null;
|
||||
connected.value = false;
|
||||
subscribedTopics.value = [];
|
||||
disconnectCallbacks.forEach(cb => cb());
|
||||
};
|
||||
|
||||
// 订阅主题
|
||||
const subscribe = (topic: string, options: SubscribeOptions): Promise<void> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!client || !connected.value) {
|
||||
reject(new Error('未连接到MQTT服务器'));
|
||||
return;
|
||||
}
|
||||
|
||||
if (subscribedTopics.value.includes(topic)) {
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
client.subscribe(topic, {
|
||||
qos: options.qos,
|
||||
onSuccess: () => {
|
||||
console.log(`订阅主题成功: ${topic}`);
|
||||
subscribedTopics.value.push(topic);
|
||||
resolve();
|
||||
},
|
||||
onFailure: (err) => {
|
||||
console.error(`订阅主题失败: ${topic}`, err);
|
||||
reject(new Error(err.errorMessage || `订阅主题 ${topic} 失败`));
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// 取消订阅
|
||||
const unsubscribe = (topic: string): Promise<void> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!client || !connected.value) {
|
||||
reject(new Error('未连接到MQTT服务器'));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!subscribedTopics.value.includes(topic)) {
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
client.unsubscribe(topic, {
|
||||
onSuccess: () => {
|
||||
console.log(`取消订阅主题成功: ${topic}`);
|
||||
subscribedTopics.value = subscribedTopics.value.filter(t => t !== topic);
|
||||
resolve();
|
||||
},
|
||||
onFailure: (err) => {
|
||||
console.error(`取消订阅主题失败: ${topic}`, err);
|
||||
reject(new Error(err.errorMessage || `取消订阅主题 ${topic} 失败`));
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// 发布消息
|
||||
const publish = (
|
||||
topic: string,
|
||||
payload: string | ArrayBuffer,
|
||||
options: { qos: 0 | 1 | 2; retained?: boolean }
|
||||
): Promise<void> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!client || !connected.value) {
|
||||
reject(new Error('未连接到MQTT服务器'));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!topic) {
|
||||
reject(new Error('主题不能为空'));
|
||||
return;
|
||||
}
|
||||
|
||||
const message = new Paho.Message(
|
||||
typeof payload === 'string' ? payload : payload.toString()
|
||||
);
|
||||
|
||||
message.destinationName = topic;
|
||||
message.qos = options.qos;
|
||||
message.retained = options.retained ?? false;
|
||||
|
||||
client.send(message);
|
||||
resolve();
|
||||
});
|
||||
};
|
||||
|
||||
// 事件注册
|
||||
const onConnect = (callback: () => void) => {
|
||||
connectCallbacks.push(callback);
|
||||
};
|
||||
|
||||
const onMessage = (callback: (message: MqttMessage) => void) => {
|
||||
messageCallbacks.push(callback);
|
||||
};
|
||||
|
||||
const onError = (callback: (err: Error) => void) => {
|
||||
errorCallbacks.push(callback);
|
||||
};
|
||||
|
||||
const onDisconnect = (callback: () => void) => {
|
||||
disconnectCallbacks.push(callback);
|
||||
};
|
||||
|
||||
// 组件卸载时断开连接
|
||||
onUnmounted(() => {
|
||||
disconnect();
|
||||
});
|
||||
|
||||
return {
|
||||
connected,
|
||||
connecting,
|
||||
error,
|
||||
messages,
|
||||
subscribedTopics,
|
||||
connect,
|
||||
disconnect,
|
||||
subscribe,
|
||||
unsubscribe,
|
||||
publish,
|
||||
onConnect,
|
||||
onMessage,
|
||||
onError,
|
||||
onDisconnect
|
||||
};
|
||||
}
|
@ -34,6 +34,7 @@ const service = axios.create({
|
||||
// 请求拦截器
|
||||
service.interceptors.request.use(
|
||||
(config: InternalAxiosRequestConfig) => {
|
||||
|
||||
// 对应国际化资源文件后缀
|
||||
config.headers['Content-Language'] = getLanguage();
|
||||
|
||||
|
7
src/views/controlCenter/210/index.vue
Normal file
@ -0,0 +1,7 @@
|
||||
<template>
|
||||
<div class="device-page p-2">
|
||||
这是210控制页面
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts"></script>
|
||||
<style lang="scss" scoped></style>
|
@ -113,18 +113,32 @@
|
||||
<div class="message-content">
|
||||
<el-input type="textarea" class="textareaTFT" :rows="4" placeholder="现场危险,停止救援!紧急撤离至安全区域!"
|
||||
v-model="deviceDetail.sendMsg" resize="none" />
|
||||
<el-button type="primary" class="send-btn" @click="sendTextMessage"
|
||||
:loading="sendTextLoading" :loading-text="sendTextLoading ? '发送中...' : '发送'"> {{
|
||||
sendTextLoading ? '发送中' : '发送' }}</el-button>
|
||||
<div style="text-align: end;clear: both;">
|
||||
<el-button type="primary" class="send-btn" @click="sendTextMessage"
|
||||
:loading="sendTextLoading" :loading-text="sendTextLoading ? '发送中...' : '发送'"> {{
|
||||
sendTextLoading ? '发送中' : '发送' }}</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
<!-- ===========充电提示框====== -->
|
||||
<el-dialog title="充电提示" v-model="centerDialogVisible" width="15%">
|
||||
<div style="display: flex; align-items: center;">
|
||||
<h3 style="color: rgba(224, 52, 52, 1)">设备电量低于20%</h3>
|
||||
</div>
|
||||
<div>请及时充电</div>
|
||||
<span slot="footer" class="dialog-footer" style="text-align: right;display: block;">
|
||||
<el-button type="primary" @click="centerDialogVisible = false">确 定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
<script setup name="DeviceControl" lang="ts">
|
||||
const route = useRoute();
|
||||
import { useMqtt } from '@/utils/mqtt';
|
||||
import api from '@/api/controlCenter/controlPanel/index'
|
||||
import { DeviceDetail, LightMode } from '@/api/controlCenter/controlPanel/types';
|
||||
import { generateShortId, getDeviceStatus } from '@/utils/function';
|
||||
@ -147,7 +161,16 @@ const fullscreenLoading = ref(false)
|
||||
const forceAlarmLoading = ref(false) //强制报警
|
||||
const sendTextLoading = ref(false)
|
||||
const lightModesLoading = ref(false)
|
||||
|
||||
const centerDialogVisible = ref(false)
|
||||
const {
|
||||
connected,
|
||||
connect,
|
||||
subscribe,
|
||||
onConnect,
|
||||
onError,
|
||||
onMessage,
|
||||
disconnect
|
||||
} = useMqtt();
|
||||
// 灯光模式数据(引用导入的图片)
|
||||
const lightModes = ref<LightMode[]>([
|
||||
{
|
||||
@ -206,7 +229,7 @@ const laserMode = ref<LightMode>({
|
||||
active: false
|
||||
});
|
||||
|
||||
const deviceDetail = ref<DeviceDetail>({
|
||||
const deviceDetail = ref<DeviceDetail & { typeName: string }>({
|
||||
// 重点:personnelInfo 初始化为空对象,避免 undefined
|
||||
personnelInfo: {
|
||||
unitName: '',
|
||||
@ -223,7 +246,9 @@ const deviceDetail = ref<DeviceDetail>({
|
||||
longitude: '',
|
||||
latitude: '',
|
||||
address: '',
|
||||
sendMsg: ''
|
||||
sendMsg: '',
|
||||
chargeState: '0',
|
||||
typeName: ''
|
||||
});
|
||||
// 保留原有的操作中标志位
|
||||
const isUpdatingStatus = ref(false);
|
||||
@ -244,6 +269,7 @@ const handleModeClick = async (modeId: string) => {
|
||||
typeName: deviceDetail.value.typeName,
|
||||
});
|
||||
if (res.code === 200) {
|
||||
ElMessage.closeAll();
|
||||
proxy?.$modal.msgSuccess(res.msg);
|
||||
setActiveLightMode(modeId);
|
||||
} else {
|
||||
@ -300,9 +326,12 @@ const getList = async () => {
|
||||
targetModeId = matchedMode.id;
|
||||
}
|
||||
setActiveLightMode(targetModeId);
|
||||
const laserStatus = res.data.laserModeStatus ?? 0;
|
||||
const laserStatus = Number(res.data.laserLightMode);
|
||||
laserMode.value.active = laserStatus === 1;
|
||||
laserMode.value.switchStatus = laserStatus === 1;
|
||||
setTimeout(() => {
|
||||
console.log('延迟检查激光状态:', laserMode.value.active, laserMode.value.switchStatus);
|
||||
}, 1000);
|
||||
} catch (error) {
|
||||
console.error("获取设备详情失败:", error);
|
||||
setActiveLightMode("strong"); // 异常时默认强光
|
||||
@ -321,6 +350,7 @@ const handleLaserClick = async () => {
|
||||
instructValue: targetStatus ? 1 : 0
|
||||
});
|
||||
if (res.code === 200) {
|
||||
ElMessage.closeAll();
|
||||
proxy?.$modal.msgSuccess(res.msg);
|
||||
laserMode.value.active = targetStatus;
|
||||
laserMode.value.switchStatus = targetStatus;
|
||||
@ -339,18 +369,22 @@ const handleLaserClick = async () => {
|
||||
// 人员信息发送
|
||||
const registerPostInit = () => {
|
||||
if (!deviceDetail.value.personnelInfo.unitName) {
|
||||
ElMessage.closeAll();
|
||||
proxy?.$modal.msgWarning('单位名称不能为空');
|
||||
return
|
||||
}
|
||||
if (!deviceDetail.value.personnelInfo.name) {
|
||||
ElMessage.closeAll();
|
||||
proxy?.$modal.msgWarning('姓名不能为空');
|
||||
return
|
||||
}
|
||||
if (!deviceDetail.value.personnelInfo.position) {
|
||||
ElMessage.closeAll();
|
||||
proxy?.$modal.msgWarning('职位不能为空');
|
||||
return
|
||||
}
|
||||
if (!deviceDetail.value.personnelInfo.code) {
|
||||
ElMessage.closeAll();
|
||||
proxy?.$modal.msgWarning('ID不能为空');
|
||||
return
|
||||
}
|
||||
@ -397,6 +431,7 @@ const saveBtn = () => {
|
||||
// 强制报警
|
||||
const forceAlarm = async () => {
|
||||
try {
|
||||
await proxy?.$modal.confirm('确定要对该设备开启强制报警?', '提示');
|
||||
forceAlarmLoading.value = true
|
||||
// 2. 准备请求数据
|
||||
const batchId = generateShortId();
|
||||
@ -439,6 +474,7 @@ const forceAlarm = async () => {
|
||||
const sendTextMessage = async () => {
|
||||
// 防重复提交
|
||||
if (!deviceDetail.value.sendMsg) {
|
||||
ElMessage.closeAll();
|
||||
proxy?.$modal.msgWarning('发送信息不能为空');
|
||||
return;
|
||||
}
|
||||
@ -490,9 +526,117 @@ const lookMap = (row: any) => {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getList();
|
||||
const getMainLightModeLabel = (mode: any) => {
|
||||
const modeMap = {
|
||||
0: 'close', // 0 → 关闭
|
||||
1: 'strong', // 1 → 强光
|
||||
2: 'weak', // 2 → 弱光
|
||||
3: 'strobe', // 3 → 爆闪
|
||||
4: 'flood' // 4 → 泛光
|
||||
}
|
||||
return modeMap[mode] || (console.log('未知的灯光模式:', mode), '未知');
|
||||
}
|
||||
// 处理设备消息
|
||||
const handleDeviceMessage = (msg: any) => {
|
||||
try {
|
||||
// 解析设备消息(假设格式为 { state: [类型, 模式值, 亮度, 续航...] })
|
||||
const payloadObj = JSON.parse(msg.payload.toString());
|
||||
const deviceState = payloadObj.state; // 设备状态数组
|
||||
if (!Array.isArray(deviceState)) {
|
||||
return;
|
||||
}
|
||||
// 用switch处理不同的消息类型(deviceState[0])
|
||||
switch (deviceState[0]) {
|
||||
case 1:
|
||||
// 类型1灯光主键
|
||||
const lightModeId = getMainLightModeLabel(deviceState[1]); // 获取模式ID(如'strong')
|
||||
const brightness = deviceState[2]; // 亮度值
|
||||
const batteryTime = deviceState[3]; // 续航时间
|
||||
console.log('灯光模式消息:', { 模式ID: lightModeId, 亮度: brightness, 续航: batteryTime });
|
||||
// 1. 同步灯光模式状态
|
||||
if (lightModeId !== 'unknown') {
|
||||
lightModes.value.forEach(mode => {
|
||||
const isActive = mode.id === lightModeId;
|
||||
mode.active = isActive;
|
||||
mode.switchStatus = isActive;
|
||||
});
|
||||
}
|
||||
// 2.亮度
|
||||
if (brightness !== undefined) {
|
||||
deviceDetail.value.lightBrightness = brightness.toString();
|
||||
}
|
||||
// 3.续航时间
|
||||
if (batteryTime !== undefined) {
|
||||
deviceDetail.value.batteryRemainingTime = batteryTime.toString();
|
||||
}
|
||||
break;
|
||||
case 12:
|
||||
// 灯光主键
|
||||
const lightModeIdA = getMainLightModeLabel(deviceState[1]); // 获取模式ID(如'strong')
|
||||
if (lightModeIdA !== 'unknown') {
|
||||
lightModes.value.forEach(mode => {
|
||||
const isActive = mode.id === lightModeIdA;
|
||||
mode.active = isActive;
|
||||
mode.switchStatus = isActive;
|
||||
});
|
||||
}
|
||||
// 激光
|
||||
const laserValue = deviceState[2];
|
||||
// 同步激光模式状态:1=开启(true),0=关闭(false)
|
||||
laserMode.value.active = laserValue === 1;
|
||||
laserMode.value.switchStatus = laserValue === 1;
|
||||
deviceDetail.value.batteryPercentage = deviceState[3]; //电量
|
||||
deviceDetail.value.batteryRemainingTime = deviceState[5]; //续航时间
|
||||
// getList(); // 重新获取设备详情
|
||||
if (deviceDetail.value.batteryPercentage < 20 && Number(deviceDetail.value.chargeState) == 0) {
|
||||
centerDialogVisible.value=true
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// 其他类型消息(不处理,仅打印)
|
||||
console.log('未处理的消息类型:', deviceState[0]);
|
||||
break;
|
||||
}
|
||||
} catch (e) {
|
||||
}
|
||||
};
|
||||
onMounted(async () => {
|
||||
await getList(); // 先获取设备信息
|
||||
// 连接mqtt
|
||||
onConnect(async () => {
|
||||
const deviceImei = deviceDetail.value.deviceImei;
|
||||
if (!deviceImei) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await subscribe(`A/${deviceImei}`, { qos: 1 });
|
||||
console.log('订阅成功');
|
||||
} catch (err) {
|
||||
console.error('订阅失败(onConnect内):', err);
|
||||
}
|
||||
});
|
||||
// 2. 注册消息接收回调(核心:处理设备发送的消息)
|
||||
onMessage((msg) => {
|
||||
console.log('收到新消息:', {
|
||||
主题: msg.topic,
|
||||
内容: msg.payload,
|
||||
时间: msg.time,
|
||||
QoS: msg.qos
|
||||
});
|
||||
// 在这里处理消息(根据实际业务逻辑)
|
||||
handleDeviceMessage(msg);
|
||||
});
|
||||
onError((err) => {
|
||||
console.error('MQTT连接失败原因:', err.message); // 关键:打印连接失败的具体原因
|
||||
});
|
||||
connect();
|
||||
});
|
||||
onUnmounted(() => {
|
||||
// 只有当连接已建立时,才执行断开操作(避免无效调用)
|
||||
if (connected.value) {
|
||||
console.log('页面离开,断开MQTT连接');
|
||||
disconnect(); // 调用断开连接方法
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
@ -513,6 +657,10 @@ onMounted(() => {
|
||||
.online {
|
||||
color: #00ff00;
|
||||
}
|
||||
|
||||
.offline {
|
||||
color: rgb(224, 52, 52);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -527,7 +675,7 @@ onMounted(() => {
|
||||
background: white;
|
||||
padding: 0px 20px 50px;
|
||||
border: 1px solid #ebeef5;
|
||||
height: 100%;
|
||||
height: 289px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
@ -710,9 +858,9 @@ onMounted(() => {
|
||||
}
|
||||
|
||||
.message-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
// display: flex;
|
||||
// flex-direction: column;
|
||||
// gap: 10px;
|
||||
|
||||
.el-textarea {
|
||||
border: 1px solid rgba(29, 111, 255, 1);
|
||||
@ -731,7 +879,9 @@ onMounted(() => {
|
||||
padding: 10px 20px;
|
||||
border-radius: 29px;
|
||||
background: rgba(2, 124, 251, 1);
|
||||
border: none
|
||||
border: none;
|
||||
margin: 20px 0px 30px 0;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -790,4 +940,9 @@ onMounted(() => {
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
.path-img {
|
||||
width: 52px;
|
||||
height: 28px;
|
||||
}
|
||||
</style>
|
||||
|
@ -2,20 +2,17 @@
|
||||
<div class="amap_page">
|
||||
<div ref="mapRef" class="amap-container"></div>
|
||||
<div class="content_top">
|
||||
<!-- 左侧列表内容保持不变 -->
|
||||
<div class="content_layout">
|
||||
<!-- 左侧设备列表(带复选框) -->
|
||||
<div class="device_list">
|
||||
<!-- 全选复选框 -->
|
||||
<div class="list_header">
|
||||
<div class="list_header" v-if="!isSingleDevice">
|
||||
<el-checkbox v-model="checkAll" @change="handleCheckAllChange" label="全选"></el-checkbox>
|
||||
</div>
|
||||
<div class="device_item" v-for="(device, index) in effectiveDeviceList" :key="device.id">
|
||||
<el-checkbox :value="device.id" :model-value="checkedDeviceIds.includes(device.id)"
|
||||
@update:model-value="(checked: boolean) => handleCheckboxChange(checked, device.id)"
|
||||
class="device_checkbox" :hidden="isSingleDevice" :disabled="device.onlineStatus==0"></el-checkbox>
|
||||
|
||||
<!-- 设备项(带复选框) -->
|
||||
<div class="device_item" v-for="(device, index) in props.deviceList" :key="index">
|
||||
<!-- 复选框 -->
|
||||
<el-checkbox :value="device.id" v-model="checkedDeviceIds" class="device_checkbox"></el-checkbox>
|
||||
|
||||
<!-- 设备信息 -->
|
||||
<div class="device_page">
|
||||
<div class="device_info">
|
||||
<div class="device_name">{{ device.deviceName }}</div>
|
||||
@ -25,124 +22,192 @@
|
||||
{{ device.onlineStatus === 1 ? '在线' : '离线' }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 控制按钮 -->
|
||||
<el-button class="control_btn" @click="handleControl(device)">控制</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="effectiveDeviceList.length === 0" class="nodata">
|
||||
<img src="@/assets/images/nodata.png" alt="" class="nodataImg">
|
||||
<div class="title">暂无数据</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {deviceVO } from '@/api/controlCenter/controlPanel/types';
|
||||
import { deviceVO } from '@/api/controlCenter/controlPanel/types';
|
||||
import { useRouter, useRoute } from 'vue-router';
|
||||
import strongLightDefault from '@/assets/images/position_ico.png';
|
||||
const props = defineProps({
|
||||
deviceList: {
|
||||
type: Array as PropType<deviceVO[]>, // 用PropType指定数组元素为DeviceItem
|
||||
type: Array as PropType<deviceVO[]>,
|
||||
required: false,
|
||||
default: () => [] // 数组/对象类型的默认值必须用函数返回,避免引用共享
|
||||
default: () => []
|
||||
}
|
||||
});
|
||||
console.log(props.deviceList);
|
||||
const router = useRouter();
|
||||
// 声明高德地图全局类型
|
||||
const route = useRoute();
|
||||
declare var AMap: any;
|
||||
// 地图实例
|
||||
// 核心状态
|
||||
const mapRef = ref<HTMLDivElement | null>(null);
|
||||
let mapInstance: any = null;
|
||||
// 复选框状态管理
|
||||
const checkedDeviceIds = ref(); // 存储选中的设备ID
|
||||
const checkAll = ref(false); // 全选状态
|
||||
// 全选/取消全选
|
||||
const handleCheckAllChange = (val: boolean) => {
|
||||
checkedDeviceIds.value = val
|
||||
? props.deviceList.map(device => device.id) // 全选:选中所有设备ID
|
||||
: []; // 取消全选:清空
|
||||
let infoWindow: any = null; // 信息窗口实例
|
||||
const singleDeviceId = ref<string | null>(null);
|
||||
const effectiveDeviceList = ref<deviceVO[]>([]);
|
||||
const checkedDeviceIds = ref<string[]>([]); // 明确为字符串数组
|
||||
const checkAll = ref(false);
|
||||
const isSingleDevice = ref(false);
|
||||
const activeDeviceName = ref(); // 当前激活的设备名称
|
||||
// 初始化设备ID
|
||||
const initSingleDeviceId = () => {
|
||||
const idFromParams = route.params.deviceId as string;
|
||||
const idFromQuery = route.query.deviceId as string;
|
||||
singleDeviceId.value = idFromParams || idFromQuery || null;
|
||||
isSingleDevice.value = !!singleDeviceId.value;
|
||||
console.log('单设备模式:', isSingleDevice.value, '设备ID:', singleDeviceId.value);
|
||||
};
|
||||
const initDeviceList = () => {
|
||||
if (!props.deviceList || !props.deviceList.length) {
|
||||
effectiveDeviceList.value = [];
|
||||
console.log('设备列表为空');
|
||||
return;
|
||||
}
|
||||
|
||||
// 监听单个复选框变化,更新全选状态
|
||||
watch(checkedDeviceIds, (newVal) => {
|
||||
checkAll.value = newVal.length === props.deviceList.length && props.deviceList.length > 0;
|
||||
});
|
||||
|
||||
// 设备控制事件
|
||||
const handleControl = (device: any) => {
|
||||
console.log('控制设备:', device);
|
||||
const deviceId = device.id;
|
||||
router.push('/controlCenter/6170/' + deviceId);
|
||||
};
|
||||
// 新增:获取地图中心坐标(优先用设备数据,无则用默认)
|
||||
const getCenterCoord = () => {
|
||||
// 1. 遍历设备列表,找第一个有有效经纬度的设备
|
||||
const validDevice = props.deviceList.find(
|
||||
(device:any) =>
|
||||
device.longitude !== undefined &&
|
||||
device.longitude !== null &&
|
||||
device.latitude !== undefined &&
|
||||
device.latitude !== null &&
|
||||
!isNaN(Number(device.longitude)) && // 确保是数字(避免字符串空值/非数字)
|
||||
!isNaN(Number(device.latitude))
|
||||
);
|
||||
|
||||
// 2. 有有效设备则用它的坐标,否则用默认值
|
||||
if (validDevice) {
|
||||
return [Number(validDevice.longitude), Number(validDevice.latitude)]; // 转数字防字符串坐标
|
||||
if (isSingleDevice.value && singleDeviceId.value) {
|
||||
const targetDevice = props.deviceList.find(
|
||||
device => String(device.id) === String(singleDeviceId.value)
|
||||
);
|
||||
if (targetDevice) {
|
||||
effectiveDeviceList.value = [targetDevice]; // 只显示匹配的单个设备
|
||||
checkedDeviceIds.value = [targetDevice.id];
|
||||
activeDeviceName.value = targetDevice.deviceName;
|
||||
} else {
|
||||
effectiveDeviceList.value = []; // 未找到匹配设备
|
||||
}
|
||||
} else {
|
||||
return [114.4074, 30.5928]; // 默认中心坐标
|
||||
// 多设备模式:显示所有设备
|
||||
effectiveDeviceList.value = props.deviceList;
|
||||
}
|
||||
};
|
||||
// 地图初始化(修改center配置)
|
||||
const initMap = () => {
|
||||
if (!window.AMap || !mapRef.value) return;
|
||||
// 2. 调用函数获取中心坐标(不再用固定值)
|
||||
const centerCoord = getCenterCoord();
|
||||
// 初始化地图(重点修复部分)
|
||||
const initMap = async () => {
|
||||
await nextTick();
|
||||
if (!window.AMap || !mapRef.value) {
|
||||
return;
|
||||
}
|
||||
// 销毁旧实例
|
||||
if (mapInstance) mapInstance.destroy();
|
||||
// 创建地图实例(确保容器DOM已渲染)
|
||||
mapInstance = new AMap.Map(mapRef.value, {
|
||||
center: centerCoord, // 改用动态获取的坐标
|
||||
zoom: 15,
|
||||
center: getCenterCoord(),
|
||||
zoom: isSingleDevice.value ? 8 : 8,
|
||||
resizeEnable: true
|
||||
});
|
||||
// 后续的设备打点逻辑不变...
|
||||
if (Array.isArray(props.deviceList)) {
|
||||
props.deviceList.forEach(device => {
|
||||
console.log(device, 'devicedevice');
|
||||
if (device.longitude && device.latitude) {
|
||||
new AMap.Marker({
|
||||
position: [device.longitude, device.latitude],
|
||||
title: device.deviceName,
|
||||
map: mapInstance
|
||||
});
|
||||
}
|
||||
|
||||
// 初始化信息窗口(用于显示设备名称)
|
||||
infoWindow = new AMap.InfoWindow({
|
||||
offset: new AMap.Pixel(0, -30) // 偏移量,避免遮挡标记
|
||||
});
|
||||
|
||||
// 渲染标记
|
||||
renderMarkers();
|
||||
};
|
||||
|
||||
// 获取中心坐标(确保数字类型)
|
||||
const getCenterCoord = () => {
|
||||
if (!effectiveDeviceList.value.length) {
|
||||
return [114.4074, 30.5928];
|
||||
}
|
||||
// 遍历找第一个有效坐标
|
||||
const validDevice = effectiveDeviceList.value.find(device => {
|
||||
const lng = Number(device.longitude);
|
||||
const lat = Number(device.latitude);
|
||||
return !isNaN(lng) && !isNaN(lat) && lng !== 0 && lat !== 0;
|
||||
});
|
||||
if (validDevice) {
|
||||
const lng = Number(validDevice.longitude);
|
||||
const lat = Number(validDevice.latitude);
|
||||
return [lng, lat];
|
||||
}
|
||||
// 无有效坐标时用默认
|
||||
return [114.4074, 30.5928];
|
||||
};
|
||||
|
||||
// 渲染标记(核心修复点)
|
||||
const renderMarkers = () => {
|
||||
if (!mapInstance || !effectiveDeviceList.value.length) return;
|
||||
mapInstance.clearMap();
|
||||
effectiveDeviceList.value.forEach(device => {
|
||||
const lng = device.longitude !== null && device.longitude !== undefined ? Number(device.longitude) : NaN;
|
||||
const lat = device.latitude !== null && device.latitude !== undefined ? Number(device.latitude) : NaN;
|
||||
if (isNaN(lng) || isNaN(lat) || lng === 0 || lat === 0) {
|
||||
return;
|
||||
}
|
||||
// 3. 创建标记(确保绑定到地图实例)
|
||||
const marker = new AMap.Marker({
|
||||
position: [lng, lat],
|
||||
title: device.deviceName,
|
||||
map: mapInstance,
|
||||
icon: new AMap.Icon({
|
||||
size: new AMap.Size(32, 32), // Marker显示尺寸
|
||||
image: strongLightDefault,
|
||||
imageSize: new AMap.Size(32, 32)
|
||||
})
|
||||
});
|
||||
|
||||
// 4. 标记点击事件
|
||||
marker.on('click', () => {
|
||||
activeDeviceName.value = device.deviceName;
|
||||
infoWindow.setContent(`<div style="padding:8px">${device.deviceName}</div>`);
|
||||
infoWindow.open(mapInstance, [lng, lat]);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// 全选处理
|
||||
const handleCheckAllChange = (val: boolean) => {
|
||||
if (isSingleDevice.value) return;
|
||||
checkedDeviceIds.value = val ? effectiveDeviceList.value.map(d => d.id) : [];
|
||||
};
|
||||
|
||||
// 处理单个复选框变更
|
||||
const handleCheckboxChange = (checked: boolean, deviceId: string) => {
|
||||
if (isSingleDevice.value) return;
|
||||
if (checked) {
|
||||
if (!checkedDeviceIds.value.includes(deviceId)) {
|
||||
checkedDeviceIds.value.push(deviceId);
|
||||
}
|
||||
} else {
|
||||
const index = checkedDeviceIds.value.indexOf(deviceId);
|
||||
if (index > -1) {
|
||||
checkedDeviceIds.value.splice(index, 1);
|
||||
}
|
||||
}
|
||||
};
|
||||
watch(
|
||||
() => props.deviceList, // 监听props中的deviceList
|
||||
(newDeviceList) => {
|
||||
if (!mapInstance || !Array.isArray(newDeviceList)) return;
|
||||
// 1. 清除地图上已有的所有标记(避免重复打点)
|
||||
mapInstance.clearMap();
|
||||
// 2. 重新添加新的设备标记
|
||||
newDeviceList.forEach((device:any) => {
|
||||
console.log(device, 'device');
|
||||
// 确保设备有经纬度(lng和lat),避免无效打点
|
||||
if (device.longitude && device.latitude) {
|
||||
new AMap.Marker({
|
||||
position: [device.longitude, device.latitude],
|
||||
title: device.deviceName, // 用设备名当标题(匹配父组件字段)
|
||||
map: mapInstance,
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
{ deep: true } // 深度监听(如果设备列表里的子对象变化也能触发)
|
||||
);
|
||||
|
||||
// 控制按钮点击
|
||||
const handleControl = (device: deviceVO) => {
|
||||
router.push(`/controlCenter/6170/${device.id}`);
|
||||
};
|
||||
|
||||
// 监听路由和设备列表变化
|
||||
watch(() => [route.params, route.query], () => {
|
||||
initSingleDeviceId();
|
||||
initDeviceList();
|
||||
initMap();
|
||||
}, { deep: true });
|
||||
|
||||
watch(() => props.deviceList, () => {
|
||||
initDeviceList();
|
||||
renderMarkers();
|
||||
}, { deep: true });
|
||||
|
||||
// 初始化
|
||||
onMounted(() => {
|
||||
initMap();
|
||||
initSingleDeviceId();
|
||||
initDeviceList();
|
||||
initMap();
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
@ -151,7 +216,7 @@ onUnmounted(() => {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 地图容器 */
|
||||
/* 原有样式保持不变 */
|
||||
.amap_page {
|
||||
position: relative;
|
||||
}
|
||||
@ -163,6 +228,7 @@ onUnmounted(() => {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* 设备名称提示框 */
|
||||
.content_top {
|
||||
width: 210px;
|
||||
border-radius: 4px;
|
||||
@ -175,31 +241,25 @@ onUnmounted(() => {
|
||||
left: 10px
|
||||
}
|
||||
|
||||
/* 全选行样式 */
|
||||
/* 其他样式保持不变... */
|
||||
.list_header {
|
||||
padding: 12px;
|
||||
border-bottom: 1px solid #eee;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* 设备项样式 */
|
||||
.device_item {
|
||||
padding: 0px 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
/* 复选框与内容间距 */
|
||||
cursor: default;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
|
||||
/* 复选框样式 */
|
||||
.device_checkbox {
|
||||
flex-shrink: 0;
|
||||
/* 复选框不压缩 */
|
||||
}
|
||||
|
||||
/* 设备信息区域 */
|
||||
.device_page {
|
||||
background-color: rgba(247, 248, 252, 1);
|
||||
margin-bottom: 5px;
|
||||
@ -235,7 +295,6 @@ onUnmounted(() => {
|
||||
color: #f53f3f;
|
||||
}
|
||||
|
||||
/* 控制按钮 */
|
||||
.control_btn {
|
||||
font-size: 12px;
|
||||
padding: 0px 15px;
|
||||
@ -247,4 +306,16 @@ onUnmounted(() => {
|
||||
color: rgba(2, 124, 251, 1);
|
||||
border: none;
|
||||
}
|
||||
.nodata{
|
||||
transform: translate(-1%,100%);
|
||||
left:50%;
|
||||
height: 30vh;
|
||||
}
|
||||
.nodataImg{
|
||||
width: 130px;
|
||||
}
|
||||
.title{
|
||||
color: #666;
|
||||
padding-top: 20px;
|
||||
}
|
||||
</style>
|
||||
|
@ -45,8 +45,8 @@
|
||||
<el-form-item label="设备名称" prop="deviceName">
|
||||
<el-input v-model="queryParams.deviceName" placeholder="请输入设备名称" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item label="设备状态" prop="deviceStatus">
|
||||
<el-select v-model="queryParams.deviceStatus" placeholder="设备状态" clearable>
|
||||
<el-form-item label="设备状态" prop="onlineStatus">
|
||||
<el-select v-model="queryParams.onlineStatus" placeholder="设备状态" clearable>
|
||||
<el-option label="在线" value="1"></el-option>
|
||||
<el-option label="离线" value="0"></el-option>
|
||||
</el-select>
|
||||
@ -81,7 +81,7 @@
|
||||
<el-card class="Maplist">
|
||||
<div v-if="isListView" key="list">
|
||||
<el-table v-loading="loading" border :data="deviceList" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="50" align="center" />
|
||||
<el-table-column type="selection" width="50" align="center" :selectable="isSelectable" />
|
||||
<el-table-column label="设备名称" align="center" prop="deviceName" />
|
||||
<el-table-column label="设备图片" align="center" prop="devicePic">
|
||||
<template #default="scope">
|
||||
@ -104,13 +104,24 @@
|
||||
</el-table-column>
|
||||
<el-table-column label="设备IMEI" align="center" prop="deviceImei" />
|
||||
<el-table-column label="使用人员" align="center" prop="personnelBy" />
|
||||
<el-table-column label="电量" align="center" prop="battery" />
|
||||
<el-table-column label="设备状态" align="center" prop="onlineStatus">
|
||||
<template #default="scope">
|
||||
<div class="normal green" v-if="scope.row.onlineStatus == 1">在线</div>
|
||||
<div class="normal red" v-if="scope.row.onlineStatus == 0">离线</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="电量" align="center" prop="battery">
|
||||
<template #default="scope">
|
||||
<div :class="{
|
||||
'battery-red': Number(scope.row.battery) < 20,
|
||||
'battery-yellow': Number(scope.row.battery) >= 20 && Number(scope.row.battery) < 80,
|
||||
'battery-green': Number(scope.row.battery) >= 80 && Number(scope.row.battery) <= 100
|
||||
}">
|
||||
{{ scope.row.battery }}%
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="操作" fixed="right" width="180" class-name="small-padding fixed-width">
|
||||
<template #default="scope">
|
||||
<el-button link type="primary" @click="handleControl(scope.row)">控制面板</el-button>
|
||||
@ -127,11 +138,11 @@
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-dialog v-model="dialog.visible" :title="dialog.title" width="500px" append-to-body>
|
||||
<div class="title_ps">{{ `对${arrayDeviceName.join('、')}设备发送消息` }}</div>
|
||||
<el-form ref="postFormRef" :model="form" label-width="80px">
|
||||
<el-form-item label="发送信息" prop="messageToSend">
|
||||
<el-input type="textarea" v-model="form.messageToSend" placeholder="请输入内容" />
|
||||
</el-form-item>
|
||||
<el-input type="textarea" v-model="form.messageToSend" placeholder="请输入消息内容" />
|
||||
</el-form>
|
||||
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button type="primary" @click="submitForm" :loading="sendTextLoading"
|
||||
@ -181,7 +192,7 @@ const initData: PageData<'', deviceQuery> = {
|
||||
pageSize: 10,
|
||||
deviceId: '',
|
||||
deviceName: '',
|
||||
deviceStatus: '',
|
||||
onlineStatus: '',
|
||||
deviceMac: '',
|
||||
deviceImei: '',
|
||||
personnelBy: '',
|
||||
@ -207,8 +218,12 @@ const switchView = (view: 'list' | 'map') => {
|
||||
};
|
||||
const dialog = reactive<DialogOption>({
|
||||
visible: false,
|
||||
title: ''
|
||||
title: '发送信息'
|
||||
});
|
||||
const isSelectable = (row: any) => {
|
||||
// 仅当在线状态(onlineStatus == 1)时允许选中
|
||||
return row.onlineStatus === 1;
|
||||
}
|
||||
/** 通过条件过滤节点 */
|
||||
const filterNode = (value: string, data: any) => {
|
||||
if (!value) return true;
|
||||
@ -284,14 +299,20 @@ const resetQuery = () => {
|
||||
};
|
||||
|
||||
|
||||
/** 设备控制跳转 */
|
||||
/** 设备控制跳转动态取detailPageUrl字段值,跳转 */
|
||||
const handleControl = (row: any) => {
|
||||
const deviceId = row.id;
|
||||
router.push('/controlCenter/6170/' + deviceId);
|
||||
const detailPageUrl = row.detailPageUrl;
|
||||
const basePath = detailPageUrl.replace(/\/index$/, '');
|
||||
const dynamicPath = `/${basePath}/${deviceId}`;
|
||||
router.push(dynamicPath); // 跳转路由
|
||||
};
|
||||
|
||||
/** 选择条数 */
|
||||
const arrayDeviceName = ref([])
|
||||
const handleSelectionChange = (selection: deviceVO[]) => {
|
||||
console.log(selection, 'selectionselection');
|
||||
arrayDeviceName.value = selection.map(item => item.deviceName);
|
||||
ids.value = selection;
|
||||
single.value = selection.length != 1;
|
||||
multiple.value = !selection.length;
|
||||
@ -336,10 +357,12 @@ const getDeptTree = async () => {
|
||||
const sendTextMessage = () => {
|
||||
// 防重复提交
|
||||
if (!queryParams.value.deviceType) {
|
||||
ElMessage.closeAll();
|
||||
proxy?.$modal.msgWarning('请先选择设备类型');
|
||||
return;
|
||||
}
|
||||
if (ids.value.length == 0) {
|
||||
ElMessage.closeAll();
|
||||
proxy?.$modal.msgWarning('请先选中一条设备');
|
||||
return;
|
||||
}
|
||||
@ -348,6 +371,7 @@ const sendTextMessage = () => {
|
||||
// 发送文本消息确认
|
||||
const submitForm = async () => {
|
||||
if (!form.value.messageToSend) {
|
||||
ElMessage.closeAll();
|
||||
proxy?.$modal.msgWarning('发送消息不能为空');
|
||||
return
|
||||
}
|
||||
@ -397,20 +421,23 @@ const submitForm = async () => {
|
||||
};
|
||||
// 取消
|
||||
const cancel = () => {
|
||||
dialog.visible = true;
|
||||
dialog.visible = false;
|
||||
form.value.messageToSend = ''
|
||||
}
|
||||
// 强制报警
|
||||
const forceAlarm = async () => {
|
||||
if (!queryParams.value.deviceType) {
|
||||
ElMessage.closeAll();
|
||||
proxy?.$modal.msgWarning('请先选择设备类型');
|
||||
return;
|
||||
}
|
||||
if (ids.value.length == 0) {
|
||||
ElMessage.closeAll();
|
||||
proxy?.$modal.msgWarning('请先选中一条设备');
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await proxy?.$modal.confirm('确定要对所选设备开启强制报警?', '强制报警');
|
||||
forceAlarmLoading.value = true
|
||||
// 2. 准备请求数据
|
||||
const batchId = generateShortId();
|
||||
@ -465,6 +492,25 @@ const forceAlarm = async () => {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.battery-red {
|
||||
color: rgba(224, 52, 52, 1);
|
||||
}
|
||||
|
||||
/* 20%~80% 黄色(或橙色,根据设计图调整) */
|
||||
.battery-yellow {
|
||||
color: rgba(234, 152, 0, 1);
|
||||
}
|
||||
|
||||
/* 80%~100% 绿色 */
|
||||
.battery-green {
|
||||
color: rgba(234, 152, 0, 1);
|
||||
}
|
||||
|
||||
.title_ps {
|
||||
color: rgba(224, 52, 52, 1);
|
||||
padding: 0px 0 20px 0;
|
||||
}
|
||||
|
||||
.main-tree {
|
||||
border-radius: 4px;
|
||||
box-shadow: 0px 0px 6px 0px rgba(0, 34, 96, 0.1);
|
||||
|
@ -15,11 +15,10 @@
|
||||
<el-card>
|
||||
<!-- =========搜索按钮操作======= -->
|
||||
<div class="btn_search">
|
||||
<el-button type="primary" plain icon="Download"
|
||||
@click="handleExport">导出</el-button>
|
||||
<el-button type="primary" plain icon="Download" @click="handleExport">导出</el-button>
|
||||
<div style="position: absolute; right:30px; top:20px">
|
||||
<el-input v-model="queryParams.content" placeholder="MAC/IMEI" clearable
|
||||
style="width: 200px; margin-right: 20px;" @keyup.enter="handleQuery" @input="handleInput" />
|
||||
style="width: 200px; margin-right: 20px;" @keyup.enter="handleQuery" @input="handleInput" />
|
||||
<el-button type="primary" plain @click="toggleFilter">高级筛选</el-button>
|
||||
</div>
|
||||
</div>
|
||||
@ -109,7 +108,7 @@ const initData: PageData<'', deviceQuery> = {
|
||||
deviceType: '',
|
||||
startTime: '',
|
||||
endTime: '',
|
||||
content:""
|
||||
content: ""
|
||||
},
|
||||
rules: undefined,
|
||||
form: ''
|
||||
|
@ -50,14 +50,24 @@
|
||||
<script setup lang="ts">
|
||||
import api from "@/api/controlCenter/historyjectory/index"
|
||||
import { formatTimestampToHM } from "@/utils/function"
|
||||
import { useRoute } from "vue-router"
|
||||
import strongLightDefault from '@/assets/images/position_ico.png';
|
||||
// 添加AMap类型声明
|
||||
declare global {
|
||||
interface Window {
|
||||
AMap: any;
|
||||
}
|
||||
}
|
||||
|
||||
declare var AMap: any;
|
||||
const route = useRoute();
|
||||
// -------------------------- 状态管理 --------------------------
|
||||
// 地图实例
|
||||
const mapRef = ref<AMap.Map | null>(null);
|
||||
const mapRef = ref<any>(null);
|
||||
// 轨迹线实例
|
||||
const polylineRef = ref<AMap.Polyline | null>(null);
|
||||
const polylineRef = ref<any>(null);
|
||||
// 移动标记点实例
|
||||
const markerRef = ref<AMap.Marker | null>(null);
|
||||
const markerRef = ref<any>(null);
|
||||
// 轨迹数据(初始为空,从设备列表动态获取)
|
||||
const trackPoints = ref<[number, number][]>([]); // 移除硬编码默认值
|
||||
// 设备分组数据(包含各设备的轨迹点)
|
||||
@ -92,21 +102,19 @@ const initMap = () => {
|
||||
polylineRef.value = polyline;
|
||||
// 创建标记点(初始位置临时设为 [0, 0],播放时会覆盖)
|
||||
const marker = new AMap.Marker({
|
||||
position: [0, 0],
|
||||
position: [20, 20],
|
||||
icon: new AMap.Icon({
|
||||
size: new AMap.Size(40, 40),
|
||||
image: 'https://webapi.amap.com/images/car.png',
|
||||
image: strongLightDefault,
|
||||
imageSize: new AMap.Size(40, 40),
|
||||
}),
|
||||
anchor: 'center',
|
||||
});
|
||||
map.add(marker);
|
||||
markerRef.value = marker;
|
||||
|
||||
isLoading.value = false;
|
||||
fetchTrackData(); // 数据请求移到地图初始化后
|
||||
} catch (error) {
|
||||
console.error('地图初始化失败:', error);
|
||||
isLoading.value = false;
|
||||
}
|
||||
};
|
||||
@ -208,10 +216,10 @@ const handlePlayPause = (group: any) => {
|
||||
return isNaN(lng) || isNaN(lat) ? null : [lng, lat] as [number, number];
|
||||
}).filter((point: any) => point !== null); // 过滤无效点
|
||||
|
||||
if (validTrackPoints.length < 2) {
|
||||
ElMessage.warning('有效轨迹点不足2个,无法播放');
|
||||
return;
|
||||
}
|
||||
// if (validTrackPoints.length < 2) {
|
||||
// ElMessage.warning('有效轨迹点不足2个,无法播放');
|
||||
// return;
|
||||
// }
|
||||
|
||||
// 3. 重置播放状态并赋值轨迹数据
|
||||
resetPlayState();
|
||||
@ -254,11 +262,11 @@ watch(trackPoints, (newPoints) => {
|
||||
|
||||
<style scoped>
|
||||
/* 样式保持不变 */
|
||||
:deep .el-timeline-item__tail {
|
||||
:deep(.el-timeline-item__tail) {
|
||||
border-left: 1px solid rgba(0, 198, 250, 0.2);
|
||||
}
|
||||
|
||||
:deep .el-timeline-item__timestamp {
|
||||
:deep(.el-timeline-item__timestamp) {
|
||||
color: rgba(2, 124, 251, 1);
|
||||
font-size: 16px;
|
||||
}
|
||||
@ -446,4 +454,4 @@ watch(trackPoints, (newPoints) => {
|
||||
color: #666;
|
||||
padding-top: 20px;
|
||||
}
|
||||
</style>
|
||||
</style>
|
@ -1,254 +0,0 @@
|
||||
<template>
|
||||
<div class="p-2">
|
||||
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
|
||||
<div v-show="showSearch" class="mb-[10px]">
|
||||
<el-card shadow="hover">
|
||||
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
|
||||
<el-form-item label="部门id" prop="deptId">
|
||||
<el-input v-model="queryParams.deptId" placeholder="请输入部门id" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="用户id" prop="userId">
|
||||
<el-input v-model="queryParams.userId" placeholder="请输入用户id" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="排序号" prop="orderNum">
|
||||
<el-input v-model="queryParams.orderNum" placeholder="请输入排序号" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="key键" prop="testKey">
|
||||
<el-input v-model="queryParams.testKey" placeholder="请输入key键" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="值" prop="value">
|
||||
<el-input v-model="queryParams.value" placeholder="请输入值" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
</div>
|
||||
</transition>
|
||||
|
||||
<el-card shadow="hover">
|
||||
<template #header>
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button v-hasPermi="['demo:demo:add']" type="primary" plain icon="Plus" @click="handleAdd">新增</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button v-hasPermi="['demo:demo:edit']" type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()">修改</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button v-hasPermi="['demo:demo:remove']" type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()"
|
||||
>删除</el-button
|
||||
>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button v-hasPermi="['demo:demo:export']" type="warning" plain icon="Download" @click="handleExport">导出</el-button>
|
||||
</el-col>
|
||||
<right-toolbar v-model:show-search="showSearch" @query-table="getList"></right-toolbar>
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
<el-table v-loading="loading" border :data="demoList" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column v-if="true" label="主键" align="center" prop="id" />
|
||||
<el-table-column label="部门id" align="center" prop="deptId" />
|
||||
<el-table-column label="用户id" align="center" prop="userId" />
|
||||
<el-table-column label="排序号" align="center" prop="orderNum" />
|
||||
<el-table-column label="key键" align="center" prop="testKey" />
|
||||
<el-table-column label="值" align="center" prop="value" />
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template #default="scope">
|
||||
<el-tooltip content="修改" placement="top">
|
||||
<el-button v-hasPermi="['demo:demo:edit']" link type="primary" icon="Edit" @click="handleUpdate(scope.row)"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="删除" placement="top">
|
||||
<el-button v-hasPermi="['demo:demo:remove']" link type="primary" icon="Delete" @click="handleDelete(scope.row)"></el-button>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination v-show="total > 0" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" :total="total" @pagination="getList" />
|
||||
</el-card>
|
||||
<!-- 添加或修改测试单对话框 -->
|
||||
<el-dialog v-model="dialog.visible" :title="dialog.title" width="500px" append-to-body>
|
||||
<el-form ref="demoFormRef" :model="form" :rules="rules" label-width="80px">
|
||||
<el-form-item label="部门id" prop="deptId">
|
||||
<el-input v-model="form.deptId" placeholder="请输入部门id" />
|
||||
</el-form-item>
|
||||
<el-form-item label="用户id" prop="userId">
|
||||
<el-input v-model="form.userId" placeholder="请输入用户id" />
|
||||
</el-form-item>
|
||||
<el-form-item label="排序号" prop="orderNum">
|
||||
<el-input v-model="form.orderNum" placeholder="请输入排序号" />
|
||||
</el-form-item>
|
||||
<el-form-item label="key键" prop="testKey">
|
||||
<el-input v-model="form.testKey" placeholder="请输入key键" />
|
||||
</el-form-item>
|
||||
<el-form-item label="值" prop="value">
|
||||
<el-input v-model="form.value" placeholder="请输入值" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button :loading="buttonLoading" type="primary" @click="submitForm">确 定</el-button>
|
||||
<el-button @click="cancel">取 消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="Demo" lang="ts">
|
||||
import { listDemo, getDemo, delDemo, addDemo, updateDemo } from '@/api/demo/demo';
|
||||
import { DemoVO, DemoQuery, DemoForm } from '@/api/demo/demo/types';
|
||||
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
|
||||
const demoList = ref<DemoVO[]>([]);
|
||||
const buttonLoading = ref(false);
|
||||
const loading = ref(true);
|
||||
const showSearch = ref(true);
|
||||
const ids = ref<Array<string | number>>([]);
|
||||
const single = ref(true);
|
||||
const multiple = ref(true);
|
||||
const total = ref(0);
|
||||
|
||||
const queryFormRef = ref<ElFormInstance>();
|
||||
const demoFormRef = ref<ElFormInstance>();
|
||||
|
||||
const dialog = reactive<DialogOption>({
|
||||
visible: false,
|
||||
title: ''
|
||||
});
|
||||
|
||||
const initFormData: DemoForm = {
|
||||
id: undefined,
|
||||
deptId: undefined,
|
||||
userId: undefined,
|
||||
orderNum: undefined,
|
||||
testKey: undefined,
|
||||
value: undefined
|
||||
};
|
||||
const data = reactive<PageData<DemoForm, DemoQuery>>({
|
||||
form: { ...initFormData },
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
deptId: undefined,
|
||||
userId: undefined,
|
||||
orderNum: undefined,
|
||||
testKey: undefined,
|
||||
value: undefined
|
||||
},
|
||||
rules: {
|
||||
id: [{ required: true, message: '主键不能为空', trigger: 'blur' }],
|
||||
deptId: [{ required: true, message: '部门id不能为空', trigger: 'blur' }],
|
||||
userId: [{ required: true, message: '用户id不能为空', trigger: 'blur' }],
|
||||
orderNum: [{ required: true, message: '排序号不能为空', trigger: 'blur' }],
|
||||
testKey: [{ required: true, message: 'key键不能为空', trigger: 'blur' }],
|
||||
value: [{ required: true, message: '值不能为空', trigger: 'blur' }]
|
||||
}
|
||||
});
|
||||
|
||||
const { queryParams, form, rules } = toRefs(data);
|
||||
|
||||
/** 查询测试单列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true;
|
||||
const res = await listDemo(queryParams.value);
|
||||
demoList.value = res.rows;
|
||||
total.value = res.total;
|
||||
loading.value = false;
|
||||
};
|
||||
|
||||
/** 取消按钮 */
|
||||
const cancel = () => {
|
||||
reset();
|
||||
dialog.visible = false;
|
||||
};
|
||||
|
||||
/** 表单重置 */
|
||||
const reset = () => {
|
||||
form.value = { ...initFormData };
|
||||
demoFormRef.value?.resetFields();
|
||||
};
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.value.pageNum = 1;
|
||||
getList();
|
||||
};
|
||||
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = () => {
|
||||
queryFormRef.value?.resetFields();
|
||||
handleQuery();
|
||||
};
|
||||
|
||||
/** 多选框选中数据 */
|
||||
const handleSelectionChange = (selection: DemoVO[]) => {
|
||||
ids.value = selection.map((item) => item.id);
|
||||
single.value = selection.length != 1;
|
||||
multiple.value = !selection.length;
|
||||
};
|
||||
|
||||
/** 新增按钮操作 */
|
||||
const handleAdd = () => {
|
||||
reset();
|
||||
dialog.visible = true;
|
||||
dialog.title = '添加测试单';
|
||||
};
|
||||
|
||||
/** 修改按钮操作 */
|
||||
const handleUpdate = async (row?: DemoVO) => {
|
||||
reset();
|
||||
const _id = row?.id || ids.value[0];
|
||||
const res = await getDemo(_id);
|
||||
Object.assign(form.value, res.data);
|
||||
dialog.visible = true;
|
||||
dialog.title = '修改测试单';
|
||||
};
|
||||
|
||||
/** 提交按钮 */
|
||||
const submitForm = () => {
|
||||
demoFormRef.value?.validate(async (valid: boolean) => {
|
||||
if (valid) {
|
||||
buttonLoading.value = true;
|
||||
if (form.value.id) {
|
||||
await updateDemo(form.value).finally(() => (buttonLoading.value = false));
|
||||
} else {
|
||||
await addDemo(form.value).finally(() => (buttonLoading.value = false));
|
||||
}
|
||||
proxy?.$modal.msgSuccess('修改成功');
|
||||
dialog.visible = false;
|
||||
await getList();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/** 删除按钮操作 */
|
||||
const handleDelete = async (row?: DemoVO) => {
|
||||
const _ids = row?.id || ids.value;
|
||||
await proxy?.$modal.confirm('是否确认删除测试单编号为"' + _ids + '"的数据项?').finally(() => (loading.value = false));
|
||||
await delDemo(_ids);
|
||||
proxy?.$modal.msgSuccess('删除成功');
|
||||
await getList();
|
||||
};
|
||||
|
||||
/** 导出按钮操作 */
|
||||
const handleExport = () => {
|
||||
proxy?.download(
|
||||
'demo/demo/export',
|
||||
{
|
||||
...queryParams.value
|
||||
},
|
||||
`demo_${new Date().getTime()}.xlsx`
|
||||
);
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getList();
|
||||
});
|
||||
</script>
|
@ -1,259 +0,0 @@
|
||||
<template>
|
||||
<div class="p-2">
|
||||
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
|
||||
<div v-show="showSearch" class="mb-[10px]">
|
||||
<el-card shadow="hover">
|
||||
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
|
||||
<el-form-item label="树节点名" prop="treeName">
|
||||
<el-input v-model="queryParams.treeName" placeholder="请输入树节点名" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
</div>
|
||||
</transition>
|
||||
|
||||
<el-card shadow="hover">
|
||||
<template #header>
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button v-hasPermi="['demo:tree:add']" type="primary" plain icon="Plus" @click="handleAdd()">新增</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="info" plain icon="Sort" @click="handleToggleExpandAll">展开/折叠</el-button>
|
||||
</el-col>
|
||||
<right-toolbar v-model:show-search="showSearch" @query-table="getList"></right-toolbar>
|
||||
</el-row>
|
||||
</template>
|
||||
<el-table
|
||||
ref="treeTableRef"
|
||||
v-loading="loading"
|
||||
:data="treeList"
|
||||
row-key="id"
|
||||
border
|
||||
:default-expand-all="isExpandAll"
|
||||
:tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
|
||||
>
|
||||
<el-table-column label="父id" align="center" prop="parentId" />
|
||||
<el-table-column label="部门id" align="center" prop="deptId" />
|
||||
<el-table-column label="用户id" align="center" prop="userId" />
|
||||
<el-table-column label="树节点名" align="center" prop="treeName" />
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template #default="scope">
|
||||
<el-tooltip content="修改" placement="top">
|
||||
<el-button v-hasPermi="['demo:tree:edit']" link type="primary" icon="Edit" @click="handleUpdate(scope.row)" />
|
||||
</el-tooltip>
|
||||
<el-tooltip content="新增" placement="top">
|
||||
<el-button v-hasPermi="['demo:tree:add']" link type="primary" icon="Plus" @click="handleAdd(scope.row)" />
|
||||
</el-tooltip>
|
||||
<el-tooltip content="删除" placement="top">
|
||||
<el-button v-hasPermi="['demo:tree:remove']" link type="primary" icon="Delete" @click="handleDelete(scope.row)" />
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
<!-- 添加或修改测试树对话框 -->
|
||||
<el-dialog v-model="dialog.visible" :title="dialog.title" width="500px" append-to-body>
|
||||
<el-form ref="treeFormRef" :model="form" :rules="rules" label-width="80px">
|
||||
<el-form-item label="父id" prop="parentId">
|
||||
<el-tree-select
|
||||
v-model="form.parentId"
|
||||
:data="treeOptions"
|
||||
:props="{ value: 'id', label: 'treeName', children: 'children' } as any"
|
||||
value-key="id"
|
||||
placeholder="请选择父id"
|
||||
check-strictly
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="部门id" prop="deptId">
|
||||
<el-input v-model="form.deptId" placeholder="请输入部门id" />
|
||||
</el-form-item>
|
||||
<el-form-item label="用户id" prop="userId">
|
||||
<el-input v-model="form.userId" placeholder="请输入用户id" />
|
||||
</el-form-item>
|
||||
<el-form-item label="值" prop="treeName">
|
||||
<el-input v-model="form.treeName" placeholder="请输入值" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button :loading="buttonLoading" type="primary" @click="submitForm">确 定</el-button>
|
||||
<el-button @click="cancel">取 消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="Tree" lang="ts">
|
||||
import { listTree, getTree, delTree, addTree, updateTree } from '@/api/demo/tree';
|
||||
import { TreeVO, TreeQuery, TreeForm } from '@/api/demo/tree/types';
|
||||
|
||||
type TreeOption = {
|
||||
id: number;
|
||||
treeName: string;
|
||||
children?: TreeOption[];
|
||||
};
|
||||
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
|
||||
const treeList = ref<TreeVO[]>([]);
|
||||
const treeOptions = ref<TreeOption[]>([]);
|
||||
const buttonLoading = ref(false);
|
||||
const showSearch = ref(true);
|
||||
const isExpandAll = ref(true);
|
||||
const loading = ref(false);
|
||||
|
||||
const queryFormRef = ref<ElFormInstance>();
|
||||
const treeFormRef = ref<ElFormInstance>();
|
||||
const treeTableRef = ref<ElTableInstance>();
|
||||
|
||||
const dialog = reactive<DialogOption>({
|
||||
visible: false,
|
||||
title: ''
|
||||
});
|
||||
|
||||
const initFormData: TreeForm = {
|
||||
id: undefined,
|
||||
parentId: undefined,
|
||||
deptId: undefined,
|
||||
userId: undefined,
|
||||
treeName: undefined
|
||||
};
|
||||
|
||||
const data = reactive<PageData<TreeForm, TreeQuery>>({
|
||||
form: { ...initFormData },
|
||||
queryParams: {
|
||||
parentId: undefined,
|
||||
deptId: undefined,
|
||||
userId: undefined,
|
||||
treeName: undefined
|
||||
},
|
||||
rules: {
|
||||
id: [{ required: true, message: '主键不能为空', trigger: 'blur' }],
|
||||
parentId: [{ required: true, message: '父id不能为空', trigger: 'blur' }],
|
||||
deptId: [{ required: true, message: '部门id不能为空', trigger: 'blur' }],
|
||||
userId: [{ required: true, message: '用户id不能为空', trigger: 'blur' }],
|
||||
treeName: [{ required: true, message: '值不能为空', trigger: 'blur' }]
|
||||
}
|
||||
});
|
||||
|
||||
const { queryParams, form, rules } = toRefs(data);
|
||||
|
||||
/** 查询测试树列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true;
|
||||
const res = await listTree(queryParams.value);
|
||||
const data = proxy?.handleTree<TreeVO>(res.data, 'id', 'parentId');
|
||||
if (data) {
|
||||
treeList.value = data;
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
/** 查询测试树下拉树结构 */
|
||||
const getTreeselect = async () => {
|
||||
const res = await listTree();
|
||||
treeOptions.value = [];
|
||||
const data: TreeOption = { id: 0, treeName: '顶级节点', children: [] };
|
||||
data.children = proxy?.handleTree<TreeOption>(res.data, 'id', 'parentId');
|
||||
treeOptions.value.push(data);
|
||||
};
|
||||
|
||||
// 取消按钮
|
||||
const cancel = () => {
|
||||
reset();
|
||||
dialog.visible = false;
|
||||
};
|
||||
|
||||
// 表单重置
|
||||
const reset = () => {
|
||||
form.value = { ...initFormData };
|
||||
treeFormRef.value?.resetFields();
|
||||
};
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
getList();
|
||||
};
|
||||
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = () => {
|
||||
queryFormRef.value?.resetFields();
|
||||
handleQuery();
|
||||
};
|
||||
|
||||
/** 新增按钮操作 */
|
||||
const handleAdd = (row?: TreeVO) => {
|
||||
reset();
|
||||
getTreeselect();
|
||||
if (row && row.id) {
|
||||
form.value.parentId = row.id;
|
||||
} else {
|
||||
form.value.parentId = 0;
|
||||
}
|
||||
dialog.visible = true;
|
||||
dialog.title = '添加测试树';
|
||||
};
|
||||
|
||||
/** 展开/折叠操作 */
|
||||
const handleToggleExpandAll = () => {
|
||||
isExpandAll.value = !isExpandAll.value;
|
||||
toggleExpandAll(treeList.value, isExpandAll.value);
|
||||
};
|
||||
|
||||
/** 展开/折叠操作 */
|
||||
const toggleExpandAll = (data: TreeVO[], status: boolean) => {
|
||||
data.forEach((item) => {
|
||||
treeTableRef.value?.toggleRowExpansion(item, status);
|
||||
if (item.children && item.children.length > 0) toggleExpandAll(item.children, status);
|
||||
});
|
||||
};
|
||||
|
||||
/** 修改按钮操作 */
|
||||
const handleUpdate = async (row: TreeVO) => {
|
||||
reset();
|
||||
await getTreeselect();
|
||||
if (row) {
|
||||
form.value.parentId = row.id;
|
||||
}
|
||||
const res = await getTree(row.id);
|
||||
Object.assign(form.value, res.data);
|
||||
dialog.visible = true;
|
||||
dialog.title = '修改测试树';
|
||||
};
|
||||
|
||||
/** 提交按钮 */
|
||||
const submitForm = () => {
|
||||
treeFormRef.value?.validate(async (valid: boolean) => {
|
||||
if (valid) {
|
||||
buttonLoading.value = true;
|
||||
if (form.value.id) {
|
||||
await updateTree(form.value).finally(() => (buttonLoading.value = false));
|
||||
} else {
|
||||
await addTree(form.value).finally(() => (buttonLoading.value = false));
|
||||
}
|
||||
proxy?.$modal.msgSuccess('操作成功');
|
||||
dialog.visible = false;
|
||||
await getList();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/** 删除按钮操作 */
|
||||
const handleDelete = async (row: TreeVO) => {
|
||||
await proxy?.$modal.confirm('是否确认删除测试树编号为"' + row.id + '"的数据项?');
|
||||
loading.value = true;
|
||||
await delTree(row.id).finally(() => (loading.value = false));
|
||||
await getList();
|
||||
proxy?.$modal.msgSuccess('删除成功');
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getList();
|
||||
});
|
||||
</script>
|
@ -3,48 +3,60 @@
|
||||
<transition :enter-active-class="proxy?.animate.searchAnimate.enter"
|
||||
:leave-active-class="proxy?.animate.searchAnimate.leave">
|
||||
<div v-show="showSearch" class="mb-[10px]">
|
||||
<el-card shadow="hover">
|
||||
<el-button :type="isListView ? 'primary' : ''" @click="switchView('card')">
|
||||
<el-card shadow="hover" class="btn_wev">
|
||||
<el-button :type="isListView ? 'primary' : ''" @click="switchView('card')" class="btn_list">
|
||||
{{ isListView ? '卡片显示' : '卡片显示' }}
|
||||
</el-button>
|
||||
<el-button :type="!isListView ? 'primary' : ''" @click="switchView('list')">
|
||||
<el-button :type="!isListView ? 'primary' : ''" @click="switchView('list')" class="btn_list">
|
||||
{{ !isListView ? '列表显示' : '列表显示' }}
|
||||
</el-button>
|
||||
<el-form ref="queryFormRef" :model="queryParams" :inline="true" style="margin-top: 20px;">
|
||||
<el-form-item label="设备名称" prop="deviceName">
|
||||
<el-input v-model="queryParams.deviceName" placeholder="请输入设备名称" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="设备类型" prop="deviceType">
|
||||
<el-select v-model="queryParams.deviceType" placeholder="设备类型">
|
||||
<el-option v-for="item in deviceTypeOptions" :key="item.value" :label="item.typeName"
|
||||
:value="item.deviceTypeId" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="报警事项" prop="deviceAction">
|
||||
<el-select v-model="queryParams.deviceAction">
|
||||
<el-option value="0" label="强制报警"></el-option>
|
||||
<el-option value="1" label="撞击闯入"></el-option>
|
||||
<el-option value="2" label="手动报警"></el-option>
|
||||
<el-option value="3" label="电子围栏告警"></el-option>
|
||||
<el-option value="4" label="强制告警"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="报警时间" style="width: 308px">
|
||||
<el-date-picker v-model="dateRange" value-format="YYYY-MM-DD HH:mm:ss" type="daterange"
|
||||
range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期"
|
||||
:default-time="[new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 1, 1, 23, 59, 59)]"></el-date-picker>
|
||||
</el-form-item>
|
||||
<el-form-item label="报警状态" prop="treatmentState">
|
||||
<el-select v-model="queryParams.treatmentState">
|
||||
<el-option value="0" label="已处理"></el-option>
|
||||
<el-option value="1" label="未处理"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div class="btn_search">
|
||||
<div style="position: absolute; right:30px; top:30px">
|
||||
<el-input v-model="queryParams.content" placeholder="报警信息" clearable
|
||||
style="width: 200px; margin-right: 20px;" @keyup.enter="handleQuery" @input="handleInput" />
|
||||
<el-button type="primary" plain @click="toggleFilter">高级筛选</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<el-collapse accordion v-model="activeNames">
|
||||
<el-collapse-item name="1">
|
||||
<el-form ref="queryFormRef" :model="queryParams" :inline="true" style="margin-top: 20px;">
|
||||
<el-form-item label="设备名称" prop="deviceName">
|
||||
<el-input v-model="queryParams.deviceName" placeholder="请输入设备名称" clearable
|
||||
@keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="设备类型" prop="deviceType">
|
||||
<el-select v-model="queryParams.deviceType" placeholder="设备类型">
|
||||
<el-option v-for="item in deviceTypeOptions" :key="item.value" :label="item.typeName"
|
||||
:value="item.deviceTypeId" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="报警事项" prop="deviceAction">
|
||||
<el-select v-model="queryParams.deviceAction">
|
||||
<el-option value="0" label="强制报警"></el-option>
|
||||
<el-option value="1" label="撞击闯入"></el-option>
|
||||
<el-option value="2" label="手动报警"></el-option>
|
||||
<el-option value="3" label="电子围栏告警"></el-option>
|
||||
<el-option value="4" label="强制告警"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="报警时间" style="width: 308px">
|
||||
<el-date-picker v-model="dateRange" value-format="YYYY-MM-DD HH:mm:ss" type="daterange"
|
||||
range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期"
|
||||
:default-time="[new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 1, 1, 23, 59, 59)]"></el-date-picker>
|
||||
</el-form-item>
|
||||
<el-form-item label="处理状态" prop="treatmentState">
|
||||
<el-select v-model="queryParams.treatmentState">
|
||||
<el-option value="0" label="已处理"></el-option>
|
||||
<el-option value="1" label="待处理"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-collapse-item>
|
||||
</el-collapse>
|
||||
</el-card>
|
||||
</div>
|
||||
</transition>
|
||||
@ -158,7 +170,8 @@ const showSearch = ref(true);
|
||||
const total = ref(0);
|
||||
const deviceTypeOptions = ref([]); //设备类型
|
||||
const queryFormRef = ref<ElFormInstance>();
|
||||
const alarmFormRef = ref<ElFormInstance>();
|
||||
const activeNames = ref([]);
|
||||
const debounceTimer = ref(null) // 用于防抖的定时器
|
||||
const initFormData: AlarmForm = {
|
||||
id: undefined,
|
||||
deviceId: undefined,
|
||||
@ -225,14 +238,23 @@ const getDeviceType = () => {
|
||||
|
||||
})
|
||||
};
|
||||
const toggleFilter = () => {
|
||||
if (activeNames.value.length > 0) {
|
||||
activeNames.value = [];
|
||||
} else {
|
||||
activeNames.value = ['1'];
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/** 表单重置 */
|
||||
const reset = () => {
|
||||
form.value = { ...initFormData };
|
||||
alarmFormRef.value?.resetFields();
|
||||
}
|
||||
|
||||
const handleInput = () => {
|
||||
if (debounceTimer.value) {
|
||||
clearTimeout(debounceTimer.value)
|
||||
}
|
||||
// 300ms后执行查询(避免输入过程中频繁调用接口)
|
||||
debounceTimer.value = setTimeout(() => {
|
||||
handleQuery() // 调用查询接口的方法
|
||||
}, 300)
|
||||
};
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.value.pageNum = 1;
|
||||
@ -250,8 +272,25 @@ onMounted(() => {
|
||||
});
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
:deep .el-collapse-item__header {
|
||||
display: none;
|
||||
}
|
||||
|
||||
:deep .el-collapse-item__wrap {
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
.btn_wev {
|
||||
padding: 5px 0;
|
||||
}
|
||||
|
||||
.btn_list {
|
||||
margin-top: -5px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.custom-alarm-card {
|
||||
height: 298px;
|
||||
height: 270px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
box-shadow: 0px 0px 6px 0px rgba(0, 27, 74, 0.1);
|
||||
@ -320,7 +359,8 @@ onMounted(() => {
|
||||
.label {
|
||||
font-weight: 500;
|
||||
margin-right: 4px;
|
||||
margin: 12px 0;
|
||||
margin: 4px 0;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.value {
|
||||
@ -334,6 +374,7 @@ onMounted(() => {
|
||||
margin: 10px 0 10px 0;
|
||||
position: relative;
|
||||
color: rgb(224, 52, 52);
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.alearm::before {
|
||||
@ -354,6 +395,7 @@ onMounted(() => {
|
||||
margin: 10px 0 10px 0;
|
||||
position: relative;
|
||||
color: rgba(56, 64, 79, 1);
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.alearmADD::before {
|
||||
|
@ -64,6 +64,7 @@
|
||||
<template #default="scope">
|
||||
<div v-if="scope.row.communicationMode == 0">4G</div>
|
||||
<div v-if="scope.row.communicationMode == 1">蓝牙</div>
|
||||
<div v-if="scope.row.communicationMode == 2">4G&蓝牙</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="createTime" label="创建日期" />
|
||||
@ -105,7 +106,7 @@
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="pc路由跳转" prop="pcModelDictionary" style="display: none;">
|
||||
<el-select v-model="form.pcModelDictionary" placeholder="请选择" >
|
||||
<el-select v-model="form.pcModelDictionary" placeholder="请选择">
|
||||
<el-option v-for="item in pcmodelDictionaryOptions" :key="item.dictCode" :label="item.dictLabel"
|
||||
:value="item.dictValue" />
|
||||
</el-select>
|
||||
@ -141,6 +142,7 @@
|
||||
<el-select v-model="form.communicationMode" placeholder="请选择">
|
||||
<el-option label="4G" :value="0" />
|
||||
<el-option label="蓝牙" :value="1" />
|
||||
<el-option label="4G&蓝牙" :value="2" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
160
src/views/equipmentManagement/devices/Charge.vue
Normal file
@ -0,0 +1,160 @@
|
||||
<template>
|
||||
<div class="content">
|
||||
<div class="topFilter">
|
||||
<el-form :inline="true" :model="filter" class="demo-form-inline">
|
||||
<el-form-item label="操作时间">
|
||||
<el-date-picker
|
||||
v-model="filter.Date"
|
||||
type="daterange"
|
||||
range-separator="-"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
value-format="YYYY-MM-DD"
|
||||
:size="'default'"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="getRecordList">查询</el-button>
|
||||
<el-button link type="primary" @click="filter.Date = []">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
<div class="grid" v-loading="Status.loading">
|
||||
<el-table ref="grid" v-loading="Status.loading" border :data="List" height="calc(100vh - 320px)">
|
||||
<el-table-column label="创建时间" align="center" prop="createTime" />
|
||||
<el-table-column label="设备名称" align="center" prop="deviceName" />
|
||||
<el-table-column label="数据来源" align="center" prop="dataSource" />
|
||||
<el-table-column label="内容" align="center" prop="content" />
|
||||
</el-table>
|
||||
|
||||
<pagination
|
||||
v-show="pagin.total > 0"
|
||||
v-model:page="pagin.pageIndex"
|
||||
v-model:limit="pagin.pageSize"
|
||||
:total="pagin.total"
|
||||
@pagination="getRecordList"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import request from '@/utils/request';
|
||||
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
acIndex: {
|
||||
type: Number,
|
||||
required: true
|
||||
}
|
||||
});
|
||||
var filter = ref({
|
||||
Date: []
|
||||
});
|
||||
var Status = ref({
|
||||
loading: false
|
||||
});
|
||||
|
||||
var data = ref({});
|
||||
var grid = ref(null);
|
||||
var List = ref([]);
|
||||
var pagin = ref({
|
||||
total: 0,
|
||||
pageIndex: 1,
|
||||
pageSize: 10
|
||||
});
|
||||
function getRecordList() {
|
||||
Status.value.loading = true;
|
||||
|
||||
|
||||
setTimeout(() => {
|
||||
initData(props.data,true);
|
||||
}, 0);
|
||||
}
|
||||
|
||||
var deviceid = null;
|
||||
function initData(item,Refresh) {
|
||||
if (deviceid == item.data.deviceId && !Refresh) {
|
||||
Status.value.loading = false;
|
||||
return;
|
||||
}
|
||||
List.value = [];
|
||||
deviceid = item.data.deviceId;
|
||||
|
||||
let params = {
|
||||
startTime: '',
|
||||
endTime: '',
|
||||
deviceId:deviceid,
|
||||
pageNum:pagin.value.pageIndex,
|
||||
pageSize:pagin.value.pageSize,
|
||||
orderByColumn:"updatedAt",
|
||||
isAsc:"descending"
|
||||
};
|
||||
if (filter.value.Date && filter.value.Date.length) {
|
||||
params.startTime = filter.value.Date[0];
|
||||
params.endTime = filter.value.Date.length > 1 ? filter.value.Date[1] : '';
|
||||
}
|
||||
|
||||
request({
|
||||
url: '/equipment/chargeDischarge/list' ,
|
||||
method: 'get',
|
||||
params: params
|
||||
})
|
||||
.then((res) => {
|
||||
if (res.code == 200) {
|
||||
if (res.rows) {
|
||||
if (res.rows.length) {
|
||||
List.value = res.rows;
|
||||
}
|
||||
}
|
||||
pagin.value.total= res.total;
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
data.value = item.data;
|
||||
Status.value.loading = false;
|
||||
});
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.acIndex, // 监听的属性
|
||||
(newVal) => {
|
||||
|
||||
|
||||
if (newVal === 3) {
|
||||
// 确保参数有效
|
||||
debugger;
|
||||
console.log('newVal=', newVal);
|
||||
getRecordList(); // 调用回调
|
||||
}
|
||||
},
|
||||
{ immediate: true } // 关键:组件初始化时立即执行一次
|
||||
);
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.content {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
padding: 30px 12px 12px 12px;
|
||||
}
|
||||
|
||||
.pickerContent {
|
||||
box-sizing: border-box;
|
||||
border: 1px solid rgba(189, 198, 215, 0.8);
|
||||
border-radius: 2px;
|
||||
}
|
||||
.topFilter {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: nowrap;
|
||||
align-content: center;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
}
|
||||
:deep .pickerContent .el-date-editor {
|
||||
border: none !important;
|
||||
}
|
||||
</style>
|
161
src/views/equipmentManagement/devices/OpraRecored.vue
Normal file
@ -0,0 +1,161 @@
|
||||
<template>
|
||||
<div class="content">
|
||||
<div class="topFilter">
|
||||
<el-form :inline="true" :model="filter" class="demo-form-inline">
|
||||
<el-form-item label="操作时间">
|
||||
<el-date-picker
|
||||
v-model="filter.Date"
|
||||
type="daterange"
|
||||
range-separator="-"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
value-format="YYYY-MM-DD"
|
||||
:size="'default'"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="getRecordList">查询</el-button>
|
||||
<el-button link type="primary" @click="filter.Date = []">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
<div class="grid" v-loading="Status.loading">
|
||||
<el-table ref="grid" v-loading="Status.loading" border :data="List" height="calc(100vh - 320px)">
|
||||
<el-table-column label="创建时间" align="center" prop="createTime" />
|
||||
<el-table-column label="设备名称" align="center" prop="deviceName" />
|
||||
<el-table-column label="数据来源" align="center" prop="dataSource" />
|
||||
<el-table-column label="内容" align="center" prop="content" />
|
||||
</el-table>
|
||||
|
||||
<pagination
|
||||
v-show="pagin.total > 0"
|
||||
v-model:page="pagin.pageIndex"
|
||||
v-model:limit="pagin.pageSize"
|
||||
:total="pagin.total"
|
||||
@pagination="getRecordList"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import request from '@/utils/request';
|
||||
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
acIndex: {
|
||||
type: Number,
|
||||
required: true
|
||||
}
|
||||
});
|
||||
var filter = ref({
|
||||
Date: []
|
||||
});
|
||||
var Status = ref({
|
||||
loading: false
|
||||
});
|
||||
|
||||
var data = ref({});
|
||||
var grid = ref(null);
|
||||
var List = ref([]);
|
||||
var pagin = ref({
|
||||
total: 0,
|
||||
pageIndex: 1,
|
||||
pageSize: 10
|
||||
});
|
||||
function getRecordList() {
|
||||
Status.value.loading = true;
|
||||
|
||||
|
||||
setTimeout(() => {
|
||||
initData(props.data,true);
|
||||
}, 0);
|
||||
}
|
||||
|
||||
var deviceid = null;
|
||||
function initData(item,Refresh) {
|
||||
if (deviceid == item.data.deviceId && !Refresh) {
|
||||
Status.value.loading = false;
|
||||
return;
|
||||
}
|
||||
List.value = [];
|
||||
deviceid = item.data.deviceId;
|
||||
|
||||
|
||||
let params = {
|
||||
startTime: '',
|
||||
endTime: '',
|
||||
pageNum:pagin.value.pageIndex,
|
||||
pageSize:pagin.value.pageSize,
|
||||
orderByColumn:"updateTime",
|
||||
isAsc:"descending"
|
||||
};
|
||||
if (filter.value.Date && filter.value.Date.length) {
|
||||
params.startTime = filter.value.Date[0];
|
||||
params.endTime = filter.value.Date.length > 1 ? filter.value.Date[1] : '';
|
||||
}
|
||||
|
||||
request({
|
||||
url: '/api/device/getOperationRecord/' + item.data.deviceId,
|
||||
method: 'get',
|
||||
params: params
|
||||
})
|
||||
.then((res) => {
|
||||
if (res.code == 200) {
|
||||
if (res.rows) {
|
||||
if (res.rows.length) {
|
||||
List.value = res.rows;
|
||||
}
|
||||
}
|
||||
|
||||
pagin.value.total= res.total;
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
data.value = item.data;
|
||||
Status.value.loading = false;
|
||||
});
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.acIndex, // 监听的属性
|
||||
(newVal) => {
|
||||
|
||||
|
||||
if (newVal === 3) {
|
||||
// 确保参数有效
|
||||
debugger;
|
||||
console.log('newVal=', newVal);
|
||||
getRecordList(); // 调用回调
|
||||
}
|
||||
},
|
||||
{ immediate: true } // 关键:组件初始化时立即执行一次
|
||||
);
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.content {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
padding: 30px 12px 12px 12px;
|
||||
}
|
||||
|
||||
.pickerContent {
|
||||
box-sizing: border-box;
|
||||
border: 1px solid rgba(189, 198, 215, 0.8);
|
||||
border-radius: 2px;
|
||||
}
|
||||
.topFilter {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: nowrap;
|
||||
align-content: center;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
}
|
||||
:deep .pickerContent .el-date-editor {
|
||||
border: none !important;
|
||||
}
|
||||
</style>
|
256
src/views/equipmentManagement/devices/Usr.vue
Normal file
@ -0,0 +1,256 @@
|
||||
<template>
|
||||
<div class="content">
|
||||
<div class="row">
|
||||
<div class="item">
|
||||
<div class="lable">姓名<span>:</span></div>
|
||||
<div class="val">{{ lastData?.name }}</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="lable">单位名称<span>:</span></div>
|
||||
<div class="val">{{ lastData?.position }}</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="lable">职务<span>:</span></div>
|
||||
<div class="val">{{ lastData?.unitName }}</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="lable">ID号<span>:</span></div>
|
||||
<div class="val">{{ lastData?.code }}</div>
|
||||
</div>
|
||||
<div class="clear"></div>
|
||||
</div>
|
||||
<div class="clear divider"></div>
|
||||
<div class="listTitle">信息登记记录</div>
|
||||
<div class="list">
|
||||
<div class="item" v-for="(item, index) in detailData">
|
||||
<div class="time">
|
||||
<el-icon><Timer /></el-icon>{{ item.item }}
|
||||
</div>
|
||||
<div class="childContent">
|
||||
<div class="itemChild" :class="i === item.child.length - 1 ? '' : 'marginBottom'" v-for="(c, i) in item.child">
|
||||
<div class="itemRow">登记时间:{{ c.createTime }}</div>
|
||||
<div class="itemRow">姓 名:{{ c.name }}</div>
|
||||
<div class="itemRow">单位名称:{{ c.position }}</div>
|
||||
<div class="itemRow">职 务:{{ c.unitName }}</div>
|
||||
<div class="itemRow">ID 号:{{ c.code }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import request from '@/utils/request';
|
||||
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
acIndex: {
|
||||
type: Number,
|
||||
required: true
|
||||
}
|
||||
});
|
||||
|
||||
var detailData = ref([]);
|
||||
var data = ref({});
|
||||
var deviceid = null;
|
||||
var lastData = ref({ name: '', position: '', unitName: '', code: '' });
|
||||
function processDataByDateGroup(originalData) {
|
||||
// 1. 深拷贝原始数据(避免修改原数据,保护源数据完整性)
|
||||
const data = JSON.parse(JSON.stringify(originalData));
|
||||
|
||||
// 边界处理:若data不存在或非数组,返回原结构+空sipledate
|
||||
if (!data?.data || !Array.isArray(data.data)) {
|
||||
return {
|
||||
code: data?.code ?? 400, // 若原code不存在,默认400(请求错误)
|
||||
msg: data?.msg ?? '数据格式错误',
|
||||
sipledate: []
|
||||
};
|
||||
}
|
||||
|
||||
// 2. 先按createTime逆序排序(子数据内部也保持“新->旧”顺序)
|
||||
const sortedData = data.data.sort((a, b) => {
|
||||
const timeA = new Date(a.createTime);
|
||||
const timeB = new Date(b.createTime);
|
||||
return timeB - timeA; // 逆序:后创建的排在前面
|
||||
});
|
||||
|
||||
// 3. 按日期分组(key:yyyy-MM-dd,value:该日期下的所有子数据)
|
||||
const dateGroupMap = sortedData.reduce((map, item) => {
|
||||
// 提取当前数据的“yyyy-MM-dd”格式日期
|
||||
const dateKey = item.createTime.slice(0, 10);
|
||||
// 若当前日期未在map中,初始化空数组;若已存在,直接push子数据
|
||||
if (!map.has(dateKey)) {
|
||||
map.set(dateKey, []);
|
||||
}
|
||||
map.get(dateKey).push(item);
|
||||
return map;
|
||||
}, new Map()); // 使用Map而非Object,确保日期顺序与排序后一致
|
||||
|
||||
// 4. 转换为需求格式的sipledate数组(每个元素含item和child)
|
||||
const sipledate = Array.from(dateGroupMap.entries()).map(([date, childData]) => ({
|
||||
item: date, // 日期标识(yyyy-MM-dd),原需求“item1/item2”统一为“item”(更语义化)
|
||||
child: childData // 该日期下的所有子数据(已按时间逆序)
|
||||
}));
|
||||
|
||||
// 5. 返回最终格式结果
|
||||
return {
|
||||
code: data.code,
|
||||
msg: data.msg,
|
||||
sipledate: sipledate
|
||||
};
|
||||
}
|
||||
|
||||
function initData(item) {
|
||||
if (deviceid == item.data.deviceId) {
|
||||
return;
|
||||
}
|
||||
deviceid = item.data.deviceId;
|
||||
request({
|
||||
url: '/api/device/getDeviceUser/' + item.data.deviceId,
|
||||
method: 'get'
|
||||
})
|
||||
.then((res) => {
|
||||
if (res.code == 200) {
|
||||
if (res.data) {
|
||||
if (res.data.length) {
|
||||
lastData.value = res.data[0];
|
||||
let data = processDataByDateGroup(res);
|
||||
detailData.value = data.sipledate;
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
data.value = item.data;
|
||||
});
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.acIndex, // 监听的属性
|
||||
(newVal) => {
|
||||
if (newVal === 2 && props.data) {
|
||||
// 确保参数有效
|
||||
initData(props.data); // 调用回调
|
||||
}
|
||||
},
|
||||
{ immediate: true } // 关键:组件初始化时立即执行一次
|
||||
);
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.content {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
padding: 30px 12px 12px 12px;
|
||||
}
|
||||
.row {
|
||||
width: 50%;
|
||||
}
|
||||
.row .item {
|
||||
width: 50%;
|
||||
float: left;
|
||||
height: 36px;
|
||||
line-height: 36px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: nowrap;
|
||||
align-content: center;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
|
||||
color: rgba(56, 64, 79, 1);
|
||||
|
||||
font-family: Microsoft YaHei;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
line-height: 18px;
|
||||
letter-spacing: 0px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.row .item .lable {
|
||||
width: 100px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.divider {
|
||||
width: 100%;
|
||||
height: 0px;
|
||||
border-bottom: 1px solid rgba(235, 238, 248, 1);
|
||||
}
|
||||
|
||||
.listTitle {
|
||||
color: rgba(56, 64, 79, 1);
|
||||
|
||||
font-family: Microsoft YaHei;
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
height: 56px;
|
||||
line-height: 56px;
|
||||
letter-spacing: 0px;
|
||||
text-align: left;
|
||||
}
|
||||
.list {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
height: calc(100% - 130px);
|
||||
padding: 15px 12px;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
.list .time {
|
||||
color: #027cfb;
|
||||
|
||||
font-family: Microsoft YaHei;
|
||||
font-size: 16px;
|
||||
font-weight: 400;
|
||||
line-height: 21px;
|
||||
height: 21px;
|
||||
letter-spacing: 0px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
|
||||
.list .itemRow{
|
||||
color: rgba(56, 64, 79, 1);
|
||||
|
||||
font-family: Microsoft YaHei;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
line-height: 18px;
|
||||
letter-spacing: 0px;
|
||||
text-align: left;
|
||||
margin-bottom: 13px;
|
||||
}
|
||||
.itemChild.marginBottom {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.list .childContent {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
border-left: 1px solid rgba(0, 198, 250, 0.2);
|
||||
box-sizing: border-box;
|
||||
padding: 6px 20px;
|
||||
}
|
||||
|
||||
.list .itemChild {
|
||||
border-radius: 4px;
|
||||
background: rgba(247, 248, 252, 1);
|
||||
width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.fleft {
|
||||
float: left;
|
||||
}
|
||||
.fright {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.clear {
|
||||
clear: both;
|
||||
}
|
||||
</style>
|
181
src/views/equipmentManagement/devices/WarnRecord.vue
Normal file
@ -0,0 +1,181 @@
|
||||
<template>
|
||||
<div class="content">
|
||||
<div class="topFilter">
|
||||
<el-form :inline="true" :model="filter" class="demo-form-inline">
|
||||
<el-form-item label="操作时间">
|
||||
<el-date-picker
|
||||
v-model="filter.Date"
|
||||
type="daterange"
|
||||
range-separator="-"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
value-format="YYYY-MM-DD"
|
||||
:size="'default'"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="getRecordList">查询</el-button>
|
||||
<el-button link type="primary" @click="filter.Date = []">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
<div class="grid" v-loading="Status.loading">
|
||||
<el-table ref="grid" v-loading="Status.loading" border :data="List" height="calc(100vh - 320px)">
|
||||
<el-table-column prop="deviceAction" label="报警事项" align="center">
|
||||
<template #default="scope">
|
||||
<div>{{ dic.warType[scope.row.deviceAction] }}</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="报警开始时间" align="center" prop="startTime" />
|
||||
<el-table-column label="报警结束时间" align="center" prop="finishTime" />
|
||||
<el-table-column label="报警持续时间" align="center" prop="durationTime" />
|
||||
<el-table-column label="处理状态" align="center" prop="treatmentState">
|
||||
<template #default="scope">
|
||||
<div>{{ dic.treamt[scope.row.deviceAction] ? dic.treamt[scope.row.deviceAction] :'未处理' }}</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination
|
||||
v-show="pagin.total > 0"
|
||||
v-model:page="pagin.pageIndex"
|
||||
v-model:limit="pagin.pageSize"
|
||||
:total="pagin.total"
|
||||
@pagination="getRecordList"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import request from '@/utils/request';
|
||||
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
acIndex: {
|
||||
type: Number,
|
||||
required: true
|
||||
}
|
||||
});
|
||||
|
||||
var dic = {
|
||||
warType: {
|
||||
0: '强制报警',
|
||||
1: '撞击闯入',
|
||||
2: '手动报警',
|
||||
3: '电子围栏告警',
|
||||
4: '强制告警'
|
||||
},
|
||||
treamt:{
|
||||
0:"已处理",
|
||||
1:"未处理"
|
||||
}
|
||||
};
|
||||
|
||||
var filter = ref({
|
||||
Date: []
|
||||
});
|
||||
var Status = ref({
|
||||
loading: false
|
||||
});
|
||||
|
||||
var data = ref({});
|
||||
var grid = ref(null);
|
||||
var List = ref([]);
|
||||
var pagin = ref({
|
||||
total: 0,
|
||||
pageIndex: 1,
|
||||
pageSize: 10
|
||||
});
|
||||
function getRecordList() {
|
||||
Status.value.loading = true;
|
||||
|
||||
setTimeout(() => {
|
||||
initData(props.data, true);
|
||||
}, 0);
|
||||
}
|
||||
|
||||
var deviceid = null;
|
||||
function initData(item, Refresh) {
|
||||
if (deviceid == item.data.deviceId && !Refresh) {
|
||||
Status.value.loading = false;
|
||||
return;
|
||||
}
|
||||
List.value = [];
|
||||
deviceid = item.data.deviceId;
|
||||
let params = {
|
||||
startTime: '',
|
||||
endTime: '',
|
||||
pageNum:pagin.value.pageIndex,
|
||||
pageSize:pagin.value.pageSize,
|
||||
orderByColumn:"updateTime",
|
||||
isAsc:"descending"
|
||||
};
|
||||
if (filter.value.Date && filter.value.Date.length) {
|
||||
params.startTime = filter.value.Date[0];
|
||||
params.endTime = filter.value.Date.length > 1 ? filter.value.Date[1] : '';
|
||||
}
|
||||
|
||||
request({
|
||||
url: '/api/device/getAlarmRecord/' + item.data.deviceId,
|
||||
method: 'get',
|
||||
params: params
|
||||
})
|
||||
.then((res) => {
|
||||
debugger;
|
||||
if (res.code == 200) {
|
||||
if (res.rows) {
|
||||
if (res.rows.length) {
|
||||
List.value = res.rows;
|
||||
}
|
||||
}
|
||||
pagin.value.total= res.total;
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
data.value = item.data;
|
||||
Status.value.loading = false;
|
||||
});
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.acIndex, // 监听的属性
|
||||
(newVal) => {
|
||||
if (newVal === 4) {
|
||||
// 确保参数有效
|
||||
|
||||
console.log('newVal=', newVal);
|
||||
getRecordList(); // 调用回调
|
||||
}
|
||||
},
|
||||
{ immediate: true } // 关键:组件初始化时立即执行一次
|
||||
);
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.content {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
padding: 30px 12px 12px 12px;
|
||||
}
|
||||
|
||||
.pickerContent {
|
||||
box-sizing: border-box;
|
||||
border: 1px solid rgba(189, 198, 215, 0.8);
|
||||
border-radius: 2px;
|
||||
}
|
||||
.topFilter {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: nowrap;
|
||||
align-content: center;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
}
|
||||
:deep .pickerContent .el-date-editor {
|
||||
border: none !important;
|
||||
}
|
||||
</style>
|
212
src/views/equipmentManagement/devices/eqDetail.vue
Normal file
@ -0,0 +1,212 @@
|
||||
<template>
|
||||
<div class="content">
|
||||
<div class="leftImg fleft">
|
||||
<img class="img" :src="detailData?.devicePic" />
|
||||
</div>
|
||||
<div class="detail fleft">
|
||||
|
||||
<div class="row">
|
||||
|
||||
<div class="item">
|
||||
<div class="lable">设备名称<span>:</span></div>
|
||||
<div class="val">{{ detailData?.deviceName }}</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="lable">设备IMEI<span>:</span></div>
|
||||
<div class="val">{{ detailData?.deviceImei }}</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="lable">设备MAC<span>:</span></div>
|
||||
<div class="val">{{ detailData?.deviceMac }}</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="item">
|
||||
<div class="lable">通讯方式<span>:</span></div>
|
||||
<div class="val">{{ detailData?.communicationMode===1 ?'蓝牙':'4G' }}</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="lable">蓝牙名称<span>:</span></div>
|
||||
<div class="val">{{ detailData?.bluetoothName }}</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="lable">设备类型<span>:</span></div>
|
||||
<div class="val">{{ detailData?.typeName }}</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="item">
|
||||
<div class="lable">使用人员<span>:</span></div>
|
||||
<div class="val">{{ detailData?.personnelBy }}</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="lable">设备状态<span>:</span></div>
|
||||
<div class="val">{{ detailData?.deviceStatus===1 ?'正常':'失效' }}</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="lable">绑定时间<span>:</span></div>
|
||||
<div class="val">{{ detailData?.bindingTime }}</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="item">
|
||||
<div class="lable">在线状态<span>:</span></div>
|
||||
<div class="val">{{ detailData?.onlineStatus===1?'在线':'离线' }}</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="lable">电量<span>:</span></div>
|
||||
<div class="val">{{ detailData?.battery }}%</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="lable">告警状态<span>:</span></div>
|
||||
<div class="val">{{ detailData?.alarmStatus=='1'?'告警':'未告警' }}</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="item">
|
||||
<div class="lable">经度<span>:</span></div>
|
||||
<div class="val">{{ detailData?.latitude }}</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="lable">纬度<span>:</span></div>
|
||||
<div class="val">{{ detailData?.longitude }}</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="lable">分享用户数量<span>:</span></div>
|
||||
<div class="val">{{ detailData?.shareUsersNumber }}</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="clear"></div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import request from "@/utils/request";
|
||||
import { number } from "vue-types";
|
||||
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
acIndex:{
|
||||
type:Number,
|
||||
required: true
|
||||
}
|
||||
|
||||
})
|
||||
var data=ref({});
|
||||
var detailData = ref({});
|
||||
var deviceid=null;
|
||||
function initData(item) {
|
||||
if(deviceid==item.data.deviceId){
|
||||
return;
|
||||
}
|
||||
deviceid=item.data.deviceId
|
||||
|
||||
detailData.value={};
|
||||
request({
|
||||
url: "/api/device/pc/detail/" + item.data.deviceId,
|
||||
method: 'get'
|
||||
}).then(res => {
|
||||
if (res.code == 200) {
|
||||
if (res.data) {
|
||||
detailData.value = res.data;
|
||||
}
|
||||
}
|
||||
}).finally(() => {
|
||||
data.value=item.data;
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.acIndex, // 监听的属性
|
||||
(newVal) => {
|
||||
if (newVal===1 && props.data) { // 确保参数有效
|
||||
initData(props.data) // 调用回调
|
||||
}
|
||||
},
|
||||
{ immediate: true } // 关键:组件初始化时立即执行一次
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.content {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
padding: 30px 12px 12px 12px;
|
||||
|
||||
}
|
||||
|
||||
.leftImg {
|
||||
width: 18%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.leftImg .img {
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
object-fit: scale-down;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0px 0px 4px 0px rgba(0, 27, 74, 0.15);
|
||||
background: rgba(247, 248, 252, 1);
|
||||
}
|
||||
|
||||
.detail {
|
||||
width: 82%;
|
||||
|
||||
}
|
||||
|
||||
.detail .row {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
}
|
||||
|
||||
.detail .row .item {
|
||||
width: calc(100% / 3);
|
||||
margin-bottom: 20px;
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
color: rgba(56, 64, 79, 1);
|
||||
|
||||
font-family: Microsoft YaHei;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
|
||||
letter-spacing: 0px;
|
||||
text-align: left;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.detail .row .lable {
|
||||
width: 100px;
|
||||
text-align: right;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.detail .row .val {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.fleft {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.fright {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.clear {
|
||||
clear: both;
|
||||
}
|
||||
</style>
|
566
src/views/equipmentManagement/devices/shareManage.vue
Normal file
@ -0,0 +1,566 @@
|
||||
<template>
|
||||
<div class="content" v-loading="Status.fullLoading">
|
||||
<div class="topFilter">
|
||||
<div>
|
||||
<el-button type="primary" @click="ShowEdit"
|
||||
><el-icon> <Plus /> </el-icon>添加分享</el-button
|
||||
>
|
||||
<el-button type="danger" @click="DelShare(null, true)" :disabled="selectRows.length === 0">批量删除</el-button>
|
||||
</div>
|
||||
|
||||
<el-form :inline="true" :model="filter" class="demo-form-inline">
|
||||
<el-form-item label="">
|
||||
<el-input v-model="filter.phonenumber" placeholder="查找分享用户" @input="Search">
|
||||
<template #append>
|
||||
<el-button :icon="'Search'" />
|
||||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="分享时间">
|
||||
<el-date-picker
|
||||
v-model="filter.Date"
|
||||
type="datetimerange"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
format="YYYY-MM-DD HH:mm:ss"
|
||||
date-format="YYYY/MM/DD ddd"
|
||||
time-format="A hh:mm:ss"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="getRecordList">查询</el-button>
|
||||
<el-button link type="primary" @click="filter.Date = []">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
<div class="grid" v-loading="Status.loading">
|
||||
<el-table ref="grid" v-loading="Status.loading" border :data="List" height="calc(100vh - 320px)" @selection-change="RowSelectionChange">
|
||||
<el-table-column type="selection" width="50" align="center" />
|
||||
<el-table-column label="分享用户" align="center" prop="phonenumber" />
|
||||
<el-table-column label="分享权限" align="center" prop="permission">
|
||||
<template #default="scope">
|
||||
<div>{{ getPower(scope.row) }}</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="分享时间" align="center" prop="createTime" />
|
||||
<el-table-column label="操作" align="center" fixed="right" width="280" class-name="small-padding fixed-width opt">
|
||||
<template #default="scope">
|
||||
<div class="center">
|
||||
<el-text class="mx-1 marginRight10" type="primary" @click.stop="powerSet(scope.row)">权限管理</el-text>
|
||||
|
||||
<el-text class="mx-1" type="danger" @click.stop="DelShare(scope.row, false)">移除</el-text>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination
|
||||
v-show="pagin.total > 0"
|
||||
v-model:page="pagin.pageIndex"
|
||||
v-model:limit="pagin.pageSize"
|
||||
:total="pagin.total"
|
||||
@pagination="getRecordList"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 修改权限弹窗 -->
|
||||
<el-dialog v-model="Status.ShowPowerSet" :title="'权限管理'" width="800" :draggable="true">
|
||||
<div class="form">
|
||||
<el-form :model="cEdit" ref="ruleFormRef" style="max-width: 750px">
|
||||
<el-form-item label="分享权限" label-position="right">
|
||||
<el-checkbox-group v-model="cEdit.permission">
|
||||
<el-checkbox :label="item.label" v-for="item in power" :value="item.value" />
|
||||
</el-checkbox-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="验证码" label-position="right">
|
||||
<el-input v-model="cEdit.smsCode" placeholder="验证码">
|
||||
<template #append
|
||||
><div class="btnSendSms" @click.stop="sendSms">{{ time > 0 ? time + '秒后重发' : '发送验证码' }}</div></template
|
||||
>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
<div class="center" style="margin-top: 10px">
|
||||
<el-button type="primary" @click="SaveFormData('update')"> 确定 </el-button>
|
||||
<el-button @click="CloseEdit"> 取消 </el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 编辑框 -->
|
||||
<el-dialog v-model="Status.ShowEditPop" :data-val="Status.ShowEditPop" :title="'添加分享'" width="800" :draggable="true">
|
||||
<div class="form">
|
||||
<el-form :model="cEdit" ref="ruleFormRef" style="max-width: 750px" :label-width="'75px'">
|
||||
<el-form-item label="分享用户" label-position="right">
|
||||
<el-select v-model="cEdit.phonenumber" filterable placeholder="输入以搜索">
|
||||
<el-option v-for="item in Usrs" :key="item.phonenumber" :label="item.userName" :value="item.phonenumber" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="分享权限" label-position="right">
|
||||
<el-checkbox-group v-model="cEdit.permission">
|
||||
<el-checkbox :label="item.label" v-for="item in power" :value="item.value" />
|
||||
</el-checkbox-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="验证码" label-position="right">
|
||||
<el-input v-model="cEdit.smsCode" placeholder="验证码">
|
||||
<template #append
|
||||
><div class="btnSendSms" @click.stop="sendSms">{{ time > 0 ? time + '秒后重发' : '发送验证码' }}</div></template
|
||||
>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" label-position="right">
|
||||
<el-input v-model="cEdit.remark" placeholder="请输入备注" type="textarea" show-word-limit :rows="5" maxlength="50" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
<div class="center" style="margin-top: 10px">
|
||||
<el-button type="primary" @click="SaveFormData('add')"> 确定 </el-button>
|
||||
<el-button @click="CloseEdit()"> 取消 </el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 提示框 -->
|
||||
<el-dialog :width="300" :draggable="true" v-model="Status.confirm.Visible" :title="Status.confirm.title" width="500" center>
|
||||
<span>
|
||||
{{ Status.confirm.text }}
|
||||
</span>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button type="primary" @click="Status.confirm.OkCallback"> 确定 </el-button>
|
||||
<el-button v-show="Status.confirm.showCancel" @click="Status.confirm.cancelCallback">取消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import request from '@/utils/request';
|
||||
import common from '@/utils/common';
|
||||
import api from '@/api/equipmentManagement/device/shareManage';
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
acIndex: {
|
||||
type: Number,
|
||||
required: true
|
||||
}
|
||||
});
|
||||
|
||||
var filter = ref({
|
||||
Date: [],
|
||||
phonenumber: ''
|
||||
});
|
||||
|
||||
var Status = reactive({
|
||||
loading: false,
|
||||
fullLoading: false,
|
||||
confirm: {
|
||||
//弹出框的配置
|
||||
Visible: false,
|
||||
title: '',
|
||||
text: '',
|
||||
cancelCallback: null,
|
||||
OkCallback: null,
|
||||
showCancel: true
|
||||
},
|
||||
ShowEditPop: false, //是否显示编辑弹窗
|
||||
ShowPowerSet: false //是否显示权限管理
|
||||
});
|
||||
|
||||
var deviceid = null;
|
||||
|
||||
var data = ref({});
|
||||
|
||||
var grid = ref(null);
|
||||
//主数据
|
||||
var List = ref([]);
|
||||
|
||||
//所有用户
|
||||
var Usrs = ref([]);
|
||||
|
||||
var pagin = ref({
|
||||
total: 0,
|
||||
pageIndex: 1,
|
||||
pageSize: 10
|
||||
});
|
||||
|
||||
//当前正在编辑的数据
|
||||
var cEdit = reactive({
|
||||
phonenumber: '',
|
||||
permission: [],
|
||||
remark: '',
|
||||
smsCode: ''
|
||||
});
|
||||
|
||||
//权限字典
|
||||
var dic = reactive({
|
||||
'1': '灯光模式',
|
||||
'2': '激光模式',
|
||||
'3': '开机画面',
|
||||
'4': '人员信息登记',
|
||||
'5': '发送信息',
|
||||
'6': '产品信息'
|
||||
});
|
||||
|
||||
var power = computed(() => {
|
||||
let arr = [];
|
||||
let keys = Object.keys(dic);
|
||||
keys.forEach((key) => {
|
||||
arr.push({ label: dic[key], value: key });
|
||||
});
|
||||
return arr;
|
||||
});
|
||||
//打开编辑
|
||||
function ShowEdit() {
|
||||
Status.ShowEditPop = true;
|
||||
getUsrs();
|
||||
}
|
||||
//关闭编辑
|
||||
function CloseEdit() {
|
||||
Status.ShowEditPop = false;
|
||||
Status.ShowPowerSet = false;
|
||||
}
|
||||
|
||||
//添加分享
|
||||
|
||||
//保存分享数据
|
||||
function SaveFormData(type) {
|
||||
let data = {
|
||||
'deviceId': deviceid,
|
||||
'phonenumber': cEdit.phonenumber,
|
||||
'permission': cEdit.permission.join(','),
|
||||
'shareUser': '',
|
||||
'remark': cEdit.remark,
|
||||
'smsCode': cEdit.smsCode
|
||||
};
|
||||
|
||||
Status.fullLoading = true;
|
||||
api
|
||||
.SaveShare(data, type)
|
||||
.then((res) => {
|
||||
if (res.code == 200) {
|
||||
getRecordList();
|
||||
CloseEdit();
|
||||
}
|
||||
alert(res.msg);
|
||||
})
|
||||
.catch((ex) => {
|
||||
console.log('出现异常', ex);
|
||||
})
|
||||
.finally(() => {
|
||||
Status.fullLoading = false;
|
||||
cEdit.phonenumber = '';
|
||||
cEdit.permission = [];
|
||||
cEdit.remark = '';
|
||||
cEdit.smsCode = '';
|
||||
});
|
||||
}
|
||||
|
||||
function getPower(item) {
|
||||
let str = [];
|
||||
if (item && item.permission) {
|
||||
let arr = item.permission.split(',');
|
||||
|
||||
arr.forEach((k) => {
|
||||
str.push(dic[k]);
|
||||
});
|
||||
}
|
||||
|
||||
return str.join(',');
|
||||
}
|
||||
//批量删除
|
||||
|
||||
var selectRows = computed(() => {
|
||||
let arr = getSelectionRows(grid);
|
||||
return arr;
|
||||
});
|
||||
|
||||
function DelShare(item, isBatch) {
|
||||
let arr = [];
|
||||
if (isBatch) {
|
||||
arr = getSelectionRows(grid);
|
||||
} else {
|
||||
arr = [item];
|
||||
}
|
||||
if (arr.length === 0) {
|
||||
alert('请选择需要删除的数据');
|
||||
return;
|
||||
}
|
||||
debugger;
|
||||
let OkDel = () => {
|
||||
hideConfirm();
|
||||
Status.fullLoading = true;
|
||||
setTimeout(() => {
|
||||
let ids = arr
|
||||
.map((v) => {
|
||||
return v.id;
|
||||
})
|
||||
.join(',');
|
||||
api
|
||||
.DelShare(ids)
|
||||
.then((res) => {
|
||||
if (res.code == 200) {
|
||||
getRecordList();
|
||||
}
|
||||
hideConfirm();
|
||||
alert(res.msg);
|
||||
})
|
||||
.catch((ex) => {
|
||||
console.log('ex=', ex);
|
||||
})
|
||||
.finally(() => {
|
||||
Status.fullLoading = false;
|
||||
});
|
||||
}, 0);
|
||||
};
|
||||
confirm('您确认删除选择的' + arr.length + '条数据吗?', OkDel, hideConfirm, '提示');
|
||||
}
|
||||
|
||||
let timeOut = null;
|
||||
let time = ref(0);
|
||||
let timeIntval = null;
|
||||
function sendSms() {
|
||||
if (time.value > 0) {
|
||||
return;
|
||||
}
|
||||
if (timeIntval) {
|
||||
return;
|
||||
}
|
||||
clearTimeout(timeOut);
|
||||
timeOut = setTimeout(() => {
|
||||
api
|
||||
.sendSms(cEdit.phonenumber)
|
||||
.then((res) => {
|
||||
if (res.code == 200) {
|
||||
console.log('res=', res);
|
||||
time.value = 60;
|
||||
timeIntval = setInterval(() => {
|
||||
time.value = time.value - 1;
|
||||
if (time.value == 0) {
|
||||
clearInterval(timeIntval);
|
||||
timeIntval = null;
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
})
|
||||
.catch((ex) => {
|
||||
console.log('ex=', ex);
|
||||
})
|
||||
.finally(() => {});
|
||||
}, 500);
|
||||
}
|
||||
//权限设置
|
||||
function powerSet(item) {
|
||||
debugger;
|
||||
Status.ShowPowerSet = true;
|
||||
cEdit.permission = item.permission.split(',');
|
||||
cEdit.phonenumber = item.phonenumber;
|
||||
cEdit.remark = item.remark;
|
||||
cEdit.smsCode = '123456';
|
||||
}
|
||||
|
||||
function getUsrs() {
|
||||
if (Usrs.value && Usrs.value.length > 0) {
|
||||
return;
|
||||
}
|
||||
api.getUsrs().then((res) => {
|
||||
if (res.code == 200) {
|
||||
Usrs.value = res.rows;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function RowSelectionChange(row) {}
|
||||
var getSelectionRows = (gridInstance) => {
|
||||
if (gridInstance.value) {
|
||||
// 检查ref是否已正确引用组件实例
|
||||
var selectedRows = gridInstance.value.getSelectionRows(); // 获取选中行数据数组
|
||||
return selectedRows;
|
||||
}
|
||||
return [];
|
||||
};
|
||||
|
||||
let searchTime=null;
|
||||
function Search(){
|
||||
clearTimeout(searchTime);
|
||||
searchTime=setTimeout(()=>{
|
||||
getRecordList();
|
||||
},500);
|
||||
}
|
||||
|
||||
function getRecordList() {
|
||||
Status.loading = true;
|
||||
|
||||
setTimeout(() => {
|
||||
initData(props.data, true);
|
||||
}, 0);
|
||||
}
|
||||
|
||||
function initData(item, Refresh) {
|
||||
if (deviceid == item.data.deviceId && !Refresh) {
|
||||
Status.loading = false;
|
||||
return;
|
||||
}
|
||||
debugger;
|
||||
List.value = [];
|
||||
|
||||
deviceid = item.data.deviceId;
|
||||
let params = {
|
||||
deviceId: item.data.deviceId,
|
||||
shareStartTime: '',
|
||||
shareEndTime: '',
|
||||
phonenumber: filter.value.phonenumber,
|
||||
pageSize: pagin.value.pageSize,
|
||||
pageNum: pagin.value.pageIndex,
|
||||
orderByColumn: 'createTime',
|
||||
isAsc: 'descending'
|
||||
};
|
||||
|
||||
if (filter.value.Date && filter.value.Date.length) {
|
||||
params.shareStartTime = common.DateFormat(filter.value.Date[0], null);
|
||||
params.shareEndTime = common.DateFormat(filter.value.Date.length > 1 ? filter.value.Date[1] : '', null);
|
||||
}
|
||||
|
||||
api
|
||||
.searchShare(params)
|
||||
.then((res) => {
|
||||
if (res.code == 200) {
|
||||
if (res.rows) {
|
||||
if (res.rows.length) {
|
||||
List.value = res.rows;
|
||||
}
|
||||
}
|
||||
pagin.value.total = res.total;
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
data.value = item.data;
|
||||
Status.loading = false;
|
||||
});
|
||||
}
|
||||
|
||||
window.confirm = function (text, OK, cancel, title) {
|
||||
let Cfg = {
|
||||
Visible: true,
|
||||
title: title ? title : '提示',
|
||||
text: text ? text : '此操作不可逆,您确定这样做吗?',
|
||||
OkCallback: () => {
|
||||
Status.confirm.Visible = false;
|
||||
if (OK) {
|
||||
OK();
|
||||
}
|
||||
},
|
||||
showCancel: true,
|
||||
cancelCallback: () => {
|
||||
Status.confirm.Visible = false;
|
||||
if (cancel) {
|
||||
cancel();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Status.confirm = Cfg;
|
||||
};
|
||||
|
||||
window.alert = function (text, OK, title) {
|
||||
let Cfg = {
|
||||
Visible: true,
|
||||
title: title ? title : '提示',
|
||||
text: text ? text : '不符合规则',
|
||||
OkCallback: () => {
|
||||
Status.confirm.Visible = false;
|
||||
if (OK) {
|
||||
OK();
|
||||
}
|
||||
},
|
||||
showCancel: false,
|
||||
cancelCallback: null
|
||||
};
|
||||
|
||||
Status.confirm = Cfg;
|
||||
};
|
||||
|
||||
var hideConfirm = function () {
|
||||
let Cfg = {
|
||||
Visible: false,
|
||||
title: '提示',
|
||||
text: '',
|
||||
cancelCallback: null,
|
||||
OkCallback: null,
|
||||
showCancel: false
|
||||
};
|
||||
|
||||
Status.confirm = Cfg;
|
||||
};
|
||||
window.hideConfirm = hideConfirm;
|
||||
window.hideAlert = hideConfirm;
|
||||
|
||||
watch(
|
||||
() => props.acIndex, // 监听的属性
|
||||
(newVal) => {
|
||||
if (newVal === 5) {
|
||||
// 确保参数有效
|
||||
|
||||
console.log('newVal=', newVal);
|
||||
getRecordList(); // 调用回调
|
||||
}
|
||||
},
|
||||
{ immediate: true } // 关键:组件初始化时立即执行一次
|
||||
);
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.content {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
padding: 30px 12px 12px 12px;
|
||||
}
|
||||
.center {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: nowrap;
|
||||
align-content: center;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.pickerContent {
|
||||
box-sizing: border-box;
|
||||
border: 1px solid rgba(189, 198, 215, 0.8);
|
||||
border-radius: 2px;
|
||||
}
|
||||
.topFilter {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: nowrap;
|
||||
align-content: center;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
}
|
||||
:deep .pickerContent .el-date-editor {
|
||||
border: none !important;
|
||||
}
|
||||
|
||||
.marginRight10 {
|
||||
margin-right: 10px;
|
||||
}
|
||||
.mx-1 {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.btnSendSms {
|
||||
background-color: #409eff;
|
||||
color: #ffffff;
|
||||
padding: 0px 20px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
:deep .el-overlay .el-input-group__append {
|
||||
padding: 0px !important;
|
||||
}
|
||||
|
||||
:deep .topFilter .el-input-group__append {
|
||||
padding: 0px 10px !important;
|
||||
}
|
||||
</style>
|
1049
src/views/equipmentManagement/repairRecords/index.vue
Normal file
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="content">
|
||||
<div class="content" >
|
||||
<div class="leftTree fleft">
|
||||
<div class="head">
|
||||
<div class="title">设备分组</div>
|
||||
@ -95,7 +95,7 @@
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<el-table ref="grid" height="calc(100vh - 320px)" :v-loading="Status.loading" border :data="deviceList"
|
||||
<el-table ref="grid" height="calc(100vh - 320px)" v-loading="Status.loading" border :data="deviceList"
|
||||
@selection-change="RowSelectionChange">
|
||||
<el-table-column type="selection" width="50" align="center" />
|
||||
<el-table-column label="设备名称" align="center" prop="deviceName" />
|
||||
@ -144,7 +144,7 @@
|
||||
|
||||
|
||||
<!-- 提示框 -->
|
||||
<el-dialog :draggable="true" v-model="Status.confirm.Visible" :title="Status.confirm.title" width="500" center>
|
||||
<el-dialog :width="300" :draggable="true" v-model="Status.confirm.Visible" :title="Status.confirm.title" width="500" center>
|
||||
<span>
|
||||
{{ Status.confirm.text }}
|
||||
</span>
|
||||
@ -200,11 +200,9 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { status } from 'nprogress';
|
||||
|
||||
import api from '@/api/equipmentManagement/devicegroup/index';
|
||||
import { stat } from 'fs';
|
||||
import { func } from 'vue-types';
|
||||
import { deviceTypeAll } from '@/api/equipmentManagement/device';
|
||||
import common from '@/utils/common'
|
||||
|
||||
interface Device {
|
||||
deviceName: string;
|
||||
@ -318,14 +316,18 @@ var hideConfirm = function () {
|
||||
}
|
||||
|
||||
var showloading = function () {
|
||||
window.loadingIndex = ElLoading.service({ fullscreen: true })
|
||||
// window.loadingIndex = ElLoading.service({ fullscreen: true })
|
||||
|
||||
Status.loading=true;
|
||||
|
||||
}
|
||||
|
||||
var closeLoading = function () {
|
||||
if (window.loadingIndex) {
|
||||
window.loadingIndex.close();
|
||||
}
|
||||
// if (window.loadingIndex) {
|
||||
// window.loadingIndex.close();
|
||||
// }
|
||||
|
||||
Status.loading=false;
|
||||
}
|
||||
var hideloading = closeLoading;
|
||||
|
||||
@ -363,7 +365,7 @@ var getDeviceList = () => {
|
||||
}
|
||||
//树控件节点点击事件
|
||||
var handleNodeClick = (node) => {
|
||||
debugger;
|
||||
|
||||
if (checkNode.val == node.id) {
|
||||
return;
|
||||
}
|
||||
@ -408,7 +410,7 @@ var editNode = (node) => {
|
||||
var completeEdit = (node) => {
|
||||
|
||||
|
||||
let now = DateFormat(new Date(), "yyyy-MM-dd HH:mm:ss");
|
||||
let now =common.DateFormat(new Date(), "yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
let newNode =
|
||||
{
|
||||
@ -453,13 +455,13 @@ var cancelEdit = (node) => {
|
||||
|
||||
//删除节点
|
||||
var delNode = (node) => {
|
||||
debugger;
|
||||
|
||||
if (node.data.children && node.data.children.length > 0) {
|
||||
alert("节点包含子节点,不允许删除");
|
||||
return;
|
||||
}
|
||||
confirm("您确认删除’" + node.data.groupName + "’节点吗", () => {
|
||||
debugger;
|
||||
|
||||
hideConfirm();
|
||||
|
||||
api.delTreeNode(treeDataOrin.value, node.data.id).then((res) => {
|
||||
@ -478,48 +480,6 @@ var delNode = (node) => {
|
||||
|
||||
}
|
||||
|
||||
var DateFormat = function (date, format) {
|
||||
if (!date) {
|
||||
date = new Date();
|
||||
}
|
||||
if (!format) {
|
||||
format = 'yyyy-MM-dd HH:mm:ss';
|
||||
}
|
||||
// 处理参数默认值
|
||||
if (typeof date === 'string' || typeof date === 'number') {
|
||||
date = new Date(date);
|
||||
}
|
||||
date = date instanceof Date ? date : new Date();
|
||||
format = format || 'yyyy-MM-dd';
|
||||
|
||||
// 检查日期是否有效
|
||||
if (isNaN(date.getTime())) {
|
||||
return 'Invalid Date';
|
||||
}
|
||||
|
||||
// 定义格式化映射
|
||||
const formatMap = {
|
||||
'yyyy': date.getFullYear(),
|
||||
'MM': String(date.getMonth() + 1).padStart(2, '0'),
|
||||
'dd': String(date.getDate()).padStart(2, '0'),
|
||||
'HH': String(date.getHours()).padStart(2, '0'),
|
||||
'mm': String(date.getMinutes()).padStart(2, '0'),
|
||||
'ss': String(date.getSeconds()).padStart(2, '0'),
|
||||
'SSS': String(date.getMilliseconds()).padStart(3, '0'),
|
||||
'M': date.getMonth() + 1,
|
||||
'd': date.getDate(),
|
||||
'H': date.getHours(),
|
||||
'm': date.getMinutes(),
|
||||
's': date.getSeconds(),
|
||||
'S': date.getMilliseconds()
|
||||
};
|
||||
|
||||
// 替换格式字符串中的占位符
|
||||
return format.replace(/(yyyy|MM|dd|HH|mm|ss|SSS|M|d|H|m|s|S)/g, (match) => {
|
||||
return formatMap[match];
|
||||
});
|
||||
}
|
||||
|
||||
//增加节点
|
||||
var addNode = (node) => {
|
||||
let title = '';
|
||||
@ -533,7 +493,7 @@ var addNode = (node) => {
|
||||
}
|
||||
let val = new Date().getTime();
|
||||
// let newNode = { value: val, txt: '' };
|
||||
let now = DateFormat(new Date(), "yyyy-MM-dd HH:mm:ss");
|
||||
let now =common.DateFormat(new Date(), "yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
|
||||
|
||||
@ -543,7 +503,7 @@ var addNode = (node) => {
|
||||
|
||||
})
|
||||
.then(({ value }) => {
|
||||
debugger;
|
||||
|
||||
if (!value) {
|
||||
alert("请输入分组名称");
|
||||
return;
|
||||
@ -596,7 +556,7 @@ var defaultProps = {
|
||||
}
|
||||
|
||||
var RowSelectionChange = (tr) => {
|
||||
debugger;
|
||||
|
||||
console.log("行选中变化", tr);
|
||||
getSelectionRows();
|
||||
};
|
||||
@ -692,7 +652,7 @@ var showDevice = () => {
|
||||
if (res[1].status == 'fulfilled') {
|
||||
let res2 = res[1].value;
|
||||
if (res2.code == 200) {
|
||||
debugger;
|
||||
|
||||
let vals = res2.rows.map(item => item.deviceId);
|
||||
|
||||
transfer.value = vals;
|
||||
@ -710,7 +670,7 @@ var showDevice = () => {
|
||||
//确认选择这些设备
|
||||
var OKCheckDevice = () => {
|
||||
|
||||
debugger;
|
||||
|
||||
|
||||
let arr = transfer.value.map(item => ({
|
||||
deviceId: item
|
||||
|