@ -0,0 +1,46 @@ |
|||||
|
package com.genersoft.iot.vmp.gb28181.bean; |
||||
|
|
||||
|
/** |
||||
|
* 通过redis分发报警消息 |
||||
|
*/ |
||||
|
public class AlarmChannelMessage { |
||||
|
/** |
||||
|
* 国标编号 |
||||
|
*/ |
||||
|
private String gbId; |
||||
|
|
||||
|
/** |
||||
|
* 报警编号 |
||||
|
*/ |
||||
|
private int alarmSn; |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 报警描述 |
||||
|
*/ |
||||
|
private String alarmDescription; |
||||
|
|
||||
|
public String getGbId() { |
||||
|
return gbId; |
||||
|
} |
||||
|
|
||||
|
public void setGbId(String gbId) { |
||||
|
this.gbId = gbId; |
||||
|
} |
||||
|
|
||||
|
public int getAlarmSn() { |
||||
|
return alarmSn; |
||||
|
} |
||||
|
|
||||
|
public void setAlarmSn(int alarmSn) { |
||||
|
this.alarmSn = alarmSn; |
||||
|
} |
||||
|
|
||||
|
public String getAlarmDescription() { |
||||
|
return alarmDescription; |
||||
|
} |
||||
|
|
||||
|
public void setAlarmDescription(String alarmDescription) { |
||||
|
this.alarmDescription = alarmDescription; |
||||
|
} |
||||
|
} |
@ -0,0 +1,126 @@ |
|||||
|
package com.genersoft.iot.vmp.gb28181.utils; |
||||
|
|
||||
|
/** |
||||
|
* 坐标转换 |
||||
|
* 一个提供了百度坐标(BD09)、国测局坐标(火星坐标,GCJ02)、和WGS84坐标系之间的转换的工具类 |
||||
|
* 参考https://github.com/wandergis/coordtransform 写的Java版本
|
||||
|
* @author Xinconan |
||||
|
* @date 2016-03-18 |
||||
|
* @url https://github.com/xinconan/coordtransform
|
||||
|
*/ |
||||
|
public class Coordtransform { |
||||
|
|
||||
|
private static double x_PI = 3.14159265358979324 * 3000.0 / 180.0; |
||||
|
private static double PI = 3.1415926535897932384626; |
||||
|
private static double a = 6378245.0; |
||||
|
private static double ee = 0.00669342162296594323; |
||||
|
|
||||
|
/** |
||||
|
* 百度坐标系 (BD-09) 与 火星坐标系 (GCJ-02)的转换 |
||||
|
* 即 百度 转 谷歌、高德 |
||||
|
* @param bd_lon |
||||
|
* @param bd_lat |
||||
|
* @return Double[lon,lat] |
||||
|
*/ |
||||
|
public static Double[] BD09ToGCJ02(Double bd_lon,Double bd_lat){ |
||||
|
double x = bd_lon - 0.0065; |
||||
|
double y = bd_lat - 0.006; |
||||
|
double z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * x_PI); |
||||
|
double theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * x_PI); |
||||
|
Double[] arr = new Double[2]; |
||||
|
arr[0] = z * Math.cos(theta); |
||||
|
arr[1] = z * Math.sin(theta); |
||||
|
return arr; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 火星坐标系 (GCJ-02) 与百度坐标系 (BD-09) 的转换 |
||||
|
* 即谷歌、高德 转 百度 |
||||
|
* @param gcj_lon |
||||
|
* @param gcj_lat |
||||
|
* @return Double[lon,lat] |
||||
|
*/ |
||||
|
public static Double[] GCJ02ToBD09(Double gcj_lon,Double gcj_lat){ |
||||
|
double z = Math.sqrt(gcj_lon * gcj_lon + gcj_lat * gcj_lat) + 0.00002 * Math.sin(gcj_lat * x_PI); |
||||
|
double theta = Math.atan2(gcj_lat, gcj_lon) + 0.000003 * Math.cos(gcj_lon * x_PI); |
||||
|
Double[] arr = new Double[2]; |
||||
|
arr[0] = z * Math.cos(theta) + 0.0065; |
||||
|
arr[1] = z * Math.sin(theta) + 0.006; |
||||
|
return arr; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* WGS84转GCJ02 |
||||
|
* @param wgs_lon |
||||
|
* @param wgs_lat |
||||
|
* @return Double[lon,lat] |
||||
|
*/ |
||||
|
public static Double[] WGS84ToGCJ02(Double wgs_lon,Double wgs_lat){ |
||||
|
if(outOfChina(wgs_lon, wgs_lat)){ |
||||
|
return new Double[]{wgs_lon,wgs_lat}; |
||||
|
} |
||||
|
double dlat = transformlat(wgs_lon - 105.0, wgs_lat - 35.0); |
||||
|
double dlng = transformlng(wgs_lon - 105.0, wgs_lat - 35.0); |
||||
|
double radlat = wgs_lat / 180.0 * PI; |
||||
|
double magic = Math.sin(radlat); |
||||
|
magic = 1 - ee * magic * magic; |
||||
|
double sqrtmagic = Math.sqrt(magic); |
||||
|
dlat = (dlat * 180.0) / ((a * (1 - ee)) / (magic * sqrtmagic) * PI); |
||||
|
dlng = (dlng * 180.0) / (a / sqrtmagic * Math.cos(radlat) * PI); |
||||
|
Double[] arr = new Double[2]; |
||||
|
arr[0] = wgs_lon + dlng; |
||||
|
arr[1] = wgs_lat + dlat; |
||||
|
return arr; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* GCJ02转WGS84 |
||||
|
* @param gcj_lon |
||||
|
* @param gcj_lat |
||||
|
* @return Double[lon,lat] |
||||
|
*/ |
||||
|
public static Double[] GCJ02ToWGS84(Double gcj_lon,Double gcj_lat){ |
||||
|
if(outOfChina(gcj_lon, gcj_lat)){ |
||||
|
return new Double[]{gcj_lon,gcj_lat}; |
||||
|
} |
||||
|
double dlat = transformlat(gcj_lon - 105.0, gcj_lat - 35.0); |
||||
|
double dlng = transformlng(gcj_lon - 105.0, gcj_lat - 35.0); |
||||
|
double radlat = gcj_lat / 180.0 * PI; |
||||
|
double magic = Math.sin(radlat); |
||||
|
magic = 1 - ee * magic * magic; |
||||
|
double sqrtmagic = Math.sqrt(magic); |
||||
|
dlat = (dlat * 180.0) / ((a * (1 - ee)) / (magic * sqrtmagic) * PI); |
||||
|
dlng = (dlng * 180.0) / (a / sqrtmagic * Math.cos(radlat) * PI); |
||||
|
double mglat = gcj_lat + dlat; |
||||
|
double mglng = gcj_lon + dlng; |
||||
|
return new Double[]{gcj_lon * 2 - mglng, gcj_lat * 2 - mglat}; |
||||
|
} |
||||
|
|
||||
|
private static Double transformlat(double lng, double lat) { |
||||
|
double ret = -100.0 + 2.0 * lng + 3.0 * lat + 0.2 * lat * lat + 0.1 * lng * lat + 0.2 * Math.sqrt(Math.abs(lng)); |
||||
|
ret += (20.0 * Math.sin(6.0 * lng * PI) + 20.0 * Math.sin(2.0 * lng * PI)) * 2.0 / 3.0; |
||||
|
ret += (20.0 * Math.sin(lat * PI) + 40.0 * Math.sin(lat / 3.0 * PI)) * 2.0 / 3.0; |
||||
|
ret += (160.0 * Math.sin(lat / 12.0 * PI) + 320 * Math.sin(lat * PI / 30.0)) * 2.0 / 3.0; |
||||
|
return ret; |
||||
|
} |
||||
|
|
||||
|
private static Double transformlng(double lng,double lat) { |
||||
|
double ret = 300.0 + lng + 2.0 * lat + 0.1 * lng * lng + 0.1 * lng * lat + 0.1 * Math.sqrt(Math.abs(lng)); |
||||
|
ret += (20.0 * Math.sin(6.0 * lng * PI) + 20.0 * Math.sin(2.0 * lng * PI)) * 2.0 / 3.0; |
||||
|
ret += (20.0 * Math.sin(lng * PI) + 40.0 * Math.sin(lng / 3.0 * PI)) * 2.0 / 3.0; |
||||
|
ret += (150.0 * Math.sin(lng / 12.0 * PI) + 300.0 * Math.sin(lng / 30.0 * PI)) * 2.0 / 3.0; |
||||
|
return ret; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* outOfChina |
||||
|
* @描述: 判断是否在国内,不在国内则不做偏移 |
||||
|
* @param lng |
||||
|
* @param lat |
||||
|
* @return {boolean} |
||||
|
*/ |
||||
|
private static boolean outOfChina(Double lng,Double lat) { |
||||
|
return (lng < 72.004 || lng > 137.8347) || ((lat < 0.8293 || lat > 55.8271) || false); |
||||
|
}; |
||||
|
|
||||
|
} |
@ -0,0 +1,69 @@ |
|||||
|
package com.genersoft.iot.vmp.service.impl; |
||||
|
|
||||
|
import com.alibaba.fastjson.JSON; |
||||
|
import com.genersoft.iot.vmp.gb28181.bean.*; |
||||
|
import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander; |
||||
|
import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform; |
||||
|
import com.genersoft.iot.vmp.service.bean.GPSMsgInfo; |
||||
|
import com.genersoft.iot.vmp.storager.IRedisCatchStorage; |
||||
|
import com.genersoft.iot.vmp.storager.IVideoManagerStorage; |
||||
|
import com.genersoft.iot.vmp.vmanager.bean.WVPResult; |
||||
|
import org.slf4j.Logger; |
||||
|
import org.slf4j.LoggerFactory; |
||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
import org.springframework.data.redis.connection.Message; |
||||
|
import org.springframework.data.redis.connection.MessageListener; |
||||
|
import org.springframework.http.HttpStatus; |
||||
|
import org.springframework.http.ResponseEntity; |
||||
|
import org.springframework.stereotype.Component; |
||||
|
|
||||
|
import java.text.SimpleDateFormat; |
||||
|
|
||||
|
@Component |
||||
|
public class RedisAlarmMsgListener implements MessageListener { |
||||
|
|
||||
|
private final static Logger logger = LoggerFactory.getLogger(RedisAlarmMsgListener.class); |
||||
|
|
||||
|
@Autowired |
||||
|
private ISIPCommander commander; |
||||
|
|
||||
|
@Autowired |
||||
|
private ISIPCommanderForPlatform commanderForPlatform; |
||||
|
|
||||
|
@Autowired |
||||
|
private IVideoManagerStorage storage; |
||||
|
|
||||
|
private final SimpleDateFormat formatForGB = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); |
||||
|
|
||||
|
@Override |
||||
|
public void onMessage(Message message, byte[] bytes) { |
||||
|
logger.info("收到来自REDIS的ALARM通知: {}", new String(message.getBody())); |
||||
|
AlarmChannelMessage alarmChannelMessage = JSON.parseObject(message.getBody(), AlarmChannelMessage.class); |
||||
|
if (alarmChannelMessage == null) { |
||||
|
logger.warn("[REDIS的ALARM通知]消息解析失败"); |
||||
|
return; |
||||
|
} |
||||
|
String gbId = alarmChannelMessage.getGbId(); |
||||
|
Device device = storage.queryVideoDevice(gbId); |
||||
|
ParentPlatform platform = storage.queryParentPlatByServerGBId(gbId); |
||||
|
|
||||
|
DeviceAlarm deviceAlarm = new DeviceAlarm(); |
||||
|
deviceAlarm.setChannelId(gbId); |
||||
|
deviceAlarm.setAlarmDescription(alarmChannelMessage.getAlarmDescription()); |
||||
|
deviceAlarm.setAlarmMethod("" + alarmChannelMessage.getAlarmSn()); |
||||
|
deviceAlarm.setAlarmPriority("1"); |
||||
|
deviceAlarm.setAlarmTime(formatForGB.format(System.currentTimeMillis())); |
||||
|
deviceAlarm.setAlarmType("1"); |
||||
|
deviceAlarm.setLongitude(0); |
||||
|
deviceAlarm.setLatitude(0); |
||||
|
|
||||
|
|
||||
|
if (device != null && platform == null) { |
||||
|
commander.sendAlarmMessage(device, deviceAlarm); |
||||
|
}else if (device == null && platform != null){ |
||||
|
commanderForPlatform.sendAlarmMessage(platform, deviceAlarm); |
||||
|
}else { |
||||
|
logger.warn("无法确定" + gbId + "是平台还是设备"); |
||||
|
} |
||||
|
} |
||||
|
} |
@ -1,74 +0,0 @@ |
|||||
package com.genersoft.iot.vmp.utils.node; |
|
||||
|
|
||||
import com.fasterxml.jackson.annotation.JsonInclude; |
|
||||
|
|
||||
import java.util.ArrayList; |
|
||||
import java.util.List; |
|
||||
|
|
||||
/** |
|
||||
* 节点基类 |
|
||||
* |
|
||||
*/ |
|
||||
public class BaseNode<T> implements INode<T> { |
|
||||
|
|
||||
private static final long serialVersionUID = 1L; |
|
||||
|
|
||||
/** |
|
||||
* 主键ID |
|
||||
*/ |
|
||||
protected String channelId; |
|
||||
|
|
||||
/** |
|
||||
* 父节点ID |
|
||||
*/ |
|
||||
protected String parentId; |
|
||||
|
|
||||
/** |
|
||||
* 子孙节点 |
|
||||
*/ |
|
||||
@JsonInclude(JsonInclude.Include.NON_EMPTY) |
|
||||
protected List<T> children = new ArrayList<T>(); |
|
||||
|
|
||||
/** |
|
||||
* 是否有子孙节点 |
|
||||
*/ |
|
||||
@JsonInclude(JsonInclude.Include.NON_EMPTY) |
|
||||
private Boolean hasChildren; |
|
||||
|
|
||||
/** |
|
||||
* 是否有子孙节点 |
|
||||
* |
|
||||
* @return Boolean |
|
||||
*/ |
|
||||
@Override |
|
||||
public Boolean getHasChildren() { |
|
||||
if (children.size() > 0) { |
|
||||
return true; |
|
||||
} else { |
|
||||
return this.hasChildren; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
@Override |
|
||||
public String getChannelId() { |
|
||||
return channelId; |
|
||||
} |
|
||||
|
|
||||
@Override |
|
||||
public String getParentId() { |
|
||||
return parentId; |
|
||||
} |
|
||||
|
|
||||
@Override |
|
||||
public List<T> getChildren() { |
|
||||
return children; |
|
||||
} |
|
||||
|
|
||||
public void setChildren(List<T> children) { |
|
||||
this.children = children; |
|
||||
} |
|
||||
|
|
||||
public void setHasChildren(Boolean hasChildren) { |
|
||||
this.hasChildren = hasChildren; |
|
||||
} |
|
||||
} |
|
@ -1,31 +0,0 @@ |
|||||
package com.genersoft.iot.vmp.utils.node; |
|
||||
|
|
||||
|
|
||||
|
|
||||
/** |
|
||||
* 森林节点类 |
|
||||
* |
|
||||
*/ |
|
||||
public class ForestNode extends BaseNode<ForestNode> { |
|
||||
|
|
||||
private static final long serialVersionUID = 1L; |
|
||||
|
|
||||
/** |
|
||||
* 节点内容 |
|
||||
*/ |
|
||||
private Object content; |
|
||||
|
|
||||
public ForestNode(String id, String parentId, Object content) { |
|
||||
this.channelId = id; |
|
||||
this.parentId = parentId; |
|
||||
this.content = content; |
|
||||
} |
|
||||
|
|
||||
public Object getContent() { |
|
||||
return content; |
|
||||
} |
|
||||
|
|
||||
public void setContent(Object content) { |
|
||||
this.content = content; |
|
||||
} |
|
||||
} |
|
@ -1,68 +0,0 @@ |
|||||
package com.genersoft.iot.vmp.utils.node; |
|
||||
|
|
||||
import com.google.common.collect.ImmutableMap; |
|
||||
import com.google.common.collect.Maps; |
|
||||
|
|
||||
import java.util.ArrayList; |
|
||||
import java.util.List; |
|
||||
import java.util.Map; |
|
||||
|
|
||||
/** |
|
||||
* 森林管理类 |
|
||||
* |
|
||||
* @author smallchill |
|
||||
*/ |
|
||||
public class ForestNodeManager<T extends INode<T>> { |
|
||||
|
|
||||
/** |
|
||||
* 森林的所有节点 |
|
||||
*/ |
|
||||
private final ImmutableMap<String, T> nodeMap; |
|
||||
|
|
||||
/** |
|
||||
* 森林的父节点ID |
|
||||
*/ |
|
||||
private final Map<String, Object> parentIdMap = Maps.newHashMap(); |
|
||||
|
|
||||
public ForestNodeManager(List<T> nodes) { |
|
||||
nodeMap = Maps.uniqueIndex(nodes, INode::getChannelId); |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* 根据节点ID获取一个节点 |
|
||||
* |
|
||||
* @param id 节点ID |
|
||||
* @return 对应的节点对象 |
|
||||
*/ |
|
||||
public INode<T> getTreeNodeAt(String id) { |
|
||||
if (nodeMap.containsKey(id)) { |
|
||||
return nodeMap.get(id); |
|
||||
} |
|
||||
return null; |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* 增加父节点ID |
|
||||
* |
|
||||
* @param parentId 父节点ID |
|
||||
*/ |
|
||||
public void addParentId(String parentId) { |
|
||||
parentIdMap.put(parentId, ""); |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* 获取树的根节点(一个森林对应多颗树) |
|
||||
* |
|
||||
* @return 树的根节点集合 |
|
||||
*/ |
|
||||
public List<T> getRoot() { |
|
||||
List<T> roots = new ArrayList<>(); |
|
||||
nodeMap.forEach((key, node) -> { |
|
||||
if (node.getParentId() == null || parentIdMap.containsKey(node.getChannelId())) { |
|
||||
roots.add(node); |
|
||||
} |
|
||||
}); |
|
||||
return roots; |
|
||||
} |
|
||||
|
|
||||
} |
|
@ -1,51 +0,0 @@ |
|||||
package com.genersoft.iot.vmp.utils.node; |
|
||||
|
|
||||
import com.genersoft.iot.vmp.utils.CollectionUtil; |
|
||||
|
|
||||
import java.util.List; |
|
||||
|
|
||||
/** |
|
||||
* 森林节点归并类 |
|
||||
* |
|
||||
*/ |
|
||||
public class ForestNodeMerger { |
|
||||
|
|
||||
/** |
|
||||
* 将节点数组归并为一个森林(多棵树)(填充节点的children域) |
|
||||
* 时间复杂度为O(n^2) |
|
||||
* |
|
||||
* @param items 节点域 |
|
||||
* @return 多棵树的根节点集合 |
|
||||
*/ |
|
||||
public static <T extends INode<T>> List<T> merge(List<T> items) { |
|
||||
ForestNodeManager<T> forestNodeManager = new ForestNodeManager<>(items); |
|
||||
items.forEach(forestNode -> { |
|
||||
if (forestNode.getParentId() != null) { |
|
||||
INode<T> node = forestNodeManager.getTreeNodeAt(forestNode.getParentId()); |
|
||||
if (node != null) { |
|
||||
node.getChildren().add(forestNode); |
|
||||
} else { |
|
||||
forestNodeManager.addParentId(forestNode.getChannelId()); |
|
||||
} |
|
||||
} |
|
||||
}); |
|
||||
return forestNodeManager.getRoot(); |
|
||||
} |
|
||||
|
|
||||
public static <T extends INode<T>> List<T> merge(List<T> items, String[] parentIds) { |
|
||||
ForestNodeManager<T> forestNodeManager = new ForestNodeManager<>(items); |
|
||||
items.forEach(forestNode -> { |
|
||||
if (forestNode.getParentId() != null) { |
|
||||
INode<T> node = forestNodeManager.getTreeNodeAt(forestNode.getParentId()); |
|
||||
if (CollectionUtil.contains(parentIds, forestNode.getChannelId())){ |
|
||||
forestNodeManager.addParentId(forestNode.getChannelId()); |
|
||||
} else { |
|
||||
if (node != null){ |
|
||||
node.getChildren().add(forestNode); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
}); |
|
||||
return forestNodeManager.getRoot(); |
|
||||
} |
|
||||
} |
|
@ -1,42 +0,0 @@ |
|||||
package com.genersoft.iot.vmp.utils.node; |
|
||||
|
|
||||
import java.io.Serializable; |
|
||||
import java.util.List; |
|
||||
|
|
||||
/** |
|
||||
* |
|
||||
* 节点 |
|
||||
*/ |
|
||||
public interface INode<T> extends Serializable { |
|
||||
|
|
||||
/** |
|
||||
* 主键 |
|
||||
* |
|
||||
* @return String |
|
||||
*/ |
|
||||
String getChannelId(); |
|
||||
|
|
||||
/** |
|
||||
* 父主键 |
|
||||
* |
|
||||
* @return String |
|
||||
*/ |
|
||||
String getParentId(); |
|
||||
|
|
||||
/** |
|
||||
* 子孙节点 |
|
||||
* |
|
||||
* @return List<T> |
|
||||
*/ |
|
||||
List<T> getChildren(); |
|
||||
|
|
||||
/** |
|
||||
* 是否有子孙节点 |
|
||||
* |
|
||||
* @return Boolean |
|
||||
*/ |
|
||||
default Boolean getHasChildren() { |
|
||||
return false; |
|
||||
} |
|
||||
|
|
||||
} |
|
@ -1,42 +0,0 @@ |
|||||
package com.genersoft.iot.vmp.utils.node; |
|
||||
|
|
||||
|
|
||||
|
|
||||
/** |
|
||||
* 树型节点类 |
|
||||
* |
|
||||
*/ |
|
||||
public class TreeNode extends BaseNode<TreeNode> { |
|
||||
|
|
||||
private static final long serialVersionUID = 1L; |
|
||||
|
|
||||
private String title; |
|
||||
|
|
||||
private String key; |
|
||||
|
|
||||
private String value; |
|
||||
|
|
||||
public String getTitle() { |
|
||||
return title; |
|
||||
} |
|
||||
|
|
||||
public void setTitle(String title) { |
|
||||
this.title = title; |
|
||||
} |
|
||||
|
|
||||
public String getKey() { |
|
||||
return key; |
|
||||
} |
|
||||
|
|
||||
public void setKey(String key) { |
|
||||
this.key = key; |
|
||||
} |
|
||||
|
|
||||
public String getValue() { |
|
||||
return value; |
|
||||
} |
|
||||
|
|
||||
public void setValue(String value) { |
|
||||
this.value = value; |
|
||||
} |
|
||||
} |
|
@ -1,121 +0,0 @@ |
|||||
package com.genersoft.iot.vmp.vmanager.bean; |
|
||||
|
|
||||
import com.fasterxml.jackson.annotation.JsonInclude; |
|
||||
import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; |
|
||||
import com.genersoft.iot.vmp.utils.node.INode; |
|
||||
import io.swagger.annotations.ApiModel; |
|
||||
|
|
||||
import java.util.ArrayList; |
|
||||
import java.util.List; |
|
||||
|
|
||||
@ApiModel(value = "DeviceChannelTree对象", description = "DeviceChannelTree对象") |
|
||||
public class DeviceChannelTree extends DeviceChannel implements INode<DeviceChannelTree> { |
|
||||
private static final long serialVersionUID = 1L; |
|
||||
|
|
||||
/** |
|
||||
* 主键ID |
|
||||
*/ |
|
||||
private int id; |
|
||||
|
|
||||
/** |
|
||||
* 父节点ID |
|
||||
*/ |
|
||||
private String parentId; |
|
||||
|
|
||||
private String parentName; |
|
||||
|
|
||||
private String title; |
|
||||
|
|
||||
private String key; |
|
||||
|
|
||||
private String value; |
|
||||
|
|
||||
/** |
|
||||
* 子孙节点 |
|
||||
*/ |
|
||||
@JsonInclude(JsonInclude.Include.NON_EMPTY) |
|
||||
private List<DeviceChannelTree> children; |
|
||||
|
|
||||
/** |
|
||||
* 是否有子孙节点 |
|
||||
*/ |
|
||||
@JsonInclude(JsonInclude.Include.NON_EMPTY) |
|
||||
private Boolean hasChildren; |
|
||||
|
|
||||
@Override |
|
||||
public List<DeviceChannelTree> getChildren() { |
|
||||
if (this.children == null) { |
|
||||
this.children = new ArrayList<>(); |
|
||||
} |
|
||||
return this.children; |
|
||||
} |
|
||||
|
|
||||
@Override |
|
||||
public Boolean getHasChildren() { |
|
||||
if (children.size() > 0) { |
|
||||
return true; |
|
||||
} else { |
|
||||
return this.hasChildren; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
@Override |
|
||||
public int getId() { |
|
||||
return id; |
|
||||
} |
|
||||
|
|
||||
@Override |
|
||||
public void setId(int id) { |
|
||||
this.id = id; |
|
||||
} |
|
||||
|
|
||||
@Override |
|
||||
public String getParentId() { |
|
||||
return parentId; |
|
||||
} |
|
||||
|
|
||||
@Override |
|
||||
public void setParentId(String parentId) { |
|
||||
this.parentId = parentId; |
|
||||
} |
|
||||
|
|
||||
public String getParentName() { |
|
||||
return parentName; |
|
||||
} |
|
||||
|
|
||||
public void setParentName(String parentName) { |
|
||||
this.parentName = parentName; |
|
||||
} |
|
||||
|
|
||||
public String getTitle() { |
|
||||
return title; |
|
||||
} |
|
||||
|
|
||||
public void setTitle(String title) { |
|
||||
this.title = title; |
|
||||
} |
|
||||
|
|
||||
public String getKey() { |
|
||||
return key; |
|
||||
} |
|
||||
|
|
||||
public void setKey(String key) { |
|
||||
this.key = key; |
|
||||
} |
|
||||
|
|
||||
public String getValue() { |
|
||||
return value; |
|
||||
} |
|
||||
|
|
||||
public void setValue(String value) { |
|
||||
this.value = value; |
|
||||
} |
|
||||
|
|
||||
public void setChildren(List<DeviceChannelTree> children) { |
|
||||
this.children = children; |
|
||||
} |
|
||||
|
|
||||
public void setHasChildren(Boolean hasChildren) { |
|
||||
this.hasChildren = hasChildren; |
|
||||
} |
|
||||
} |
|
@ -1,56 +0,0 @@ |
|||||
package com.genersoft.iot.vmp.vmanager.bean; |
|
||||
|
|
||||
import com.genersoft.iot.vmp.utils.node.TreeNode; |
|
||||
|
|
||||
public class DeviceChannelTreeNode extends TreeNode { |
|
||||
|
|
||||
private Integer status; |
|
||||
|
|
||||
private String deviceId; |
|
||||
|
|
||||
private String channelId; |
|
||||
|
|
||||
private Double lng; |
|
||||
|
|
||||
private Double lat; |
|
||||
|
|
||||
public Integer getStatus() { |
|
||||
return status; |
|
||||
} |
|
||||
|
|
||||
public void setStatus(Integer status) { |
|
||||
this.status = status; |
|
||||
} |
|
||||
|
|
||||
public String getDeviceId() { |
|
||||
return deviceId; |
|
||||
} |
|
||||
|
|
||||
public void setDeviceId(String deviceId) { |
|
||||
this.deviceId = deviceId; |
|
||||
} |
|
||||
|
|
||||
public String getChannelId() { |
|
||||
return channelId; |
|
||||
} |
|
||||
|
|
||||
public void setChannelId(String channelId) { |
|
||||
this.channelId = channelId; |
|
||||
} |
|
||||
|
|
||||
public Double getLng() { |
|
||||
return lng; |
|
||||
} |
|
||||
|
|
||||
public void setLng(Double lng) { |
|
||||
this.lng = lng; |
|
||||
} |
|
||||
|
|
||||
public Double getLat() { |
|
||||
return lat; |
|
||||
} |
|
||||
|
|
||||
public void setLat(Double lat) { |
|
||||
this.lat = lat; |
|
||||
} |
|
||||
} |
|
@ -1,19 +0,0 @@ |
|||||
import axios from 'axios'; |
|
||||
|
|
||||
export const tree = (deviceId) => { |
|
||||
return axios({ |
|
||||
url: `/api/device/query/${deviceId}/tree`, |
|
||||
method: 'get' |
|
||||
}) |
|
||||
} |
|
||||
|
|
||||
export const deviceList = (page, count) => { |
|
||||
return axios({ |
|
||||
method: 'get', |
|
||||
url:`/api/device/query/devices`, |
|
||||
params: { |
|
||||
page, |
|
||||
count |
|
||||
} |
|
||||
}) |
|
||||
} |
|
@ -1,70 +0,0 @@ |
|||||
<template> |
|
||||
<div> |
|
||||
<el-tree :data="channelList" :props="props" @node-click="sendDevicePush"> |
|
||||
<span slot-scope="{ node }"> |
|
||||
<span v-if="node.isLeaf"> |
|
||||
<i class="el-icon-video-camera" :style="{color:node.disabled==1?'#67C23A':'#F56C6C'}"></i> |
|
||||
</span> |
|
||||
<span v-else> |
|
||||
<i class="el-icon-folder"></i> |
|
||||
</span> |
|
||||
<span> |
|
||||
{{ node.label }} |
|
||||
</span> |
|
||||
</span> |
|
||||
</el-tree> |
|
||||
</div> |
|
||||
</template> |
|
||||
<script> |
|
||||
import ChannelTreeItem from "@/components/channelTreeItem" |
|
||||
import {tree} from '@/api/deviceApi' |
|
||||
|
|
||||
export default { |
|
||||
components: { |
|
||||
ChannelTreeItem, |
|
||||
}, |
|
||||
props:{ |
|
||||
device: { |
|
||||
type: Object, |
|
||||
required: true |
|
||||
} |
|
||||
}, |
|
||||
data() { |
|
||||
return { |
|
||||
loading: false, |
|
||||
channelList: [], |
|
||||
props: { |
|
||||
label: 'title', |
|
||||
children: 'children', |
|
||||
isLeaf: 'hasChildren', |
|
||||
disabled: 'status' |
|
||||
}, |
|
||||
} |
|
||||
}, |
|
||||
computed: { |
|
||||
|
|
||||
}, |
|
||||
mounted() { |
|
||||
this.leafs = [] |
|
||||
this.getTree() |
|
||||
}, |
|
||||
methods: { |
|
||||
getTree() { |
|
||||
this.loading = true |
|
||||
var that = this |
|
||||
tree(this.device.deviceId).then(function (res) { |
|
||||
console.log(res.data.data); |
|
||||
that.channelList = res.data.data; |
|
||||
that.loading = false; |
|
||||
}).catch(function (error) { |
|
||||
console.log(error); |
|
||||
that.loading = false; |
|
||||
}); |
|
||||
}, |
|
||||
sendDevicePush(c) { |
|
||||
if(c.hasChildren) return |
|
||||
this.$emit('sendDevicePush',c) |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
</script> |
|
@ -1,74 +0,0 @@ |
|||||
<template> |
|
||||
<div> |
|
||||
<!-- <div :index="item.key" v-for="(item,i) in list" :key="i+'-'"> |
|
||||
<el-submenu v-if="item.hasChildren"> |
|
||||
<template slot="title"> |
|
||||
<i class="el-icon-video-camera"></i> |
|
||||
<span slot="title">{{item.title || item.deviceId}}</span> |
|
||||
</template> |
|
||||
<channel-list :list="item.children" @sendDevicePush="sendDevicePush"></channel-list> |
|
||||
</el-submenu> |
|
||||
<el-menu-item v-else :index="item.key" @click="sendDevicePush(item)"> |
|
||||
<template slot="title" > |
|
||||
<i class="el-icon-switch-button" :style="{color:item.status==1?'#67C23A':'#F56C6C'}"></i> |
|
||||
<span slot="title">{{item.title}}</span> |
|
||||
</template> |
|
||||
</el-menu-item> |
|
||||
</div> --> |
|
||||
<div > |
|
||||
<template v-if="!item.hasChildren"> |
|
||||
<el-menu-item :index="item.key" @click="sendDevicePush(item)"> |
|
||||
<i class="el-icon-video-camera" :style="{color:item.status==1?'#67C23A':'#F56C6C'}"></i> |
|
||||
{{item.title}} |
|
||||
</el-menu-item> |
|
||||
</template> |
|
||||
|
|
||||
<el-submenu v-else :index="item.key"> |
|
||||
<template slot="title" > |
|
||||
<i class="el-icon-location-outline"></i> |
|
||||
{{item.title}} |
|
||||
</template> |
|
||||
|
|
||||
<template v-for="child in item.children"> |
|
||||
<channel-item |
|
||||
v-if="child.hasChildren" |
|
||||
:item="child" |
|
||||
:key="child.key" |
|
||||
@sendDevicePush="sendDevicePush"/> |
|
||||
<el-menu-item v-else :key="child.key" :index="child.key" @click="sendDevicePush(child)"> |
|
||||
<i class="el-icon-video-camera" :style="{color:child.status==1?'#67C23A':'#F56C6C'}"></i> |
|
||||
{{child.title}} |
|
||||
</el-menu-item> |
|
||||
</template> |
|
||||
</el-submenu> |
|
||||
</div> |
|
||||
</div> |
|
||||
</template> |
|
||||
<script> |
|
||||
export default { |
|
||||
name:'ChannelItem', |
|
||||
props:{ |
|
||||
list:Array, |
|
||||
channelId: String, |
|
||||
item: { |
|
||||
type: Object, |
|
||||
required: true |
|
||||
} |
|
||||
}, |
|
||||
data () { |
|
||||
return { |
|
||||
|
|
||||
} |
|
||||
}, |
|
||||
watch: { |
|
||||
channelId(val) { |
|
||||
console.log(val); |
|
||||
} |
|
||||
}, |
|
||||
methods: { |
|
||||
sendDevicePush(c) { |
|
||||
this.$emit('sendDevicePush',c) |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
</script> |
|
@ -0,0 +1,160 @@ |
|||||
|
<template> |
||||
|
<div id="DeviceTree" style="width: 100%;height: 100%; background-color: #FFFFFF; overflow: auto"> |
||||
|
<el-container> |
||||
|
<el-header>设备列表</el-header> |
||||
|
<el-main style="background-color: #ffffff;"> |
||||
|
<div class="device-tree-main-box"> |
||||
|
<el-tree :props="defaultProps" :load="loadNode" lazy @node-click="handleNodeClick"@node-contextmenu="handleContextMenu" style="min-width: 100%; display:inline-block !important;"> |
||||
|
<span class="custom-tree-node" slot-scope="{ node, data }" style="width: 100%"> |
||||
|
<span v-if="node.data.type === 0 && node.data.online" title="在线设备" class="device-online iconfont icon-jiedianleizhukongzhongxin2"></span> |
||||
|
<span v-if="node.data.type === 0 && !node.data.online " title="离线设备" class="device-offline iconfont icon-jiedianleizhukongzhongxin2"></span> |
||||
|
<span v-if="node.data.type === 2 && node.data.online" title="目录" class="device-online iconfont icon-jiedianleilianjipingtai"></span> |
||||
|
<span v-if="node.data.type === 2 && !node.data.online" title="目录" class="device-offline iconfont icon-jiedianleilianjipingtai"></span> |
||||
|
<span v-if="node.data.type === 3 && node.data.online " title="在线通道" class="device-online iconfont icon-shebeileijiankongdian"></span> |
||||
|
<span v-if="node.data.type === 3 && !node.data.online" title="在线通道" class="device-offline iconfont icon-shebeileijiankongdian"></span> |
||||
|
<span v-if="node.data.type === 4 && node.data.online " title="在线通道-球机" class="device-online iconfont icon-shebeileiqiuji"></span> |
||||
|
<span v-if="node.data.type === 4 && !node.data.online" title="在线通道-球机" class="device-offline iconfont icon-shebeileiqiuji"></span> |
||||
|
<span v-if="node.data.type === 5 && node.data.online " title="在线通道-半球" class="device-online iconfont icon-shebeileibanqiu"></span> |
||||
|
<span v-if="node.data.type === 5 && !node.data.online" title="在线通道-半球" class="device-offline iconfont icon-shebeileibanqiu"></span> |
||||
|
<span v-if="node.data.type === 6 && node.data.online " title="在线通道-枪机" class="device-online iconfont icon-shebeileiqiangjitongdao"></span> |
||||
|
<span v-if="node.data.type === 6 && !node.data.online" title="在线通道-枪机" class="device-offline iconfont icon-shebeileiqiangjitongdao"></span> |
||||
|
<span v-if="node.data.online" style="padding-left: 1px" class="device-online">{{ node.label }}</span> |
||||
|
<span v-if="!node.data.online" style="padding-left: 1px" class="device-offline">{{ node.label }}</span> |
||||
|
<span> |
||||
|
<i v-if="node.data.hasGPS && node.data.online" style="color: #9d9d9d" class="device-online iconfont icon-dizhi"></i> |
||||
|
<i v-if="node.data.hasGPS && !node.data.online" style="color: #9d9d9d" class="device-offline iconfont icon-dizhi"></i> |
||||
|
</span> |
||||
|
</span> |
||||
|
</el-tree> |
||||
|
</div> |
||||
|
</el-main> |
||||
|
</el-container> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import DeviceService from "../service/DeviceService.js"; |
||||
|
|
||||
|
export default { |
||||
|
name: 'DeviceTree', |
||||
|
data() { |
||||
|
return { |
||||
|
deviceService: new DeviceService(), |
||||
|
defaultProps: { |
||||
|
children: 'children', |
||||
|
label: 'name', |
||||
|
isLeaf: 'isLeaf' |
||||
|
} |
||||
|
}; |
||||
|
}, |
||||
|
props: ['clickEvent', 'contextMenuEvent'], |
||||
|
methods: { |
||||
|
handleNodeClick(data,node,element) { |
||||
|
console.log("点击事件") |
||||
|
console.log(data) |
||||
|
if(typeof (this.clickEvent) == "function") { |
||||
|
this.clickEvent(data.userData) |
||||
|
} |
||||
|
}, |
||||
|
handleContextMenu(event,data,node,element) { |
||||
|
console.log("右键点击事件") |
||||
|
if(typeof (this.contextMenuEvent) == "function") { |
||||
|
this.contextMenuEvent(event, data.userData) |
||||
|
} |
||||
|
}, |
||||
|
loadNode: function(node, resolve){ |
||||
|
if (node.level === 0) { |
||||
|
this.deviceService.getAllDeviceList((data)=>{ |
||||
|
console.log(data) |
||||
|
if (data.length > 0) { |
||||
|
let nodeList = [] |
||||
|
for (let i = 0; i < data.length; i++) { |
||||
|
console.log(data[i].name) |
||||
|
let node = { |
||||
|
name: data[i].name || data[i].deviceId, |
||||
|
isLeaf: false, |
||||
|
id: data[i].deviceId, |
||||
|
type: data[i].online, |
||||
|
online: data[i].online === 1, |
||||
|
userData: data[i] |
||||
|
} |
||||
|
nodeList.push(node); |
||||
|
} |
||||
|
resolve(nodeList) |
||||
|
}else { |
||||
|
resolve([]) |
||||
|
} |
||||
|
}, (error)=>{ |
||||
|
|
||||
|
}) |
||||
|
} |
||||
|
if (node.level === 1) { |
||||
|
this.deviceService.getAllChannel(true, node.data.id, (catalogData) => { |
||||
|
this.deviceService.getAllChannel(false, node.data.id, (channelData) => { |
||||
|
let data = catalogData.concat(channelData) |
||||
|
this.channelDataHandler(data, resolve) |
||||
|
}) |
||||
|
}) |
||||
|
}else if (node.level > 1){ |
||||
|
this.deviceService.getAllSubChannel(true, node.data.deviceId, node.data.id, (catalogData)=>{ |
||||
|
this.deviceService.getAllSubChannel(false, node.data.deviceId, node.data.id, (channelData)=>{ |
||||
|
let data = catalogData.concat(channelData) |
||||
|
this.channelDataHandler(data, resolve) |
||||
|
}) |
||||
|
}) |
||||
|
} |
||||
|
}, |
||||
|
channelDataHandler: function (data, resolve) { |
||||
|
if (data.length > 0) { |
||||
|
let nodeList = [] |
||||
|
for (let i = 0; i < data.length; i++) { |
||||
|
let type = 3; |
||||
|
if (data[i].subCount > 0) { |
||||
|
type = 2; |
||||
|
}else if (data[i].ptztype === 1 ) { // 1-球机;2-半球;3-固定枪机;4-遥控枪机 |
||||
|
type = 4; |
||||
|
}else if (data[i].ptztype === 2) { |
||||
|
type = 5; |
||||
|
}else if (data[i].ptztype === 3 || data[i].ptztype === 4) { |
||||
|
type = 6; |
||||
|
} |
||||
|
let node = { |
||||
|
name: data[i].name || data[i].channelId, |
||||
|
isLeaf: data[i].subCount === 0, |
||||
|
id: data[i].channelId, |
||||
|
deviceId: data[i].deviceId, |
||||
|
type: type, |
||||
|
online: data[i].status === 1, |
||||
|
hasGPS: data[i].longitude*data[i].latitude !== 0, |
||||
|
userData: data[i] |
||||
|
} |
||||
|
nodeList.push(node); |
||||
|
} |
||||
|
resolve(nodeList) |
||||
|
}else { |
||||
|
resolve([]) |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
destroyed() { |
||||
|
// if (this.jessibuca) { |
||||
|
// this.jessibuca.destroy(); |
||||
|
// } |
||||
|
// this.playing = false; |
||||
|
// this.loaded = false; |
||||
|
// this.performance = ""; |
||||
|
}, |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style> |
||||
|
.device-tree-main-box{ |
||||
|
text-align: left; |
||||
|
} |
||||
|
.device-online{ |
||||
|
color: #252525; |
||||
|
} |
||||
|
.device-offline{ |
||||
|
color: #727272; |
||||
|
} |
||||
|
</style> |
@ -0,0 +1,257 @@ |
|||||
|
<template> |
||||
|
<div id="mapContainer" ref="mapContainer" style="width: 100%;height: 100%;"></div> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import 'ol/ol.css'; |
||||
|
import Map from 'ol/Map'; |
||||
|
import OSM from 'ol/source/OSM'; |
||||
|
import XYZ from 'ol/source/XYZ'; |
||||
|
import VectorSource from 'ol/source/Vector'; |
||||
|
import Tile from 'ol/layer/Tile'; |
||||
|
import VectorLayer from 'ol/layer/Vector'; |
||||
|
import Style from 'ol/style/Style'; |
||||
|
import Icon from 'ol/style/Icon'; |
||||
|
import View from 'ol/View'; |
||||
|
import Feature from 'ol/Feature'; |
||||
|
import Overlay from 'ol/Overlay'; |
||||
|
import {Point, LineString} from 'ol/geom'; |
||||
|
import {get as getProj, fromLonLat} from 'ol/proj'; |
||||
|
import {ZoomSlider, Zoom} from 'ol/control'; |
||||
|
import {containsCoordinate} from 'ol/extent'; |
||||
|
|
||||
|
import {v4} from 'uuid' |
||||
|
|
||||
|
let olMap = null; |
||||
|
|
||||
|
export default { |
||||
|
name: 'MapComponent', |
||||
|
data() { |
||||
|
return { |
||||
|
|
||||
|
|
||||
|
}; |
||||
|
}, |
||||
|
created(){ |
||||
|
this.$nextTick(() => { |
||||
|
setTimeout(()=>{ |
||||
|
this.init() |
||||
|
}, 100) |
||||
|
}) |
||||
|
|
||||
|
}, |
||||
|
props: [], |
||||
|
mounted () { |
||||
|
|
||||
|
}, |
||||
|
methods: { |
||||
|
init(){ |
||||
|
|
||||
|
let center = fromLonLat([116.41020, 39.915119]); |
||||
|
if (mapParam.center) { |
||||
|
center = fromLonLat(mapParam.center); |
||||
|
} |
||||
|
const view = new View({ |
||||
|
center: center, |
||||
|
zoom: mapParam.zoom || 10, |
||||
|
projection: this.projection, |
||||
|
maxZoom: mapParam.maxZoom || 19, |
||||
|
minZoom: mapParam.minZoom || 1, |
||||
|
}); |
||||
|
let tileLayer = null; |
||||
|
if (mapParam.tilesUrl) { |
||||
|
tileLayer = new Tile({ |
||||
|
source: new XYZ({ |
||||
|
projection: getProj("EPSG:3857"), |
||||
|
wrapX: false, |
||||
|
tileSize: 256 || mapParam.tileSize, |
||||
|
url: mapParam.tilesUrl |
||||
|
}) |
||||
|
}) |
||||
|
}else { |
||||
|
tileLayer = new Tile({ |
||||
|
preload: 4, |
||||
|
source: new OSM(), |
||||
|
}) |
||||
|
} |
||||
|
olMap = new Map({ |
||||
|
target: this.$refs.mapContainer, // 容器ID |
||||
|
layers: [tileLayer], // 默认图层 |
||||
|
view: view, // 视图 |
||||
|
controls:[ // 控件 |
||||
|
// new ZoomSlider(), |
||||
|
new Zoom(), |
||||
|
] , |
||||
|
}) |
||||
|
console.log(3222) |
||||
|
}, |
||||
|
setCenter(point){ |
||||
|
|
||||
|
}, |
||||
|
zoomIn(zoom){ |
||||
|
|
||||
|
}, |
||||
|
zoomOut(zoom){ |
||||
|
|
||||
|
}, |
||||
|
centerAndZoom(point,zoom,callback){ |
||||
|
var zoom_ = olMap.getView().getZoom(); |
||||
|
zoom = zoom|| zoom_; |
||||
|
var duration = 600; |
||||
|
olMap.getView().setCenter(fromLonLat(point)) |
||||
|
olMap.getView().animate({ |
||||
|
zoom: zoom , |
||||
|
duration: duration |
||||
|
}); |
||||
|
}, |
||||
|
panTo(point, zoom){ |
||||
|
let duration = 800; |
||||
|
|
||||
|
olMap.getView().cancelAnimations() |
||||
|
olMap.getView().animate({ |
||||
|
center: fromLonLat(point), |
||||
|
duration: duration |
||||
|
}); |
||||
|
if (!containsCoordinate(olMap.getView().calculateExtent(), fromLonLat(point))) { |
||||
|
olMap.getView().animate({ |
||||
|
zoom: olMap.getView().getZoom() - 1, |
||||
|
duration: duration / 2 |
||||
|
}, { |
||||
|
zoom: zoom || olMap.getView().getZoom(), |
||||
|
duration: duration / 2 |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
}, |
||||
|
fit(layer){ |
||||
|
let extent = layer.getSource().getExtent(); |
||||
|
if (extent) { |
||||
|
olMap.getView().fit(extent,{ |
||||
|
duration : 600, |
||||
|
padding: [100, 100, 100, 100] |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
}, |
||||
|
openInfoBox(position, content, offset){ |
||||
|
let id = v4() |
||||
|
// let infoBox = document.createElement("div"); |
||||
|
// infoBox.innerHTML = content ; |
||||
|
// infoBox.setAttribute("infoBoxId", id) |
||||
|
let overlay = new Overlay({ |
||||
|
id:id, |
||||
|
autoPan:true, |
||||
|
autoPanAnimation:{ |
||||
|
duration: 250 |
||||
|
}, |
||||
|
element: content, |
||||
|
positioning:"bottom-center", |
||||
|
offset:offset, |
||||
|
// className:overlayStyle.className |
||||
|
}); |
||||
|
olMap.addOverlay(overlay); |
||||
|
overlay.setPosition(fromLonLat(position)); |
||||
|
return id; |
||||
|
}, |
||||
|
closeInfoBox(id){ |
||||
|
olMap.getOverlayById(id).setPosition(undefined) |
||||
|
// olMap.removeOverlay(olMap.getOverlayById(id)) |
||||
|
}, |
||||
|
/** |
||||
|
* 添加图层 |
||||
|
* @param data |
||||
|
* [ |
||||
|
* { |
||||
|
* |
||||
|
* position: [119.1212,45,122], |
||||
|
* image: { |
||||
|
* src:"/images/123.png", |
||||
|
* anchor: [0.5, 0.5] |
||||
|
* |
||||
|
* } |
||||
|
* } |
||||
|
* |
||||
|
* ] |
||||
|
*/ |
||||
|
addLayer(data, clickEvent){ |
||||
|
let style = new Style(); |
||||
|
if (data.length > 0) { |
||||
|
let features = []; |
||||
|
for (let i = 0; i < data.length; i++) { |
||||
|
let feature = new Feature(new Point(fromLonLat(data[i].position))); |
||||
|
feature.customData = data[i].data; |
||||
|
let cloneStyle = style.clone() |
||||
|
cloneStyle.setImage(new Icon({ |
||||
|
anchor: data[i].image.anchor, |
||||
|
crossOrigin: 'Anonymous', |
||||
|
src: data[i].image.src, |
||||
|
})) |
||||
|
feature.setStyle(cloneStyle) |
||||
|
features.push(feature); |
||||
|
} |
||||
|
let source = new VectorSource(); |
||||
|
source.addFeatures(features); |
||||
|
let vectorLayer = new VectorLayer({ |
||||
|
source:source, |
||||
|
style:style, |
||||
|
renderMode:"image", |
||||
|
declutter: false |
||||
|
}) |
||||
|
olMap.addLayer(vectorLayer) |
||||
|
if (typeof clickEvent == "function") { |
||||
|
olMap.on("click", (event)=>{ |
||||
|
vectorLayer.getFeatures(event.pixel).then((features)=>{ |
||||
|
if (features.length > 0) { |
||||
|
let items = [] |
||||
|
for (let i = 0; i < features.length; i++) { |
||||
|
items.push(features[i].customData) |
||||
|
} |
||||
|
clickEvent(items) |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
|
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
return vectorLayer; |
||||
|
} |
||||
|
}, |
||||
|
removeLayer(layer){ |
||||
|
olMap.removeLayer(layer) |
||||
|
}, |
||||
|
|
||||
|
addLineLayer(positions) { |
||||
|
if (positions.length > 0) { |
||||
|
let points = []; |
||||
|
for (let i = 0; i < positions.length; i++) { |
||||
|
points.push(fromLonLat(positions[i])); |
||||
|
} |
||||
|
let line = new LineString(points) |
||||
|
let lineFeature = new Feature(line); |
||||
|
|
||||
|
let source = new VectorSource(); |
||||
|
source.addFeature(lineFeature); |
||||
|
let vectorLayer = new VectorLayer({ |
||||
|
source: source, |
||||
|
}) |
||||
|
olMap.addLayer(vectorLayer) |
||||
|
return vectorLayer; |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
destroyed() { |
||||
|
// if (this.jessibuca) { |
||||
|
// this.jessibuca.destroy(); |
||||
|
// } |
||||
|
// this.playing = false; |
||||
|
// this.loaded = false; |
||||
|
// this.performance = ""; |
||||
|
}, |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style> |
||||
|
|
||||
|
</style> |
@ -0,0 +1,309 @@ |
|||||
|
<template> |
||||
|
<div id="container" ref="containerId" @dblclick="fullscreenSwich" style="width: 100%"> |
||||
|
<div class="buttons-box" id="buttonsBox"> |
||||
|
<div class="buttons-box-left"> |
||||
|
<i v-if="!playing" class="iconfont icon-play jessibuca-btn" @click="playBtnClick"></i> |
||||
|
<i v-if="playing" class="iconfont icon-pause jessibuca-btn" @click="pause"></i> |
||||
|
<i class="iconfont icon-stop jessibuca-btn" @click="destroy"></i> |
||||
|
<i v-if="isNotMute" class="iconfont icon-audio-high jessibuca-btn" @click="jessibuca.mute()"></i> |
||||
|
<i v-if="!isNotMute" class="iconfont icon-audio-mute jessibuca-btn" @click="jessibuca.cancelMute()"></i> |
||||
|
</div> |
||||
|
<div class="buttons-box-right"> |
||||
|
<span class="jessibuca-btn">{{ kBps }} kb/s</span> |
||||
|
<!-- <i class="iconfont icon-file-record1 jessibuca-btn"></i>--> |
||||
|
<!-- <i class="iconfont icon-xiangqing2 jessibuca-btn" ></i>--> |
||||
|
<i class="iconfont icon-camera1196054easyiconnet jessibuca-btn" @click="jessibuca.screenshot('截图','png',0.5)" |
||||
|
style="font-size: 1rem !important"></i> |
||||
|
<i class="iconfont icon-shuaxin11 jessibuca-btn" @click="playBtnClick"></i> |
||||
|
<i v-if="!fullscreen" class="iconfont icon-weibiaoti10 jessibuca-btn" @click="fullscreenSwich"></i> |
||||
|
<i v-if="fullscreen" class="iconfont icon-weibiaoti11 jessibuca-btn" @click="fullscreenSwich"></i> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
export default { |
||||
|
name: 'jessibuca', |
||||
|
data() { |
||||
|
return { |
||||
|
jessibuca: null, |
||||
|
playing: false, |
||||
|
isNotMute: false, |
||||
|
quieting: false, |
||||
|
fullscreen: false, |
||||
|
loaded: false, // mute |
||||
|
speed: 0, |
||||
|
performance: "", // 工作情况 |
||||
|
kBps: 0, |
||||
|
btnDom: null, |
||||
|
videoInfo: null, |
||||
|
volume: 1, |
||||
|
rotate: 0, |
||||
|
vod: true, // 点播 |
||||
|
forceNoOffscreen: false, |
||||
|
}; |
||||
|
}, |
||||
|
props: ['videoUrl', 'error', 'hasAudio', 'height'], |
||||
|
mounted() { |
||||
|
window.onerror = (msg) => { |
||||
|
// console.error(msg) |
||||
|
}; |
||||
|
let paramUrl = decodeURIComponent(this.$route.params.url) |
||||
|
this.$nextTick(() => { |
||||
|
this.updatePlayerDomSize() |
||||
|
window.onresize = () => { |
||||
|
this.updatePlayerDomSize() |
||||
|
} |
||||
|
if (typeof (this.videoUrl) == "undefined") { |
||||
|
this.videoUrl = paramUrl; |
||||
|
} |
||||
|
this.btnDom = document.getElementById("buttonsBox"); |
||||
|
console.log("初始化时的地址为: " + this.videoUrl) |
||||
|
this.play(this.videoUrl) |
||||
|
}) |
||||
|
}, |
||||
|
watch: { |
||||
|
videoUrl(newData, oldData) { |
||||
|
this.play(newData) |
||||
|
}, |
||||
|
immediate: true |
||||
|
}, |
||||
|
methods: { |
||||
|
updatePlayerDomSize() { |
||||
|
let dom = document.getElementById('container'); |
||||
|
const width = dom.parentNode.clientWidth |
||||
|
dom.style.width = width + 'px'; |
||||
|
dom.style.height = (9 / 16) * width + "px"; |
||||
|
}, |
||||
|
create() { |
||||
|
let options = {}; |
||||
|
console.log(this.$refs.containerId) |
||||
|
console.log("hasAudio " + this.hasAudio) |
||||
|
|
||||
|
this.jessibuca = new window.Jessibuca(Object.assign( |
||||
|
{ |
||||
|
container: this.$refs.containerId, |
||||
|
videoBuffer: 0.2, // 最大缓冲时长,单位秒 |
||||
|
isResize: true, |
||||
|
decoder: "static/js/jessibuca/decoder.js", |
||||
|
useMSE: false, |
||||
|
showBandwidth: false, |
||||
|
isFlv: true, |
||||
|
// text: "WVP-PRO", |
||||
|
// background: "static/images/zlm-logo.png", |
||||
|
loadingText: "加载中", |
||||
|
hasAudio: typeof (this.hasAudio) == "undefined" ? true : this.hasAudio, |
||||
|
debug: false, |
||||
|
supportDblclickFullscreen: false, // 是否支持屏幕的双击事件,触发全屏,取消全屏事件。 |
||||
|
operateBtns: { |
||||
|
fullscreen: false, |
||||
|
screenshot: false, |
||||
|
play: false, |
||||
|
audio: false, |
||||
|
recorder: false, |
||||
|
}, |
||||
|
record: "record", |
||||
|
vod: this.vod, |
||||
|
forceNoOffscreen: this.forceNoOffscreen, |
||||
|
isNotMute: this.isNotMute, |
||||
|
}, |
||||
|
options |
||||
|
)); |
||||
|
|
||||
|
let _this = this; |
||||
|
this.jessibuca.on("load", function () { |
||||
|
console.log("on load init"); |
||||
|
}); |
||||
|
|
||||
|
this.jessibuca.on("log", function (msg) { |
||||
|
console.log("on log", msg); |
||||
|
}); |
||||
|
this.jessibuca.on("record", function (msg) { |
||||
|
console.log("on record:", msg); |
||||
|
}); |
||||
|
this.jessibuca.on("pause", function () { |
||||
|
_this.playing = false; |
||||
|
}); |
||||
|
this.jessibuca.on("play", function () { |
||||
|
_this.playing = true; |
||||
|
}); |
||||
|
this.jessibuca.on("fullscreen", function (msg) { |
||||
|
console.log("on fullscreen", msg); |
||||
|
_this.fullscreen = msg |
||||
|
}); |
||||
|
|
||||
|
this.jessibuca.on("mute", function (msg) { |
||||
|
console.log("on mute", msg); |
||||
|
_this.isNotMute = !msg; |
||||
|
}); |
||||
|
this.jessibuca.on("audioInfo", function (msg) { |
||||
|
// console.log("audioInfo", msg); |
||||
|
}); |
||||
|
|
||||
|
this.jessibuca.on("videoInfo", function (msg) { |
||||
|
// this.videoInfo = msg; |
||||
|
console.log("videoInfo", msg); |
||||
|
|
||||
|
}); |
||||
|
|
||||
|
this.jessibuca.on("bps", function (bps) { |
||||
|
// console.log('bps', bps); |
||||
|
|
||||
|
}); |
||||
|
let _ts = 0; |
||||
|
this.jessibuca.on("timeUpdate", function (ts) { |
||||
|
// console.log('timeUpdate,old,new,timestamp', _ts, ts, ts - _ts); |
||||
|
_ts = ts; |
||||
|
}); |
||||
|
|
||||
|
this.jessibuca.on("videoInfo", function (info) { |
||||
|
console.log("videoInfo", info); |
||||
|
}); |
||||
|
|
||||
|
this.jessibuca.on("error", function (error) { |
||||
|
console.log("error", error); |
||||
|
}); |
||||
|
|
||||
|
this.jessibuca.on("timeout", function () { |
||||
|
console.log("timeout"); |
||||
|
}); |
||||
|
|
||||
|
this.jessibuca.on('start', function () { |
||||
|
console.log('start'); |
||||
|
}) |
||||
|
|
||||
|
this.jessibuca.on("performance", function (performance) { |
||||
|
let show = "卡顿"; |
||||
|
if (performance === 2) { |
||||
|
show = "非常流畅"; |
||||
|
} else if (performance === 1) { |
||||
|
show = "流畅"; |
||||
|
} |
||||
|
_this.performance = show; |
||||
|
}); |
||||
|
this.jessibuca.on('buffer', function (buffer) { |
||||
|
// console.log('buffer', buffer); |
||||
|
}) |
||||
|
|
||||
|
this.jessibuca.on('stats', function (stats) { |
||||
|
// console.log('stats', stats); |
||||
|
}) |
||||
|
|
||||
|
this.jessibuca.on('kBps', function (kBps) { |
||||
|
_this.kBps = Math.round(kBps); |
||||
|
}); |
||||
|
|
||||
|
// 显示时间戳 PTS |
||||
|
this.jessibuca.on('videoFrame', function () { |
||||
|
|
||||
|
}) |
||||
|
|
||||
|
// |
||||
|
this.jessibuca.on('metadata', function () { |
||||
|
|
||||
|
}); |
||||
|
}, |
||||
|
playBtnClick: function (event) { |
||||
|
this.play(this.videoUrl) |
||||
|
}, |
||||
|
play: function (url) { |
||||
|
console.log(url) |
||||
|
if (this.jessibuca) { |
||||
|
this.destroy(); |
||||
|
} |
||||
|
this.create(); |
||||
|
this.jessibuca.on("play", () => { |
||||
|
this.playing = true; |
||||
|
this.loaded = true; |
||||
|
this.quieting = this.jessibuca.quieting; |
||||
|
}); |
||||
|
if (this.jessibuca.hasLoaded()) { |
||||
|
this.jessibuca.play(url); |
||||
|
} else { |
||||
|
this.jessibuca.on("load", () => { |
||||
|
console.log("load 播放") |
||||
|
this.jessibuca.play(url); |
||||
|
}); |
||||
|
} |
||||
|
}, |
||||
|
pause: function () { |
||||
|
if (this.jessibuca) { |
||||
|
this.jessibuca.pause(); |
||||
|
} |
||||
|
this.playing = false; |
||||
|
this.err = ""; |
||||
|
this.performance = ""; |
||||
|
}, |
||||
|
destroy: function () { |
||||
|
if (this.jessibuca) { |
||||
|
this.jessibuca.destroy(); |
||||
|
} |
||||
|
if (document.getElementById("buttonsBox") == null) { |
||||
|
document.getElementById("container").appendChild(this.btnDom) |
||||
|
} |
||||
|
this.jessibuca = null; |
||||
|
this.playing = false; |
||||
|
this.err = ""; |
||||
|
this.performance = ""; |
||||
|
|
||||
|
}, |
||||
|
eventcallbacK: function (type, message) { |
||||
|
// console.log("player 事件回调") |
||||
|
// console.log(type) |
||||
|
// console.log(message) |
||||
|
}, |
||||
|
fullscreenSwich: function () { |
||||
|
let isFull = this.isFullscreen() |
||||
|
this.jessibuca.setFullscreen(!isFull) |
||||
|
this.fullscreen = !isFull; |
||||
|
}, |
||||
|
isFullscreen: function () { |
||||
|
return document.fullscreenElement || |
||||
|
document.msFullscreenElement || |
||||
|
document.mozFullScreenElement || |
||||
|
document.webkitFullscreenElement || false; |
||||
|
} |
||||
|
}, |
||||
|
destroyed() { |
||||
|
if (this.jessibuca) { |
||||
|
this.jessibuca.destroy(); |
||||
|
} |
||||
|
this.playing = false; |
||||
|
this.loaded = false; |
||||
|
this.performance = ""; |
||||
|
}, |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style> |
||||
|
.buttons-box { |
||||
|
width: 100%; |
||||
|
height: 28px; |
||||
|
background-color: rgba(43, 51, 63, 0.7); |
||||
|
position: absolute; |
||||
|
display: -webkit-box; |
||||
|
display: -ms-flexbox; |
||||
|
display: flex; |
||||
|
left: 0; |
||||
|
bottom: 0; |
||||
|
user-select: none; |
||||
|
z-index: 10; |
||||
|
} |
||||
|
|
||||
|
.jessibuca-btn { |
||||
|
width: 20px; |
||||
|
color: rgb(255, 255, 255); |
||||
|
line-height: 27px; |
||||
|
margin: 0px 10px; |
||||
|
padding: 0px 2px; |
||||
|
cursor: pointer; |
||||
|
text-align: center; |
||||
|
font-size: 0.8rem !important; |
||||
|
} |
||||
|
|
||||
|
.buttons-box-right { |
||||
|
position: absolute; |
||||
|
right: 0; |
||||
|
} |
||||
|
</style> |
@ -1,383 +1,357 @@ |
|||||
<template> |
<template> |
||||
<div id="devicePosition" style="height: 100%"> |
<div id="devicePosition" style="width: 100vw; height: 91vh;"> |
||||
<div style="background-color: #ffffff; position: relative; padding: 1rem 0.5rem 0.5rem 0.5rem; text-align: center;"> |
<el-container v-if="onOff" style="height: 91vh;" v-loading="isLoging"> |
||||
<span style="font-size: 1rem; font-weight: 500">设备定位 ({{ parentChannelId == 0 ? deviceId : parentChannelId }})</span> |
<el-aside width="auto" style="background-color: #ffffff"> |
||||
|
<DeviceTree ref="deviceTree" :clickEvent="clickEvent" :contextMenuEvent="contextmenuEventHandler" ></DeviceTree> |
||||
|
</el-aside> |
||||
|
<el-main style="height: 91vh; padding: 0"> |
||||
|
<MapComponent ref="map"></MapComponent> |
||||
|
</el-main> |
||||
|
</el-container> |
||||
|
<div v-if="!onOff" style="width: 100%; height:100%; text-align: center; line-height: 5rem"> |
||||
|
<p>地图功能已关闭</p> |
||||
</div> |
</div> |
||||
<div style="background-color: #ffffff; margin-bottom: 1rem; position: relative; padding: 0.5rem; text-align: left; font-size: 14px;"> |
<div ref="infobox" v-if="channel != null " > |
||||
<el-button icon="el-icon-arrow-left" size="mini" style="margin-right: 1rem" type="primary" @click="showDevice">返回</el-button> |
<div v-if="channel != null" class="infobox-content"> |
||||
<!-- <span class="demonstration">从</span> --> |
<el-descriptions class="margin-top" :title="channel.name" :column="1" :colon="true" size="mini" :labelStyle="labelStyle" > |
||||
<el-date-picker v-model="searchFrom" type="datetime" placeholder="选择开始日期时间" default-time="00:00:00" size="mini" style="width: 11rem;" align="right" :picker-options="pickerOptions"></el-date-picker> |
<el-descriptions-item label="生产厂商">{{channel.manufacture}}</el-descriptions-item> |
||||
<el-date-picker v-model="searchTo" type="datetime" placeholder="选择结束日期时间" default-time="00:00:00" size="mini" style="width: 11rem;" align="right" :picker-options="pickerOptions"></el-date-picker> |
<el-descriptions-item label="型号">{{channel.model}}</el-descriptions-item> |
||||
<el-button-group> |
<el-descriptions-item label="设备归属" >{{channel.owner}}</el-descriptions-item> |
||||
<el-button icon="el-icon-search" size="mini" type="primary" @click="showHistoryPath">历史轨迹</el-button> |
<el-descriptions-item label="行政区域" >{{channel.civilCode}}</el-descriptions-item> |
||||
<el-button icon="el-icon-search" size="mini" style="margin-right: 1rem" type="primary" @click="showLatestPosition">最新位置</el-button> |
<el-descriptions-item label="安装地址" >{{channel.address == null?'未知': channel.address}}</el-descriptions-item> |
||||
</el-button-group> |
<el-descriptions-item label="云台类型" >{{channel.ptztypeText}}</el-descriptions-item> |
||||
<el-tag style="width: 5rem; text-align: center" size="medium">过期时间</el-tag> |
<el-descriptions-item label="经纬度" >{{channel.longitude}},{{channel.latitude}}</el-descriptions-item> |
||||
<el-input-number size="mini" v-model="expired" :min="300" :controls="false" style="width: 4rem;"></el-input-number> |
<el-descriptions-item label="状态"> |
||||
<el-tag style="width: 5rem; text-align: center" size="medium">上报周期</el-tag> |
<el-tag size="small" v-if="channel.status === 1">在线</el-tag> |
||||
<el-input-number size="mini" v-model="interval" :min="1" :controls="false" style="width: 4rem;"></el-input-number> |
<el-tag size="small" type="info" v-if="channel.status === 0">离线</el-tag> |
||||
<el-button-group> |
</el-descriptions-item> |
||||
<el-button icon="el-icon-search" size="mini" type="primary" @click="subscribeMobilePosition">位置订阅</el-button> |
</el-descriptions> |
||||
<el-button icon="el-icon-search" size="mini" type="primary" @click="unSubscribeMobilePosition">取消订阅</el-button> |
<div style="padding-top: 10px"> |
||||
</el-button-group> |
<el-button type="primary" size="small" title="播放" icon="el-icon-video-play" @click="play(channel)"></el-button> |
||||
<el-checkbox size="mini" style="margin-right: 1rem; float: right" v-model="autoList" @change="autoListChange" >自动刷新</el-checkbox> |
<el-button type="primary" size="small" title="编辑位置" icon="el-icon-edit" @click="edit(channel)"></el-button> |
||||
</div> |
<el-button type="primary" size="small" title="轨迹查询" icon="el-icon-map-location" @click="getTrace(channel)"></el-button> |
||||
<div class="mapContainer" style="background-color: #ffffff; position: relative; padding: 1rem 0.5rem 0.5rem 0.5rem; text-align: center; height: calc(100% - 10rem);"> |
</div> |
||||
<div class="baidumap" id="allmap"></div> |
<span class="infobox-close el-icon-close" @click="closeInfoBox()"></span> |
||||
|
</div> |
||||
</div> |
</div> |
||||
|
<devicePlayer ref="devicePlayer" ></devicePlayer> |
||||
|
<queryTrace ref="queryTrace" ></queryTrace> |
||||
</div> |
</div> |
||||
</template> |
</template> |
||||
|
|
||||
<script> |
<script> |
||||
import uiHeader from "../layout/UiHeader.vue"; |
import MapComponent from "./common/MapComponent.vue"; |
||||
import geoTools from "./GeoConvertTools.js"; |
import DeviceService from "./service/DeviceService"; |
||||
|
import DeviceTree from "./common/DeviceTree"; |
||||
|
import channelMapInfobox from "./dialog/channelMapInfobox"; |
||||
|
import devicePlayer from './dialog/devicePlayer.vue' |
||||
|
import queryTrace from './dialog/queryTrace.vue' |
||||
|
|
||||
export default { |
export default { |
||||
name: "devicePosition", |
name: "devicePosition", |
||||
components: { |
components: { |
||||
uiHeader, |
MapComponent, |
||||
|
DeviceTree, |
||||
|
channelMapInfobox, |
||||
|
devicePlayer, |
||||
|
queryTrace, |
||||
|
}, |
||||
|
data() { |
||||
|
return { |
||||
|
onOff: typeof window.mapParam !== "undefined", |
||||
|
deviceService: new DeviceService(), |
||||
|
layer: null, |
||||
|
lineLayer: null, |
||||
|
channel: null, |
||||
|
infoBoxId: null, |
||||
|
labelStyle: { |
||||
|
width: "56px" |
||||
|
}, |
||||
|
isLoging: false, |
||||
|
}; |
||||
|
}, |
||||
|
created() { |
||||
|
if (this.$route.query.deviceId) { |
||||
|
console.log(this.$route.query.deviceId) |
||||
|
// this.$refs.deviceTree.openByDeivceId(this.$route.query.deivceId) |
||||
|
setTimeout(()=>{ // 延迟以等待地图加载完成 TODO 后续修改为通过是实际这;状态加回调完成 |
||||
|
this.deviceService.getAllChannel(false, this.$route.query.deviceId, this.channelsHandler) |
||||
|
}, 1000) |
||||
|
|
||||
|
} |
||||
|
}, |
||||
|
destroyed() { |
||||
|
|
||||
|
}, |
||||
|
methods: { |
||||
|
clickEvent: function (data) { |
||||
|
if (data.channelId && data.subCount == 0) { |
||||
|
// 点击通道 |
||||
|
if (data.longitude * data.latitude === 0) { |
||||
|
this.$message.error('未获取到位置信息'); |
||||
|
} else { |
||||
|
if (this.layer != null) { |
||||
|
this.$refs.map.removeLayer(this.layer); |
||||
|
} |
||||
|
this.closeInfoBox() |
||||
|
this.layer = this.$refs.map.addLayer([{ |
||||
|
position: [data.longitude, data.latitude], |
||||
|
image: { |
||||
|
src: this.getImageByChannel(data), |
||||
|
anchor: [0.5, 1] |
||||
|
}, |
||||
|
data: data |
||||
|
}], this.featureClickEvent) |
||||
|
this.$refs.map.panTo([data.longitude, data.latitude], mapParam.maxZoom) |
||||
|
} |
||||
|
} |
||||
}, |
}, |
||||
data() { |
contextmenuEventHandler: function (event, data) { |
||||
return { |
if (data.channelId && data.subCount == 0) { |
||||
pickerOptions: { |
// 点击通道 |
||||
shortcuts: [{ |
this.$contextmenu({ |
||||
text: '今天', |
items: [ |
||||
onClick(picker) { |
{ |
||||
picker.$emit('pick', new Date()); |
label: "播放", |
||||
} |
icon: "el-icon-video-play", |
||||
}, { |
disabled: false, |
||||
text: '昨天', |
onClick: () => { |
||||
onClick(picker) { |
this.play(data); |
||||
const date = new Date(); |
} |
||||
date.setTime(date.getTime() - 3600 * 1000 * 24); |
|
||||
picker.$emit('pick', date); |
|
||||
} |
|
||||
}, { |
|
||||
text: '一周前', |
|
||||
onClick(picker) { |
|
||||
const date = new Date(); |
|
||||
date.setTime(date.getTime() - 3600 * 1000 * 24 * 7); |
|
||||
picker.$emit('pick', date); |
|
||||
} |
|
||||
}] |
|
||||
}, |
}, |
||||
deviceId: this.$route.params.deviceId, |
{ |
||||
showHistoryPosition: false, //显示历史轨迹 |
label: "编辑位置", |
||||
startTime: null, |
icon: "el-icon-edit", |
||||
endTime: null, |
disabled: false, |
||||
searchFrom: null, |
onClick: () => { |
||||
searchTo: null, |
this.edit(data) |
||||
expired: 600, |
} |
||||
interval: 5, |
}, |
||||
mobilePositionList: [], |
{ |
||||
mapPointList: [], |
label: "轨迹查询", |
||||
parentChannelId: this.$route.params.parentChannelId, |
icon: "el-icon-map-location", |
||||
updateLooper: 0, //数据刷新轮训标志 |
disabled: false, |
||||
total: 0, |
onClick: () => { |
||||
beforeUrl: "/deviceList", |
this.getTrace(data) |
||||
isLoging: false, |
} |
||||
autoList: false, |
} |
||||
}; |
], |
||||
|
event, // 鼠标事件信息 |
||||
|
customClass: "custom-class", // 自定义菜单 class |
||||
|
zIndex: 3000, // 菜单样式 z-index |
||||
|
}); |
||||
|
} else { |
||||
|
this.deviceOrSubChannelMenu(event, data) |
||||
|
} |
||||
|
|
||||
}, |
}, |
||||
mounted() { |
deviceOrSubChannelMenu: function (event, data) { |
||||
this.initData(); |
// 点击设备 |
||||
this.initBaiduMap(); |
this.$contextmenu({ |
||||
if (this.autoList) { |
items: [ |
||||
this.updateLooper = setInterval(this.initData, 5000); |
{ |
||||
|
label: "定位", |
||||
|
icon: "el-icon-s-promotion", |
||||
|
disabled: false, |
||||
|
onClick: () => { |
||||
|
if (!data.channelId) { |
||||
|
this.deviceService.getAllChannel(false, data.deviceId, this.channelsHandler) |
||||
|
} |
||||
|
if (data.channelId && data.subCount > 0) { |
||||
|
// 点击子目录 |
||||
|
this.deviceService.getAllSubChannel(false, data.deviceId, data.channelId, this.channelsHandler) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
], |
||||
|
event, // 鼠标事件信息 |
||||
|
customClass: "custom-class", // 自定义菜单 class |
||||
|
zIndex: 3000, // 菜单样式 z-index |
||||
|
}); |
||||
|
|
||||
|
}, |
||||
|
channelsHandler: function (channels) { |
||||
|
console.log(2) |
||||
|
if (channels.length > 0) { |
||||
|
this.clean() |
||||
|
this.closeInfoBox() |
||||
|
let params = []; |
||||
|
for (let i = 0; i < channels.length; i++) { |
||||
|
if (channels[i].longitude * channels[i].latitude === 0) { |
||||
|
continue; |
||||
|
} |
||||
|
let item = { |
||||
|
position: [channels[i].longitude, channels[i].latitude], |
||||
|
image: { |
||||
|
src: this.getImageByChannel(channels[i]), |
||||
|
anchor: [0.5, 1] |
||||
|
}, |
||||
|
data: channels[i] |
||||
|
} |
||||
|
params.push(item); |
||||
} |
} |
||||
|
console.log(3) |
||||
|
|
||||
|
this.layer = this.$refs.map.addLayer(params, this.featureClickEvent) |
||||
|
console.log(4) |
||||
|
if (params.length === 1) { |
||||
|
this.$refs.map.panTo([channels[0].longitude, channels[0].latitude], mapParam.maxZoom) |
||||
|
} else if (params.length > 1) { |
||||
|
this.$refs.map.fit(this.layer) |
||||
|
} else { |
||||
|
this.$message.error('未获取到位置信息'); |
||||
|
} |
||||
|
} else { |
||||
|
this.$message.error('未获取到位置信息'); |
||||
|
} |
||||
}, |
}, |
||||
destroyed() { |
getImageByChannel: function (channel) { |
||||
// this.$destroy("videojs"); |
let src = "static/images/gis/camera.png" |
||||
clearTimeout(this.updateLooper); |
switch (channel.ptztype) { |
||||
|
case 1: |
||||
|
if (channel.status === 1) { |
||||
|
src = "static/images/gis/camera1.png" |
||||
|
} else { |
||||
|
src = "static/images/gis/camera1-offline.png" |
||||
|
} |
||||
|
break; |
||||
|
case 2: |
||||
|
if (channel.status === 1) { |
||||
|
src = "static/images/gis/camera2.png" |
||||
|
} else { |
||||
|
src = "static/images/gis/camera2-offline.png" |
||||
|
} |
||||
|
break; |
||||
|
case 3: |
||||
|
if (channel.status === 1) { |
||||
|
src = "static/images/gis/camera3.png" |
||||
|
} else { |
||||
|
src = "static/images/gis/camera3-offline.png" |
||||
|
} |
||||
|
break; |
||||
|
default: |
||||
|
if (channel.status === 1) { |
||||
|
src = "static/images/gis/camera.png" |
||||
|
} else { |
||||
|
src = "static/images/gis/camera-offline.png" |
||||
|
} |
||||
|
} |
||||
|
return src; |
||||
}, |
}, |
||||
methods: { |
featureClickEvent: function (channels) { |
||||
initData: function () { |
this.closeInfoBox() |
||||
// if (this.parentChannelId == "" || this.parentChannelId == 0) { |
if (channels.length > 0) { |
||||
// this.getDeviceChannelList(); |
this.channel = channels[0] |
||||
// } else { |
} |
||||
// this.showSubchannels(); |
this.$nextTick(() => { |
||||
// } |
this.infoBoxId = this.$refs.map.openInfoBox([this.channel.longitude, this.channel.latitude], this.$refs.infobox, [0, -50]) |
||||
}, |
}) |
||||
initParam: function () { |
}, |
||||
// this.deviceId = this.$route.params.deviceId; |
closeInfoBox: function () { |
||||
// this.parentChannelId = this.$route.params.parentChannelId; |
if (this.infoBoxId != null) { |
||||
// this.currentPage = parseInt(this.$route.params.page); |
this.$refs.map.closeInfoBox(this.infoBoxId) |
||||
// this.count = parseInt(this.$route.params.count); |
} |
||||
// if (this.parentChannelId == "" || this.parentChannelId == 0) { |
}, |
||||
// this.beforeUrl = "/deviceList"; |
play: function (channel) { |
||||
// } |
|
||||
}, |
let deviceId = channel.deviceId; |
||||
initBaiduMap() { |
this.isLoging = true; |
||||
this.map = new BMap.Map("allmap"); // 创建地图实例 |
let channelId = channel.channelId; |
||||
let points = []; |
console.log("通知设备推流1:" + deviceId + " : " + channelId); |
||||
let point = new BMap.Point(116.231398, 39.567445); // 创建点坐标 |
let that = this; |
||||
this.map.centerAndZoom(point, 5); // 初始化地图,设置中心点坐标和地图级别 |
this.$axios({ |
||||
this.map.enableScrollWheelZoom(true); //开启鼠标滚轮缩放 |
method: 'get', |
||||
this.map.addControl(new BMap.NavigationControl()); |
url: '/api/play/start/' + deviceId + '/' + channelId |
||||
this.map.addControl(new BMap.ScaleControl()); |
}).then(function (res) { |
||||
this.map.addControl(new BMap.OverviewMapControl()); |
that.isLoging = false; |
||||
this.map.addControl(new BMap.MapTypeControl()); |
if (res.data.code === 0) { |
||||
//map.setMapStyle({ style: 'midnight' }) //地图风格 |
|
||||
}, |
that.$refs.devicePlayer.openDialog("media", deviceId, channelId, { |
||||
currentChange: function (val) { |
streamInfo: res.data.data, |
||||
// var url = `/${this.$router.currentRoute.name}/${this.deviceId}/${this.parentChannelId}/${this.count}/${val}`; |
hasAudio: channel.hasAudio |
||||
// console.log(url); |
}); |
||||
// this.$router.push(url).then(() => { |
|
||||
// this.initParam(); |
} else { |
||||
// this.initData(); |
that.$message.error(res.data.msg); |
||||
// }); |
} |
||||
}, |
}).catch(function (e) { |
||||
handleSizeChange: function (val) { |
}); |
||||
// var url = `/${this.$router.currentRoute.name}/${this.$router.params.deviceId}/${this.$router.params.parentChannelId}/${val}/1`; |
}, |
||||
// this.$router.push(url).then(() => { |
edit: function (data) { |
||||
// this.initParam(); |
this.$message.warning('暂不支持'); |
||||
// this.initData(); |
}, |
||||
// }); |
getTrace: function (data) { |
||||
}, |
// this.$message.warning('暂不支持'); |
||||
showDevice: function () { |
this.clean() |
||||
this.$router.push(this.beforeUrl).then(() => { |
this.$refs.queryTrace.openDialog(data, (channelPositions) => { |
||||
this.initParam(); |
console.log("getTrace") |
||||
this.initData(); |
console.log(channelPositions) |
||||
}); |
if (channelPositions.length === 0) { |
||||
}, |
this.$message.success('未查询到轨迹信息'); |
||||
autoListChange: function () { |
} else { |
||||
if (this.autoList) { |
let positions = []; |
||||
this.updateLooper = setInterval(this.initData, 1500); |
for (let i = 0; i < channelPositions.length; i++) { |
||||
} else { |
if (channelPositions[i].cnLng * channelPositions[i].cnLat > 0) { |
||||
window.clearInterval(this.updateLooper); |
positions.push([channelPositions[i].cnLng, channelPositions[i].cnLat]) |
||||
} |
|
||||
}, |
|
||||
showHistoryPath: function () { |
|
||||
this.map.clearOverlays(); |
|
||||
this.mapPointList = []; |
|
||||
this.mobilePositionList = []; |
|
||||
if (!!this.searchFrom) { |
|
||||
this.startTime = this.toGBString(this.searchFrom); |
|
||||
console.log(this.startTime); |
|
||||
} else{ |
|
||||
this.startTime = null; |
|
||||
} |
|
||||
if (!!this.searchTo) { |
|
||||
this.endTime = this.toGBString(this.searchTo); |
|
||||
console.log(this.endTime); |
|
||||
} else { |
|
||||
this.endTime = null; |
|
||||
} |
|
||||
let self = this; |
|
||||
this.$axios({ |
|
||||
method: 'get', |
|
||||
url:`/api/position/history/${this.deviceId}`, |
|
||||
params: { |
|
||||
start: self.startTime, |
|
||||
end: self.endTime, |
|
||||
}, |
|
||||
}).then(function (res) { |
|
||||
self.total = res.data.length; |
|
||||
self.mobilePositionList = res.data; |
|
||||
console.log(self.mobilePositionList); |
|
||||
if (self.total == 0) { |
|
||||
self.$message({ |
|
||||
showClose: true, |
|
||||
message: '未找到符合条件的移动位置信息', |
|
||||
type: 'error' |
|
||||
}); |
|
||||
} else { |
|
||||
self.$nextTick(() => { |
|
||||
self.showMarkPoints(self); |
|
||||
}); |
|
||||
} |
|
||||
}).catch(function (error) { |
|
||||
console.log(error); |
|
||||
}); |
|
||||
}, |
|
||||
showLatestPosition: function() { |
|
||||
this.map.clearOverlays(); |
|
||||
this.mapPointList = []; |
|
||||
this.mobilePositionList = []; |
|
||||
let self = this; |
|
||||
this.$axios({ |
|
||||
method: 'get', |
|
||||
url:`/api/position/latest/${this.deviceId}` |
|
||||
}).then(function (res) { |
|
||||
console.log(res.data); |
|
||||
self.total = res.data.length; |
|
||||
self.mobilePositionList.push(res.data); |
|
||||
console.log(self.mobilePositionList); |
|
||||
if (self.total == 0) { |
|
||||
self.$message({ |
|
||||
showClose: true, |
|
||||
message: '未找到符合条件的移动位置信息', |
|
||||
type: 'error' |
|
||||
}); |
|
||||
} else { |
|
||||
self.$nextTick(() => { |
|
||||
self.showMarkPoints(self); |
|
||||
}); |
|
||||
} |
|
||||
}).catch(function (error) { |
|
||||
console.log(error); |
|
||||
}); |
|
||||
}, |
|
||||
subscribeMobilePosition: function() { |
|
||||
let self = this; |
|
||||
this.$axios({ |
|
||||
method: 'get', |
|
||||
url:`/api/position/subscribe/${this.deviceId}`, |
|
||||
params: { |
|
||||
expires: self.expired, |
|
||||
interval: self.interval, |
|
||||
}, |
|
||||
}).then(function (res) { |
|
||||
console.log(res.data); |
|
||||
}) |
|
||||
.catch(function (error) { |
|
||||
console.log(error); |
|
||||
}); |
|
||||
}, |
|
||||
unSubscribeMobilePosition: function() { |
|
||||
let self = this; |
|
||||
this.$axios({ |
|
||||
method: 'get', |
|
||||
url:`/api/position/subscribe/${this.deviceId}`, |
|
||||
params: { |
|
||||
expires: 0, |
|
||||
interval: self.interval, |
|
||||
}, |
|
||||
}) |
|
||||
.then(function (res) { |
|
||||
console.log(res.data); |
|
||||
}).catch(function (error) { |
|
||||
console.log(error); |
|
||||
}); |
|
||||
}, |
|
||||
toGBString: function (dateTime) { |
|
||||
return ( |
|
||||
dateTime.getFullYear() + |
|
||||
"-" + this.twoDigits(dateTime.getMonth() + 1) + |
|
||||
"-" + this.twoDigits(dateTime.getDate()) + |
|
||||
"T" + this.twoDigits(dateTime.getHours()) + |
|
||||
":" + this.twoDigits(dateTime.getMinutes()) + |
|
||||
":" + this.twoDigits(dateTime.getSeconds()) |
|
||||
); |
|
||||
}, |
|
||||
twoDigits: function (num) { |
|
||||
if (num < 10) { |
|
||||
return "0" + num; |
|
||||
} else { |
|
||||
return "" + num; |
|
||||
} |
|
||||
}, |
|
||||
showMarkPoints: function(self) { |
|
||||
let that = self; |
|
||||
let npointJ = null; |
|
||||
let npointW = null; |
|
||||
let point = null; |
|
||||
for (let i = 0; i < self.mobilePositionList.length; i++) { |
|
||||
if (self.mobilePositionList[i].geodeticSystem == "BD-09") { |
|
||||
npointJ = self.mobilePositionList[i].cnLng; |
|
||||
npointW = self.mobilePositionList[i].cnLat; |
|
||||
point = new BMap.Point(npointJ, npointW); |
|
||||
} else { |
|
||||
npointJ = self.mobilePositionList[i].longitude; |
|
||||
npointW = self.mobilePositionList[i].latitude; |
|
||||
let bd2 = geoTools.GPSToBaidu(npointJ, npointW); |
|
||||
point = new BMap.Point(bd2.lat, bd2.lng); |
|
||||
} |
|
||||
self.mapPointList.push(point); |
|
||||
let marker = new BMap.Marker(point); // 创建标注 |
|
||||
self.map.addOverlay(marker); // 将标注添加到地图中 |
|
||||
//提示信息 可以解析 HTML标签以及CSS |
|
||||
let infoWindow = new BMap.InfoWindow(`<p style='text-align:left;font-weight:800'>设备: ${self.mobilePositionList[i].deviceId}</p> |
|
||||
<p style='text-align:left;font-weight:0'>时间: ${self.mobilePositionList[i].time}</p>`); |
|
||||
// 鼠标移上标注点要发生的事 |
|
||||
marker.addEventListener("mouseover", function () { |
|
||||
this.openInfoWindow(infoWindow); |
|
||||
}); |
|
||||
// 鼠标移开标注点要发生的事 |
|
||||
marker.addEventListener("mouseout", function () { |
|
||||
this.closeInfoWindow(infoWindow); |
|
||||
}); |
|
||||
// 鼠标点击标注点要发生的事情 |
|
||||
marker.addEventListener("click", function () { |
|
||||
alert("点击"); |
|
||||
}); |
|
||||
} |
} |
||||
let view = that.map.getViewport(eval(self.mapPointList)); |
|
||||
that.map.centerAndZoom(view.center, view.zoom); |
} |
||||
}, |
if (positions.length === 0) { |
||||
|
this.$message.success('未查询到轨迹信息'); |
||||
|
return; |
||||
|
} |
||||
|
this.lineLayer = this.$refs.map.addLineLayer(positions) |
||||
|
this.$refs.map.fit(this.lineLayer) |
||||
|
} |
||||
|
}) |
||||
}, |
}, |
||||
|
clean: function (){ |
||||
|
if (this.lineLayer != null) { |
||||
|
this.$refs.map.removeLayer(this.lineLayer) |
||||
|
} |
||||
|
if (this.infoBoxId != null) { |
||||
|
this.$refs.map.closeInfoBox(this.infoBoxId) |
||||
|
} |
||||
|
if (this.layer != null) { |
||||
|
this.$refs.map.removeLayer(this.layer) |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
|
||||
}; |
}; |
||||
</script> |
</script> |
||||
|
|
||||
<style> |
<style> |
||||
.videoList { |
.infobox-content{ |
||||
display: flex; |
width: 260px; |
||||
flex-wrap: wrap; |
background-color: #FFFFFF; |
||||
align-content: flex-start; |
padding: 10px; |
||||
} |
border-radius: 10px; |
||||
|
border: 1px solid #e2e2e2; |
||||
.video-item { |
|
||||
position: relative; |
|
||||
width: 15rem; |
|
||||
height: 10rem; |
|
||||
margin-right: 1rem; |
|
||||
background-color: #000000; |
|
||||
} |
} |
||||
|
|
||||
.video-item-img { |
.infobox-content::after { |
||||
position: absolute; |
position: absolute; |
||||
top: 0; |
bottom: -11px; |
||||
bottom: 0; |
left: 130px; |
||||
left: 0; |
display: block; |
||||
right: 0; |
|
||||
margin: auto; |
|
||||
width: 100%; |
|
||||
height: 100%; |
|
||||
} |
|
||||
|
|
||||
.video-item-img:after { |
|
||||
content: ""; |
content: ""; |
||||
display: inline-block; |
width: 16px; |
||||
position: absolute; |
height: 16px; |
||||
z-index: 2; |
background: url('~@static/images/arrow.png') no-repeat center; |
||||
top: 0; |
|
||||
bottom: 0; |
|
||||
left: 0; |
|
||||
right: 0; |
|
||||
margin: auto; |
|
||||
width: 3rem; |
|
||||
height: 3rem; |
|
||||
background-image: url("../assets/loading.png"); |
|
||||
background-size: cover; |
|
||||
background-color: #000000; |
|
||||
} |
} |
||||
|
.infobox-close { |
||||
.video-item-title { |
|
||||
position: absolute; |
position: absolute; |
||||
bottom: 0; |
right: 1rem; |
||||
|
top: 1rem; |
||||
color: #000000; |
color: #000000; |
||||
background-color: #ffffff; |
cursor:pointer |
||||
line-height: 1.5rem; |
|
||||
padding: 0.3rem; |
|
||||
width: 14.4rem; |
|
||||
} |
} |
||||
|
.el-descriptions__title { |
||||
.baidumap { |
font-size: 1rem; |
||||
|
font-weight: 700; |
||||
|
padding: 20px 20px 0px 23px; |
||||
|
text-align: center; |
||||
width: 100%; |
width: 100%; |
||||
height: 100%; |
|
||||
border: none; |
|
||||
position: absolute; |
|
||||
left: 0; |
|
||||
top: 0; |
|
||||
right: 0; |
|
||||
bottom: 0; |
|
||||
margin: auto; |
|
||||
} |
|
||||
|
|
||||
/* 去除百度地图版权那行字 和 百度logo */ |
|
||||
.baidumap > .BMap_cpyCtrl { |
|
||||
display: none !important; |
|
||||
} |
|
||||
.baidumap > .anchorBL { |
|
||||
display: none !important; |
|
||||
} |
} |
||||
</style> |
</style> |
||||
|
@ -0,0 +1,65 @@ |
|||||
|
<template> |
||||
|
<div id="channelMapInfobox" style="display: none"> |
||||
|
<div > |
||||
|
<el-descriptions class="margin-top" title="channel.name" :column="4" direction="vertical"> |
||||
|
<el-descriptions-item label="生产厂商">{{channel.manufacture}}</el-descriptions-item> |
||||
|
<el-descriptions-item label="型号">{{channel.model}}</el-descriptions-item> |
||||
|
<el-descriptions-item label="设备归属" >{{channel.owner}}</el-descriptions-item> |
||||
|
<el-descriptions-item label="行政区域" >{{channel.civilCode}}</el-descriptions-item> |
||||
|
<el-descriptions-item label="安装地址" >{{channel.address}}</el-descriptions-item> |
||||
|
<el-descriptions-item label="云台类型" >{{channel.ptztypeText}}</el-descriptions-item> |
||||
|
<el-descriptions-item label="经纬度" >{{channel.longitude}},{{channel.latitude}}</el-descriptions-item> |
||||
|
<el-descriptions-item label="状态"> |
||||
|
<el-tag size="small" v-if="channel.status === 1">在线</el-tag> |
||||
|
<el-tag size="small" v-if="channel.status === 0">离线</el-tag> |
||||
|
</el-descriptions-item> |
||||
|
</el-descriptions> |
||||
|
</div> |
||||
|
|
||||
|
<devicePlayer ref="devicePlayer" v-loading="isLoging"></devicePlayer> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import devicePlayer from '../dialog/devicePlayer.vue' |
||||
|
|
||||
|
export default { |
||||
|
name: "channelMapInfobox", |
||||
|
props: ['channel'], |
||||
|
computed: {devicePlayer}, |
||||
|
created() {}, |
||||
|
data() { |
||||
|
return { |
||||
|
showDialog: false, |
||||
|
isLoging: false |
||||
|
}; |
||||
|
}, |
||||
|
methods: { |
||||
|
|
||||
|
play: function (){ |
||||
|
let deviceId = this.channel.deviceId; |
||||
|
this.isLoging = true; |
||||
|
let channelId = this.channel.channelId; |
||||
|
console.log("通知设备推流1:" + deviceId + " : " + channelId); |
||||
|
let that = this; |
||||
|
this.$axios({ |
||||
|
method: 'get', |
||||
|
url: '/api/play/start/' + deviceId + '/' + channelId |
||||
|
}).then(function (res) { |
||||
|
that.isLoging = false; |
||||
|
if (res.data.code === 0) { |
||||
|
that.$refs.devicePlayer.openDialog("media", deviceId, channelId, { |
||||
|
streamInfo: res.data.data, |
||||
|
hasAudio: this.channel.hasAudio |
||||
|
}); |
||||
|
} else { |
||||
|
that.$message.error(res.data.msg); |
||||
|
} |
||||
|
}).catch(function (e) { |
||||
|
}); |
||||
|
}, |
||||
|
close: function () { |
||||
|
}, |
||||
|
}, |
||||
|
}; |
||||
|
</script> |
@ -1,316 +0,0 @@ |
|||||
<template> |
|
||||
<div :id="containerId" :ref="containerId" style="width: 100%;height: auto; background-color: #000" @dblclick="fullscreenSwich"> |
|
||||
<div class="buttons-box" id="buttonsBox"> |
|
||||
<div class="buttons-box-left"> |
|
||||
<i v-if="!playing" class="iconfont icon-play jessibuca-btn" @click="playBtnClick"></i> |
|
||||
<i v-if="playing" class="iconfont icon-pause jessibuca-btn" @click="pause"></i> |
|
||||
<i class="iconfont icon-stop jessibuca-btn" @click="destroy"></i> |
|
||||
<i v-if="isNotMute" class="iconfont icon-audio-high jessibuca-btn" @click="jessibuca.mute()"></i> |
|
||||
<i v-if="!isNotMute" class="iconfont icon-audio-mute jessibuca-btn" @click="jessibuca.cancelMute()"></i> |
|
||||
</div> |
|
||||
<div class="buttons-box-right"> |
|
||||
<span class="jessibuca-btn">{{kBps}} kb/s</span> |
|
||||
<!-- <i class="iconfont icon-file-record1 jessibuca-btn"></i>--> |
|
||||
<!-- <i class="iconfont icon-xiangqing2 jessibuca-btn" ></i>--> |
|
||||
<i class="iconfont icon-camera1196054easyiconnet jessibuca-btn" @click="jessibuca.screenshot('截图','png',0.5)" style="font-size: 1rem !important"></i> |
|
||||
<i class="iconfont icon-shuaxin11 jessibuca-btn" @click="playBtnClick"></i> |
|
||||
<i v-if="!fullscreen" class="iconfont icon-weibiaoti10 jessibuca-btn" @click="fullscreenSwich"></i> |
|
||||
<i v-if="fullscreen" class="iconfont icon-weibiaoti11 jessibuca-btn" @click="fullscreenSwich"></i> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
</template> |
|
||||
|
|
||||
<script> |
|
||||
export default { |
|
||||
name: 'jessibuca', |
|
||||
data() { |
|
||||
return { |
|
||||
jessibuca: null, |
|
||||
playing: false, |
|
||||
isNotMute: false, |
|
||||
quieting: false, |
|
||||
fullscreen: false, |
|
||||
loaded: false, // mute |
|
||||
speed: 0, |
|
||||
performance: "", // 工作情况 |
|
||||
kBps: 0, |
|
||||
btnDom: null, |
|
||||
videoInfo: null, |
|
||||
volume: 1, |
|
||||
rotate: 0, |
|
||||
vod: true, // 点播 |
|
||||
forceNoOffscreen: false, |
|
||||
}; |
|
||||
}, |
|
||||
props: ['containerId','videoUrl', 'error', 'hasAudio', 'height'], |
|
||||
mounted () { |
|
||||
window.onerror = (msg) => { |
|
||||
// console.error(msg) |
|
||||
}; |
|
||||
let paramUrl = decodeURIComponent(this.$route.params.url) |
|
||||
this.$nextTick(() =>{ |
|
||||
let dom = document.getElementById(this.containerId); |
|
||||
if (dom.parentNode.clientHeight == 0) { |
|
||||
dom.style.height = (9/16 ) * dom.clientWidth + "px" |
|
||||
} |
|
||||
dom.style.height = dom.parentNode.clientHeight + "px"; |
|
||||
dom.style.width = dom.parentNode.clientWidth + "px"; |
|
||||
|
|
||||
if (typeof (this.videoUrl) == "undefined") { |
|
||||
this.videoUrl = paramUrl; |
|
||||
} |
|
||||
this.btnDom = document.getElementById("buttonsBox"); |
|
||||
console.log("初始化时的地址为: " + this.videoUrl) |
|
||||
this.play(this.videoUrl) |
|
||||
}) |
|
||||
}, |
|
||||
watch:{ |
|
||||
videoUrl(newData, oldData){ |
|
||||
this.play(newData) |
|
||||
}, |
|
||||
immediate:true |
|
||||
}, |
|
||||
methods: { |
|
||||
create(){ |
|
||||
let options = {}; |
|
||||
console.log(this.$refs[this.containerId]) |
|
||||
console.log("hasAudio " + this.hasAudio) |
|
||||
|
|
||||
this.jessibuca = new window.Jessibuca(Object.assign( |
|
||||
{ |
|
||||
container: this.$refs[this.containerId], |
|
||||
videoBuffer: 0.2, // 最大缓冲时长,单位秒 |
|
||||
isResize: true, |
|
||||
decoder:"static/js/jessibuca/decoder.js", |
|
||||
useMSE: false, |
|
||||
showBandwidth: false, |
|
||||
isFlv: true, |
|
||||
// text: "WVP-PRO", |
|
||||
// background: "static/images/zlm-logo.png", |
|
||||
loadingText: "加载中", |
|
||||
hasAudio: typeof (this.hasAudio) =="undefined"? true: this.hasAudio, |
|
||||
debug: false, |
|
||||
supportDblclickFullscreen: false, // 是否支持屏幕的双击事件,触发全屏,取消全屏事件。 |
|
||||
operateBtns: { |
|
||||
fullscreen: false, |
|
||||
screenshot: false, |
|
||||
play: false, |
|
||||
audio: false, |
|
||||
recorder: false, |
|
||||
}, |
|
||||
record: "record", |
|
||||
vod: this.vod, |
|
||||
forceNoOffscreen: this.forceNoOffscreen, |
|
||||
isNotMute: this.isNotMute, |
|
||||
}, |
|
||||
options |
|
||||
)); |
|
||||
|
|
||||
let _this = this; |
|
||||
this.jessibuca.on("load", function () { |
|
||||
console.log("on load init"); |
|
||||
}); |
|
||||
|
|
||||
this.jessibuca.on("log", function (msg) { |
|
||||
console.log("on log", msg); |
|
||||
}); |
|
||||
this.jessibuca.on("record", function (msg) { |
|
||||
console.log("on record:", msg); |
|
||||
}); |
|
||||
this.jessibuca.on("pause", function () { |
|
||||
_this.playing = false; |
|
||||
}); |
|
||||
this.jessibuca.on("play", function () { |
|
||||
_this.playing = true; |
|
||||
}); |
|
||||
this.jessibuca.on("fullscreen", function (msg) { |
|
||||
console.log("on fullscreen", msg); |
|
||||
_this.fullscreen = msg |
|
||||
}); |
|
||||
|
|
||||
this.jessibuca.on("mute", function (msg) { |
|
||||
console.log("on mute", msg); |
|
||||
_this.isNotMute = !msg; |
|
||||
}); |
|
||||
this.jessibuca.on("audioInfo", function (msg) { |
|
||||
// console.log("audioInfo", msg); |
|
||||
}); |
|
||||
|
|
||||
this.jessibuca.on("videoInfo", function (msg) { |
|
||||
// this.videoInfo = msg; |
|
||||
console.log("videoInfo", msg); |
|
||||
|
|
||||
}); |
|
||||
|
|
||||
this.jessibuca.on("bps", function (bps) { |
|
||||
// console.log('bps', bps); |
|
||||
|
|
||||
}); |
|
||||
let _ts = 0; |
|
||||
this.jessibuca.on("timeUpdate", function (ts) { |
|
||||
// console.log('timeUpdate,old,new,timestamp', _ts, ts, ts - _ts); |
|
||||
_ts = ts; |
|
||||
}); |
|
||||
|
|
||||
this.jessibuca.on("videoInfo", function (info) { |
|
||||
console.log("videoInfo", info); |
|
||||
}); |
|
||||
|
|
||||
this.jessibuca.on("error", function (error) { |
|
||||
console.log("error", error); |
|
||||
}); |
|
||||
|
|
||||
this.jessibuca.on("timeout", function () { |
|
||||
console.log("timeout"); |
|
||||
}); |
|
||||
|
|
||||
this.jessibuca.on('start', function () { |
|
||||
console.log('start'); |
|
||||
}) |
|
||||
|
|
||||
this.jessibuca.on("performance", function (performance) { |
|
||||
let show = "卡顿"; |
|
||||
if (performance === 2) { |
|
||||
show = "非常流畅"; |
|
||||
} else if (performance === 1) { |
|
||||
show = "流畅"; |
|
||||
} |
|
||||
_this.performance = show; |
|
||||
}); |
|
||||
this.jessibuca.on('buffer', function (buffer) { |
|
||||
// console.log('buffer', buffer); |
|
||||
}) |
|
||||
|
|
||||
this.jessibuca.on('stats', function (stats) { |
|
||||
// console.log('stats', stats); |
|
||||
}) |
|
||||
|
|
||||
this.jessibuca.on('kBps', function (kBps) { |
|
||||
_this.kBps = Math.round(kBps); |
|
||||
}); |
|
||||
|
|
||||
// 显示时间戳 PTS |
|
||||
this.jessibuca.on('videoFrame', function () { |
|
||||
|
|
||||
}) |
|
||||
|
|
||||
// |
|
||||
this.jessibuca.on('metadata', function () { |
|
||||
|
|
||||
}); |
|
||||
}, |
|
||||
resize(){ |
|
||||
if (this.jessibuca){ |
|
||||
this.jessibuca.resize() |
|
||||
this.$nextTick(() =>{ |
|
||||
let dom = document.getElementById(this.containerId); |
|
||||
if (dom.parentNode.clientHeight == 0) { |
|
||||
dom.style.height = (9/16 ) * dom.clientWidth + "px" |
|
||||
} |
|
||||
dom.style.height = dom.parentNode.clientHeight + "px"; |
|
||||
dom.style.width = dom.parentNode.clientWidth + "px"; |
|
||||
}) |
|
||||
} |
|
||||
}, |
|
||||
playBtnClick: function (event){ |
|
||||
this.play(this.videoUrl) |
|
||||
}, |
|
||||
play: function (url) { |
|
||||
console.log(url) |
|
||||
if (this.jessibuca) { |
|
||||
this.destroy(); |
|
||||
} |
|
||||
this.create(); |
|
||||
this.jessibuca.on("play", () => { |
|
||||
this.playing = true; |
|
||||
this.loaded = true; |
|
||||
this.quieting = this.jessibuca.quieting; |
|
||||
}); |
|
||||
if (this.jessibuca.hasLoaded()) { |
|
||||
this.jessibuca.play(url); |
|
||||
} else { |
|
||||
this.jessibuca.on("load", () => { |
|
||||
console.log("load 播放") |
|
||||
this.jessibuca.play(url); |
|
||||
}); |
|
||||
} |
|
||||
}, |
|
||||
pause: function () { |
|
||||
if (this.jessibuca) { |
|
||||
this.jessibuca.pause(); |
|
||||
} |
|
||||
this.playing = false; |
|
||||
this.err = ""; |
|
||||
this.performance = ""; |
|
||||
}, |
|
||||
destroy: function () { |
|
||||
if (this.jessibuca) { |
|
||||
this.jessibuca.destroy(); |
|
||||
} |
|
||||
if (document.getElementById("buttonsBox") == null) { |
|
||||
document.getElementById("container").appendChild(this.btnDom) |
|
||||
} |
|
||||
this.jessibuca = null; |
|
||||
this.playing = false; |
|
||||
this.err = ""; |
|
||||
this.performance = ""; |
|
||||
|
|
||||
}, |
|
||||
eventcallbacK: function(type, message) { |
|
||||
// console.log("player 事件回调") |
|
||||
// console.log(type) |
|
||||
// console.log(message) |
|
||||
}, |
|
||||
fullscreenSwich: function (){ |
|
||||
let isFull = this.isFullscreen() |
|
||||
this.jessibuca.setFullscreen(!isFull) |
|
||||
this.fullscreen = !isFull; |
|
||||
}, |
|
||||
isFullscreen: function (){ |
|
||||
return document.fullscreenElement || |
|
||||
document.msFullscreenElement || |
|
||||
document.mozFullScreenElement || |
|
||||
document.webkitFullscreenElement || false; |
|
||||
} |
|
||||
}, |
|
||||
destroyed() { |
|
||||
if (this.jessibuca) { |
|
||||
this.jessibuca.destroy(); |
|
||||
} |
|
||||
this.playing = false; |
|
||||
this.loaded = false; |
|
||||
this.performance = ""; |
|
||||
}, |
|
||||
} |
|
||||
</script> |
|
||||
|
|
||||
<style> |
|
||||
.buttons-box{ |
|
||||
width: 100%; |
|
||||
height: 28px; |
|
||||
background-color: rgba(43, 51, 63, 0.7); |
|
||||
position: absolute; |
|
||||
display: -webkit-box; |
|
||||
display: -ms-flexbox; |
|
||||
display: flex; |
|
||||
left: 0; |
|
||||
bottom: 0; |
|
||||
user-select: none; |
|
||||
z-index: 10; |
|
||||
} |
|
||||
.jessibuca-btn{ |
|
||||
width: 20px; |
|
||||
color: rgb(255, 255, 255); |
|
||||
line-height: 27px; |
|
||||
margin: 0px 10px; |
|
||||
padding: 0px 2px; |
|
||||
cursor: pointer; |
|
||||
text-align: center; |
|
||||
font-size: 0.8rem !important; |
|
||||
} |
|
||||
.buttons-box-right { |
|
||||
position: absolute; |
|
||||
right: 0; |
|
||||
} |
|
||||
</style> |
|
@ -0,0 +1,100 @@ |
|||||
|
<template> |
||||
|
<div id="queryTrace" > |
||||
|
<el-dialog |
||||
|
title="查询轨迹" |
||||
|
width="40%" |
||||
|
top="2rem" |
||||
|
:close-on-click-modal="false" |
||||
|
:visible.sync="showDialog" |
||||
|
:destroy-on-close="true" |
||||
|
@close="close()" |
||||
|
> |
||||
|
<div v-loading="isLoging"> |
||||
|
<el-date-picker v-model="searchFrom" type="datetime" placeholder="选择开始日期时间" default-time="00:00:00" size="mini" style="width: 11rem;" align="right" :picker-options="pickerOptions"></el-date-picker> |
||||
|
<el-date-picker v-model="searchTo" type="datetime" placeholder="选择结束日期时间" default-time="00:00:00" size="mini" style="width: 11rem;" align="right" :picker-options="pickerOptions"></el-date-picker> |
||||
|
<el-button icon="el-icon-search" size="mini" type="primary" @click="onSubmit">查询</el-button> |
||||
|
</div> |
||||
|
|
||||
|
</el-dialog> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import DeviceService from '../service/DeviceService' |
||||
|
|
||||
|
export default { |
||||
|
name: "deviceEdit", |
||||
|
props: [], |
||||
|
computed: {}, |
||||
|
created() {}, |
||||
|
data() { |
||||
|
return { |
||||
|
deviceService: new DeviceService(), |
||||
|
pickerOptions: { |
||||
|
shortcuts: [{ |
||||
|
text: '今天', |
||||
|
onClick(picker) { |
||||
|
picker.$emit('pick', new Date()); |
||||
|
} |
||||
|
}, { |
||||
|
text: '昨天', |
||||
|
onClick(picker) { |
||||
|
const date = new Date(); |
||||
|
date.setTime(date.getTime() - 3600 * 1000 * 24); |
||||
|
picker.$emit('pick', date); |
||||
|
} |
||||
|
}, { |
||||
|
text: '一周前', |
||||
|
onClick(picker) { |
||||
|
const date = new Date(); |
||||
|
date.setTime(date.getTime() - 3600 * 1000 * 24 * 7); |
||||
|
picker.$emit('pick', date); |
||||
|
} |
||||
|
}] |
||||
|
}, |
||||
|
searchFrom: null, |
||||
|
searchTo: null, |
||||
|
listChangeCallback: null, |
||||
|
showDialog: false, |
||||
|
isLoging: false, |
||||
|
channel: null, |
||||
|
callback: null, |
||||
|
}; |
||||
|
}, |
||||
|
methods: { |
||||
|
openDialog: function (channel, callback) { |
||||
|
console.log(channel) |
||||
|
this.showDialog = true; |
||||
|
this.callback = callback; |
||||
|
this.channel = channel; |
||||
|
}, |
||||
|
|
||||
|
onSubmit: function () { |
||||
|
console.log("onSubmit"); |
||||
|
this.isLoging = true; |
||||
|
this.$axios.get(`/api/position/history/${this.channel.deviceId}/${this.channel.channelId}`, { |
||||
|
}).then((res)=> { |
||||
|
this.isLoging = false; |
||||
|
if (typeof this.callback == "function") { |
||||
|
if (res.data.code == 0) { |
||||
|
this.callback(res.data.data) |
||||
|
this.close() |
||||
|
}else { |
||||
|
this.$message.error(res.data.msg); |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
}).catch(function (error) { |
||||
|
this.isLoging = false; |
||||
|
console.error(error); |
||||
|
}) |
||||
|
}, |
||||
|
close: function () { |
||||
|
this.showDialog = false; |
||||
|
this.isLoging = false; |
||||
|
this.callback = null; |
||||
|
this.channel = null; |
||||
|
}, |
||||
|
}, |
||||
|
}; |
||||
|
</script> |
@ -0,0 +1,123 @@ |
|||||
|
import axios from 'axios'; |
||||
|
|
||||
|
class DeviceService{ |
||||
|
|
||||
|
constructor() { |
||||
|
this.$axios = axios; |
||||
|
} |
||||
|
|
||||
|
getDeviceList(currentPage, count, callback, errorCallback){ |
||||
|
this.$axios({ |
||||
|
method: 'get', |
||||
|
url:`/api/device/query/devices`, |
||||
|
params: { |
||||
|
page: currentPage, |
||||
|
count: count |
||||
|
} |
||||
|
}).then((res) => { |
||||
|
if (typeof (callback) == "function") callback(res.data) |
||||
|
}).catch((error) => { |
||||
|
console.log(error); |
||||
|
if (typeof (errorCallback) == "function") errorCallback(error) |
||||
|
}); |
||||
|
} |
||||
|
getAllDeviceList(callback, errorCallback) { |
||||
|
let currentPage = 1; |
||||
|
let count = 100; |
||||
|
let deviceList = [] |
||||
|
this.getAllDeviceListIteration(deviceList, currentPage, count, (data) => { |
||||
|
if (typeof (callback) == "function") callback(data) |
||||
|
}, errorCallback) |
||||
|
} |
||||
|
|
||||
|
getAllDeviceListIteration(deviceList, currentPage, count, callback, errorCallback) { |
||||
|
this.getDeviceList(currentPage, count, (data) => { |
||||
|
if (data.list) { |
||||
|
deviceList = deviceList.concat(data.list); |
||||
|
if (deviceList.length < data.total) { |
||||
|
currentPage ++ |
||||
|
this.getAllDeviceListIteration(deviceList, currentPage, count, callback, errorCallback) |
||||
|
}else { |
||||
|
if (typeof (callback) == "function") callback(deviceList) |
||||
|
} |
||||
|
} |
||||
|
}, errorCallback) |
||||
|
} |
||||
|
|
||||
|
|
||||
|
getAllChannel(isCatalog, deviceId, callback, errorCallback) { |
||||
|
let currentPage = 1; |
||||
|
let count = 100; |
||||
|
let catalogList = [] |
||||
|
this.getAllChannelIteration(isCatalog, deviceId, catalogList, currentPage, count, callback, errorCallback) |
||||
|
} |
||||
|
|
||||
|
getAllChannelIteration(isCatalog, deviceId, catalogList, currentPage, count, callback, errorCallback) { |
||||
|
this.getChanel(isCatalog, deviceId, currentPage, count, (data) => { |
||||
|
if (data.list) { |
||||
|
catalogList = catalogList.concat(data.list); |
||||
|
if (catalogList.length < data.total) { |
||||
|
currentPage ++ |
||||
|
this.getAllChannelIteration(isCatalog, deviceId, catalogList, currentPage, count, callback, errorCallback) |
||||
|
}else { |
||||
|
console.log(1) |
||||
|
if (typeof (callback) == "function") callback(catalogList) |
||||
|
} |
||||
|
} |
||||
|
}, errorCallback) |
||||
|
} |
||||
|
getChanel(isCatalog, deviceId, currentPage, count, callback, errorCallback) { |
||||
|
this.$axios({ |
||||
|
method: 'get', |
||||
|
url: `/api/device/query/devices/${deviceId}/channels`, |
||||
|
params:{ |
||||
|
page: currentPage, |
||||
|
count: count, |
||||
|
query: "", |
||||
|
online: "", |
||||
|
channelType: isCatalog |
||||
|
} |
||||
|
}).then((res) =>{ |
||||
|
if (typeof (callback) == "function") callback(res.data) |
||||
|
}).catch(errorCallback); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
getAllSubChannel(isCatalog, deviceId, channelId, callback, errorCallback) { |
||||
|
let currentPage = 1; |
||||
|
let count = 100; |
||||
|
let catalogList = [] |
||||
|
this.getAllSubChannelIteration(isCatalog, deviceId, channelId, catalogList, currentPage, count, callback, errorCallback) |
||||
|
} |
||||
|
|
||||
|
getAllSubChannelIteration(isCatalog, deviceId,channelId, catalogList, currentPage, count, callback, errorCallback) { |
||||
|
this.getSubChannel(isCatalog, deviceId, channelId, currentPage, count, (data) => { |
||||
|
if (data.list) { |
||||
|
catalogList = catalogList.concat(data.list); |
||||
|
if (catalogList.length < data.total) { |
||||
|
currentPage ++ |
||||
|
this.getAllSubChannelIteration(isCatalog, deviceId, channelId, catalogList, currentPage, count, callback, errorCallback) |
||||
|
}else { |
||||
|
if (typeof (callback) == "function") callback(catalogList) |
||||
|
} |
||||
|
} |
||||
|
}, errorCallback) |
||||
|
} |
||||
|
getSubChannel(isCatalog, deviceId, channelId, currentPage, count, callback, errorCallback) { |
||||
|
this.$axios({ |
||||
|
method: 'get', |
||||
|
url: `/api/device/query/sub_channels/${deviceId}/${channelId}/channels`, |
||||
|
params:{ |
||||
|
page: currentPage, |
||||
|
count: count, |
||||
|
query: "", |
||||
|
online: "", |
||||
|
channelType: isCatalog |
||||
|
} |
||||
|
}).then((res) =>{ |
||||
|
if (typeof (callback) == "function") callback(res.data) |
||||
|
}).catch(errorCallback); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
export default DeviceService; |
After Width: | Height: | Size: 4.9 KiB |
After Width: | Height: | Size: 8.7 KiB |
After Width: | Height: | Size: 10 KiB |
After Width: | Height: | Size: 10 KiB |
After Width: | Height: | Size: 13 KiB |
After Width: | Height: | Size: 9.8 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 10 KiB |
After Width: | Height: | Size: 13 KiB |