Commit 37a84e66917d6e22e03e31b0a115e2c16d23ed21
1 parent
d07a5680
新增实时监控功能
左边是设备通道树,右边是分屏预览
Showing
24 changed files
with
1321 additions
and
15 deletions
pom.xml
| @@ -277,5 +277,16 @@ | @@ -277,5 +277,16 @@ | ||
| 277 | </plugin> | 277 | </plugin> |
| 278 | 278 | ||
| 279 | </plugins> | 279 | </plugins> |
| 280 | + <resources> | ||
| 281 | + <resource> | ||
| 282 | + <directory>src/main/resources</directory> | ||
| 283 | + </resource> | ||
| 284 | + <resource> | ||
| 285 | + <directory>src/main/java</directory> | ||
| 286 | + <includes> | ||
| 287 | + <include>**/*.xml</include> | ||
| 288 | + </includes> | ||
| 289 | + </resource> | ||
| 290 | + </resources> | ||
| 280 | </build> | 291 | </build> |
| 281 | </project> | 292 | </project> |
src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorager.java
| @@ -5,6 +5,7 @@ import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; | @@ -5,6 +5,7 @@ import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; | ||
| 5 | import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem; | 5 | import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem; |
| 6 | import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem; | 6 | import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem; |
| 7 | import com.genersoft.iot.vmp.service.bean.GPSMsgInfo; | 7 | import com.genersoft.iot.vmp.service.bean.GPSMsgInfo; |
| 8 | +import com.genersoft.iot.vmp.vmanager.bean.DeviceChannelTree; | ||
| 8 | import com.genersoft.iot.vmp.vmanager.gb28181.platform.bean.ChannelReduce; | 9 | import com.genersoft.iot.vmp.vmanager.gb28181.platform.bean.ChannelReduce; |
| 9 | import com.github.pagehelper.PageInfo; | 10 | import com.github.pagehelper.PageInfo; |
| 10 | 11 | ||
| @@ -94,6 +95,13 @@ public interface IVideoManagerStorager { | @@ -94,6 +95,13 @@ public interface IVideoManagerStorager { | ||
| 94 | public List<DeviceChannel> queryChannelsByDeviceIdWithStartAndLimit(String deviceId, String query, Boolean hasSubChannel, Boolean online, int start, int limit); | 95 | public List<DeviceChannel> queryChannelsByDeviceIdWithStartAndLimit(String deviceId, String query, Boolean hasSubChannel, Boolean online, int start, int limit); |
| 95 | 96 | ||
| 96 | /** | 97 | /** |
| 98 | + * 获取某个设备的通道树 | ||
| 99 | + * @param deviceId 设备ID | ||
| 100 | + * @return | ||
| 101 | + */ | ||
| 102 | + List<DeviceChannelTree> tree(String deviceId); | ||
| 103 | + | ||
| 104 | + /** | ||
| 97 | * 获取某个设备的通道列表 | 105 | * 获取某个设备的通道列表 |
| 98 | * | 106 | * |
| 99 | * @param deviceId 设备ID | 107 | * @param deviceId 设备ID |
src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceChannelMapper.java
| 1 | package com.genersoft.iot.vmp.storager.dao; | 1 | package com.genersoft.iot.vmp.storager.dao; |
| 2 | 2 | ||
| 3 | import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; | 3 | import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; |
| 4 | +import com.genersoft.iot.vmp.vmanager.bean.DeviceChannelTree; | ||
| 4 | import com.genersoft.iot.vmp.vmanager.gb28181.platform.bean.ChannelReduce; | 5 | import com.genersoft.iot.vmp.vmanager.gb28181.platform.bean.ChannelReduce; |
| 5 | import org.apache.ibatis.annotations.*; | 6 | import org.apache.ibatis.annotations.*; |
| 6 | import org.springframework.stereotype.Repository; | 7 | import org.springframework.stereotype.Repository; |
| @@ -201,4 +202,6 @@ public interface DeviceChannelMapper { | @@ -201,4 +202,6 @@ public interface DeviceChannelMapper { | ||
| 201 | 202 | ||
| 202 | @Select("SELECT * FROM device_channel WHERE deviceId=#{deviceId} AND status=1") | 203 | @Select("SELECT * FROM device_channel WHERE deviceId=#{deviceId} AND status=1") |
| 203 | List<DeviceChannel> queryOnlineChannelsByDeviceId(String deviceId); | 204 | List<DeviceChannel> queryOnlineChannelsByDeviceId(String deviceId); |
| 205 | + | ||
| 206 | + List<DeviceChannelTree> tree(String deviceId); | ||
| 204 | } | 207 | } |
src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceChannelMapper.xml
0 → 100644
| 1 | +<?xml version="1.0" encoding="UTF-8"?> | ||
| 2 | +<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> | ||
| 3 | +<mapper namespace="com.genersoft.iot.vmp.storager.dao.DeviceChannelMapper"> | ||
| 4 | + | ||
| 5 | + <!-- 通用查询映射结果 --> | ||
| 6 | + <resultMap id="treeNodeResultMap" type="com.genersoft.iot.vmp.vmanager.bean.DeviceChannelTreeNode"> | ||
| 7 | + <id column="id" property="id"/> | ||
| 8 | + <result column="parentId" property="parentId"/> | ||
| 9 | + <result column="status" property="status"/> | ||
| 10 | + <result column="title" property="title"/> | ||
| 11 | + <result column="value" property="value"/> | ||
| 12 | + <result column="key" property="key"/> | ||
| 13 | + <result column="deviceId" property="deviceId"/> | ||
| 14 | + <result column="channelId" property="channelId"/> | ||
| 15 | + <result column="longitude" property="lng"/> | ||
| 16 | + <result column="latitude" property="lat"/> | ||
| 17 | + </resultMap> | ||
| 18 | + | ||
| 19 | + | ||
| 20 | + <select id="tree" resultMap="treeNodeResultMap"> | ||
| 21 | + SELECT | ||
| 22 | + channelId, | ||
| 23 | + channelId as id, | ||
| 24 | + deviceId, | ||
| 25 | + parentId, | ||
| 26 | + status, | ||
| 27 | + name as title, | ||
| 28 | + channelId as "value", | ||
| 29 | + channelId as "key", | ||
| 30 | + channelId, | ||
| 31 | + longitude, | ||
| 32 | + latitude | ||
| 33 | + from device_channel | ||
| 34 | + where deviceId = #{deviceId} | ||
| 35 | + </select> | ||
| 36 | + | ||
| 37 | +</mapper> |
src/main/java/com/genersoft/iot/vmp/storager/impl/VideoManagerStoragerImpl.java
| @@ -13,6 +13,8 @@ import com.genersoft.iot.vmp.service.bean.GPSMsgInfo; | @@ -13,6 +13,8 @@ import com.genersoft.iot.vmp.service.bean.GPSMsgInfo; | ||
| 13 | import com.genersoft.iot.vmp.storager.IRedisCatchStorage; | 13 | import com.genersoft.iot.vmp.storager.IRedisCatchStorage; |
| 14 | import com.genersoft.iot.vmp.storager.IVideoManagerStorager; | 14 | import com.genersoft.iot.vmp.storager.IVideoManagerStorager; |
| 15 | import com.genersoft.iot.vmp.storager.dao.*; | 15 | import com.genersoft.iot.vmp.storager.dao.*; |
| 16 | +import com.genersoft.iot.vmp.utils.node.ForestNodeMerger; | ||
| 17 | +import com.genersoft.iot.vmp.vmanager.bean.DeviceChannelTree; | ||
| 16 | import com.genersoft.iot.vmp.vmanager.gb28181.platform.bean.ChannelReduce; | 18 | import com.genersoft.iot.vmp.vmanager.gb28181.platform.bean.ChannelReduce; |
| 17 | import com.github.pagehelper.PageHelper; | 19 | import com.github.pagehelper.PageHelper; |
| 18 | import com.github.pagehelper.PageInfo; | 20 | import com.github.pagehelper.PageInfo; |
| @@ -329,6 +331,11 @@ public class VideoManagerStoragerImpl implements IVideoManagerStorager { | @@ -329,6 +331,11 @@ public class VideoManagerStoragerImpl implements IVideoManagerStorager { | ||
| 329 | } | 331 | } |
| 330 | 332 | ||
| 331 | @Override | 333 | @Override |
| 334 | + public List<DeviceChannelTree> tree(String deviceId) { | ||
| 335 | + return ForestNodeMerger.merge(deviceChannelMapper.tree(deviceId)); | ||
| 336 | + } | ||
| 337 | + | ||
| 338 | + @Override | ||
| 332 | public List<DeviceChannel> queryChannelsByDeviceId(String deviceId) { | 339 | public List<DeviceChannel> queryChannelsByDeviceId(String deviceId) { |
| 333 | return deviceChannelMapper.queryChannels(deviceId, null,null, null, null); | 340 | return deviceChannelMapper.queryChannels(deviceId, null,null, null, null); |
| 334 | } | 341 | } |
src/main/java/com/genersoft/iot/vmp/utils/CollectionUtil.java
0 → 100644
| 1 | +package com.genersoft.iot.vmp.utils; | ||
| 2 | + | ||
| 3 | +import java.util.Arrays; | ||
| 4 | + | ||
| 5 | +public class CollectionUtil { | ||
| 6 | + | ||
| 7 | + public static <T> boolean contains(T[] array, final T element) { | ||
| 8 | + return array != null && Arrays.stream(array).anyMatch((x) -> { | ||
| 9 | + return ObjectUtils.nullSafeEquals(x, element); | ||
| 10 | + }); | ||
| 11 | + } | ||
| 12 | +} |
src/main/java/com/genersoft/iot/vmp/utils/ObjectUtils.java
0 → 100644
| 1 | +package com.genersoft.iot.vmp.utils; | ||
| 2 | + | ||
| 3 | +import java.util.Arrays; | ||
| 4 | + | ||
| 5 | +public class ObjectUtils { | ||
| 6 | + public static boolean nullSafeEquals(Object o1, Object o2) { | ||
| 7 | + if (o1 == o2) { | ||
| 8 | + return true; | ||
| 9 | + } else if (o1 != null && o2 != null) { | ||
| 10 | + if (o1.equals(o2)) { | ||
| 11 | + return true; | ||
| 12 | + } else { | ||
| 13 | + return o1.getClass().isArray() && o2.getClass().isArray() && arrayEquals(o1, o2); | ||
| 14 | + } | ||
| 15 | + } else { | ||
| 16 | + return false; | ||
| 17 | + } | ||
| 18 | + } | ||
| 19 | + | ||
| 20 | + private static boolean arrayEquals(Object o1, Object o2) { | ||
| 21 | + if (o1 instanceof Object[] && o2 instanceof Object[]) { | ||
| 22 | + return Arrays.equals((Object[])((Object[])o1), (Object[])((Object[])o2)); | ||
| 23 | + } else if (o1 instanceof boolean[] && o2 instanceof boolean[]) { | ||
| 24 | + return Arrays.equals((boolean[])((boolean[])o1), (boolean[])((boolean[])o2)); | ||
| 25 | + } else if (o1 instanceof byte[] && o2 instanceof byte[]) { | ||
| 26 | + return Arrays.equals((byte[])((byte[])o1), (byte[])((byte[])o2)); | ||
| 27 | + } else if (o1 instanceof char[] && o2 instanceof char[]) { | ||
| 28 | + return Arrays.equals((char[])((char[])o1), (char[])((char[])o2)); | ||
| 29 | + } else if (o1 instanceof double[] && o2 instanceof double[]) { | ||
| 30 | + return Arrays.equals((double[])((double[])o1), (double[])((double[])o2)); | ||
| 31 | + } else if (o1 instanceof float[] && o2 instanceof float[]) { | ||
| 32 | + return Arrays.equals((float[])((float[])o1), (float[])((float[])o2)); | ||
| 33 | + } else if (o1 instanceof int[] && o2 instanceof int[]) { | ||
| 34 | + return Arrays.equals((int[])((int[])o1), (int[])((int[])o2)); | ||
| 35 | + } else if (o1 instanceof long[] && o2 instanceof long[]) { | ||
| 36 | + return Arrays.equals((long[])((long[])o1), (long[])((long[])o2)); | ||
| 37 | + } else { | ||
| 38 | + return o1 instanceof short[] && o2 instanceof short[] && Arrays.equals((short[]) ((short[]) o1), (short[]) ((short[]) o2)); | ||
| 39 | + } | ||
| 40 | + } | ||
| 41 | +} |
src/main/java/com/genersoft/iot/vmp/utils/node/BaseNode.java
0 → 100644
| 1 | +package com.genersoft.iot.vmp.utils.node; | ||
| 2 | + | ||
| 3 | +import com.fasterxml.jackson.annotation.JsonInclude; | ||
| 4 | +import lombok.Data; | ||
| 5 | + | ||
| 6 | +import java.util.ArrayList; | ||
| 7 | +import java.util.List; | ||
| 8 | + | ||
| 9 | +/** | ||
| 10 | + * 节点基类 | ||
| 11 | + * | ||
| 12 | + */ | ||
| 13 | +@Data | ||
| 14 | +public class BaseNode<T> implements INode<T> { | ||
| 15 | + | ||
| 16 | + private static final long serialVersionUID = 1L; | ||
| 17 | + | ||
| 18 | + /** | ||
| 19 | + * 主键ID | ||
| 20 | + */ | ||
| 21 | + protected String id; | ||
| 22 | + | ||
| 23 | + /** | ||
| 24 | + * 父节点ID | ||
| 25 | + */ | ||
| 26 | + protected String parentId; | ||
| 27 | + | ||
| 28 | + /** | ||
| 29 | + * 子孙节点 | ||
| 30 | + */ | ||
| 31 | + @JsonInclude(JsonInclude.Include.NON_EMPTY) | ||
| 32 | + protected List<T> children = new ArrayList<T>(); | ||
| 33 | + | ||
| 34 | + /** | ||
| 35 | + * 是否有子孙节点 | ||
| 36 | + */ | ||
| 37 | + @JsonInclude(JsonInclude.Include.NON_EMPTY) | ||
| 38 | + private Boolean hasChildren; | ||
| 39 | + | ||
| 40 | + /** | ||
| 41 | + * 是否有子孙节点 | ||
| 42 | + * | ||
| 43 | + * @return Boolean | ||
| 44 | + */ | ||
| 45 | + @Override | ||
| 46 | + public Boolean getHasChildren() { | ||
| 47 | + if (children.size() > 0) { | ||
| 48 | + return true; | ||
| 49 | + } else { | ||
| 50 | + return this.hasChildren; | ||
| 51 | + } | ||
| 52 | + } | ||
| 53 | + | ||
| 54 | +} |
src/main/java/com/genersoft/iot/vmp/utils/node/ForestNode.java
0 → 100644
| 1 | +package com.genersoft.iot.vmp.utils.node; | ||
| 2 | + | ||
| 3 | +import lombok.Data; | ||
| 4 | +import lombok.EqualsAndHashCode; | ||
| 5 | + | ||
| 6 | + | ||
| 7 | +/** | ||
| 8 | + * 森林节点类 | ||
| 9 | + * | ||
| 10 | + */ | ||
| 11 | +@Data | ||
| 12 | +@EqualsAndHashCode(callSuper = false) | ||
| 13 | +public class ForestNode extends BaseNode<ForestNode> { | ||
| 14 | + | ||
| 15 | + private static final long serialVersionUID = 1L; | ||
| 16 | + | ||
| 17 | + /** | ||
| 18 | + * 节点内容 | ||
| 19 | + */ | ||
| 20 | + private Object content; | ||
| 21 | + | ||
| 22 | + public ForestNode(String id, String parentId, Object content) { | ||
| 23 | + this.id = id; | ||
| 24 | + this.parentId = parentId; | ||
| 25 | + this.content = content; | ||
| 26 | + } | ||
| 27 | + | ||
| 28 | +} |
src/main/java/com/genersoft/iot/vmp/utils/node/ForestNodeManager.java
0 → 100644
| 1 | +package com.genersoft.iot.vmp.utils.node; | ||
| 2 | + | ||
| 3 | +import com.google.common.collect.ImmutableMap; | ||
| 4 | +import com.google.common.collect.Maps; | ||
| 5 | + | ||
| 6 | +import java.util.ArrayList; | ||
| 7 | +import java.util.List; | ||
| 8 | +import java.util.Map; | ||
| 9 | + | ||
| 10 | +/** | ||
| 11 | + * 森林管理类 | ||
| 12 | + * | ||
| 13 | + * @author smallchill | ||
| 14 | + */ | ||
| 15 | +public class ForestNodeManager<T extends INode<T>> { | ||
| 16 | + | ||
| 17 | + /** | ||
| 18 | + * 森林的所有节点 | ||
| 19 | + */ | ||
| 20 | + private final ImmutableMap<String, T> nodeMap; | ||
| 21 | + | ||
| 22 | + /** | ||
| 23 | + * 森林的父节点ID | ||
| 24 | + */ | ||
| 25 | + private final Map<String, Object> parentIdMap = Maps.newHashMap(); | ||
| 26 | + | ||
| 27 | + public ForestNodeManager(List<T> nodes) { | ||
| 28 | + nodeMap = Maps.uniqueIndex(nodes, INode::getId); | ||
| 29 | + } | ||
| 30 | + | ||
| 31 | + /** | ||
| 32 | + * 根据节点ID获取一个节点 | ||
| 33 | + * | ||
| 34 | + * @param id 节点ID | ||
| 35 | + * @return 对应的节点对象 | ||
| 36 | + */ | ||
| 37 | + public INode<T> getTreeNodeAt(String id) { | ||
| 38 | + if (nodeMap.containsKey(id)) { | ||
| 39 | + return nodeMap.get(id); | ||
| 40 | + } | ||
| 41 | + return null; | ||
| 42 | + } | ||
| 43 | + | ||
| 44 | + /** | ||
| 45 | + * 增加父节点ID | ||
| 46 | + * | ||
| 47 | + * @param parentId 父节点ID | ||
| 48 | + */ | ||
| 49 | + public void addParentId(String parentId) { | ||
| 50 | + parentIdMap.put(parentId, ""); | ||
| 51 | + } | ||
| 52 | + | ||
| 53 | + /** | ||
| 54 | + * 获取树的根节点(一个森林对应多颗树) | ||
| 55 | + * | ||
| 56 | + * @return 树的根节点集合 | ||
| 57 | + */ | ||
| 58 | + public List<T> getRoot() { | ||
| 59 | + List<T> roots = new ArrayList<>(); | ||
| 60 | + nodeMap.forEach((key, node) -> { | ||
| 61 | + if (node.getParentId() == null || parentIdMap.containsKey(node.getId())) { | ||
| 62 | + roots.add(node); | ||
| 63 | + } | ||
| 64 | + }); | ||
| 65 | + return roots; | ||
| 66 | + } | ||
| 67 | + | ||
| 68 | +} |
src/main/java/com/genersoft/iot/vmp/utils/node/ForestNodeMerger.java
0 → 100644
| 1 | +package com.genersoft.iot.vmp.utils.node; | ||
| 2 | + | ||
| 3 | +import com.genersoft.iot.vmp.utils.CollectionUtil; | ||
| 4 | + | ||
| 5 | +import java.util.List; | ||
| 6 | + | ||
| 7 | +/** | ||
| 8 | + * 森林节点归并类 | ||
| 9 | + * | ||
| 10 | + */ | ||
| 11 | +public class ForestNodeMerger { | ||
| 12 | + | ||
| 13 | + /** | ||
| 14 | + * 将节点数组归并为一个森林(多棵树)(填充节点的children域) | ||
| 15 | + * 时间复杂度为O(n^2) | ||
| 16 | + * | ||
| 17 | + * @param items 节点域 | ||
| 18 | + * @return 多棵树的根节点集合 | ||
| 19 | + */ | ||
| 20 | + public static <T extends INode<T>> List<T> merge(List<T> items) { | ||
| 21 | + ForestNodeManager<T> forestNodeManager = new ForestNodeManager<>(items); | ||
| 22 | + items.forEach(forestNode -> { | ||
| 23 | + if (forestNode.getParentId() != null) { | ||
| 24 | + INode<T> node = forestNodeManager.getTreeNodeAt(forestNode.getParentId()); | ||
| 25 | + if (node != null) { | ||
| 26 | + node.getChildren().add(forestNode); | ||
| 27 | + } else { | ||
| 28 | + forestNodeManager.addParentId(forestNode.getId()); | ||
| 29 | + } | ||
| 30 | + } | ||
| 31 | + }); | ||
| 32 | + return forestNodeManager.getRoot(); | ||
| 33 | + } | ||
| 34 | + | ||
| 35 | + public static <T extends INode<T>> List<T> merge(List<T> items, String[] parentIds) { | ||
| 36 | + ForestNodeManager<T> forestNodeManager = new ForestNodeManager<>(items); | ||
| 37 | + items.forEach(forestNode -> { | ||
| 38 | + if (forestNode.getParentId() != null) { | ||
| 39 | + INode<T> node = forestNodeManager.getTreeNodeAt(forestNode.getParentId()); | ||
| 40 | + if (CollectionUtil.contains(parentIds, forestNode.getId())){ | ||
| 41 | + forestNodeManager.addParentId(forestNode.getId()); | ||
| 42 | + } else { | ||
| 43 | + if (node != null){ | ||
| 44 | + node.getChildren().add(forestNode); | ||
| 45 | + } | ||
| 46 | + } | ||
| 47 | + } | ||
| 48 | + }); | ||
| 49 | + return forestNodeManager.getRoot(); | ||
| 50 | + } | ||
| 51 | +} |
src/main/java/com/genersoft/iot/vmp/utils/node/INode.java
0 → 100644
| 1 | +package com.genersoft.iot.vmp.utils.node; | ||
| 2 | + | ||
| 3 | +import java.io.Serializable; | ||
| 4 | +import java.util.List; | ||
| 5 | + | ||
| 6 | +/** | ||
| 7 | + * | ||
| 8 | + * 节点 | ||
| 9 | + */ | ||
| 10 | +public interface INode<T> extends Serializable { | ||
| 11 | + | ||
| 12 | + /** | ||
| 13 | + * 主键 | ||
| 14 | + * | ||
| 15 | + * @return String | ||
| 16 | + */ | ||
| 17 | + String getId(); | ||
| 18 | + | ||
| 19 | + /** | ||
| 20 | + * 父主键 | ||
| 21 | + * | ||
| 22 | + * @return String | ||
| 23 | + */ | ||
| 24 | + String getParentId(); | ||
| 25 | + | ||
| 26 | + /** | ||
| 27 | + * 子孙节点 | ||
| 28 | + * | ||
| 29 | + * @return List<T> | ||
| 30 | + */ | ||
| 31 | + List<T> getChildren(); | ||
| 32 | + | ||
| 33 | + /** | ||
| 34 | + * 是否有子孙节点 | ||
| 35 | + * | ||
| 36 | + * @return Boolean | ||
| 37 | + */ | ||
| 38 | + default Boolean getHasChildren() { | ||
| 39 | + return false; | ||
| 40 | + } | ||
| 41 | + | ||
| 42 | +} |
src/main/java/com/genersoft/iot/vmp/utils/node/TreeNode.java
0 → 100644
| 1 | +package com.genersoft.iot.vmp.utils.node; | ||
| 2 | + | ||
| 3 | +import lombok.Data; | ||
| 4 | +import lombok.EqualsAndHashCode; | ||
| 5 | + | ||
| 6 | +/** | ||
| 7 | + * 树型节点类 | ||
| 8 | + * | ||
| 9 | + */ | ||
| 10 | +@Data | ||
| 11 | +@EqualsAndHashCode(callSuper = false) | ||
| 12 | +public class TreeNode extends BaseNode<TreeNode> { | ||
| 13 | + | ||
| 14 | + private static final long serialVersionUID = 1L; | ||
| 15 | + | ||
| 16 | + private String title; | ||
| 17 | + | ||
| 18 | + private String key; | ||
| 19 | + | ||
| 20 | + private String value; | ||
| 21 | +} |
src/main/java/com/genersoft/iot/vmp/vmanager/bean/DeviceChannelTree.java
0 → 100644
| 1 | +package com.genersoft.iot.vmp.vmanager.bean; | ||
| 2 | + | ||
| 3 | +import com.fasterxml.jackson.annotation.JsonInclude; | ||
| 4 | +import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; | ||
| 5 | +import com.genersoft.iot.vmp.utils.node.INode; | ||
| 6 | +import io.swagger.annotations.ApiModel; | ||
| 7 | +import lombok.Data; | ||
| 8 | +import lombok.EqualsAndHashCode; | ||
| 9 | + | ||
| 10 | +import java.util.ArrayList; | ||
| 11 | +import java.util.List; | ||
| 12 | + | ||
| 13 | +@Data | ||
| 14 | +@EqualsAndHashCode(callSuper = true) | ||
| 15 | +@ApiModel(value = "DeviceChannelTree对象", description = "DeviceChannelTree对象") | ||
| 16 | +public class DeviceChannelTree extends DeviceChannel implements INode<DeviceChannelTree> { | ||
| 17 | + private static final long serialVersionUID = 1L; | ||
| 18 | + | ||
| 19 | + /** | ||
| 20 | + * 主键ID | ||
| 21 | + */ | ||
| 22 | + private String id; | ||
| 23 | + | ||
| 24 | + /** | ||
| 25 | + * 父节点ID | ||
| 26 | + */ | ||
| 27 | + private String parentId; | ||
| 28 | + | ||
| 29 | + private String parentName; | ||
| 30 | + | ||
| 31 | + /** | ||
| 32 | + * 子孙节点 | ||
| 33 | + */ | ||
| 34 | + @JsonInclude(JsonInclude.Include.NON_EMPTY) | ||
| 35 | + private List<DeviceChannelTree> children; | ||
| 36 | + | ||
| 37 | + /** | ||
| 38 | + * 是否有子孙节点 | ||
| 39 | + */ | ||
| 40 | + @JsonInclude(JsonInclude.Include.NON_EMPTY) | ||
| 41 | + private Boolean hasChildren; | ||
| 42 | + | ||
| 43 | + @Override | ||
| 44 | + public List<DeviceChannelTree> getChildren() { | ||
| 45 | + if (this.children == null) { | ||
| 46 | + this.children = new ArrayList<>(); | ||
| 47 | + } | ||
| 48 | + return this.children; | ||
| 49 | + } | ||
| 50 | +} |
src/main/java/com/genersoft/iot/vmp/vmanager/bean/DeviceChannelTreeNode.java
0 → 100644
| 1 | +package com.genersoft.iot.vmp.vmanager.bean; | ||
| 2 | + | ||
| 3 | +import com.genersoft.iot.vmp.utils.node.TreeNode; | ||
| 4 | +import lombok.Data; | ||
| 5 | +import lombok.EqualsAndHashCode; | ||
| 6 | + | ||
| 7 | +@Data | ||
| 8 | +@EqualsAndHashCode(callSuper = true) | ||
| 9 | +public class DeviceChannelTreeNode extends TreeNode { | ||
| 10 | + | ||
| 11 | + private Integer status; | ||
| 12 | + | ||
| 13 | + private String deviceId; | ||
| 14 | + | ||
| 15 | + private String channelId; | ||
| 16 | + | ||
| 17 | + private Double lng; | ||
| 18 | + | ||
| 19 | + private Double lat; | ||
| 20 | +} |
src/main/java/com/genersoft/iot/vmp/vmanager/bean/WVPResult.java
| 1 | package com.genersoft.iot.vmp.vmanager.bean; | 1 | package com.genersoft.iot.vmp.vmanager.bean; |
| 2 | 2 | ||
| 3 | +import lombok.AllArgsConstructor; | ||
| 4 | +import lombok.Data; | ||
| 5 | +import lombok.NoArgsConstructor; | ||
| 6 | + | ||
| 7 | +@Data | ||
| 8 | +@NoArgsConstructor | ||
| 9 | +@AllArgsConstructor | ||
| 3 | public class WVPResult<T> { | 10 | public class WVPResult<T> { |
| 4 | 11 | ||
| 5 | private int code; | 12 | private int code; |
| 6 | private String msg; | 13 | private String msg; |
| 7 | private T data; | 14 | private T data; |
| 8 | 15 | ||
| 9 | - public int getCode() { | ||
| 10 | - return code; | ||
| 11 | - } | 16 | + private static final Integer SUCCESS = 200; |
| 17 | + private static final Integer FAILED = 400; | ||
| 12 | 18 | ||
| 13 | - public void setCode(int code) { | ||
| 14 | - this.code = code; | 19 | + public static <T> WVPResult<T> Data(T t, String msg) { |
| 20 | + return new WVPResult<>(SUCCESS, msg, t); | ||
| 15 | } | 21 | } |
| 16 | 22 | ||
| 17 | - public String getMsg() { | ||
| 18 | - return msg; | 23 | + public static <T> WVPResult<T> Data(T t) { |
| 24 | + return Data(t, "成功"); | ||
| 19 | } | 25 | } |
| 20 | 26 | ||
| 21 | - public void setMsg(String msg) { | ||
| 22 | - this.msg = msg; | 27 | + public static <T> WVPResult<T> fail(int code, String msg) { |
| 28 | + return new WVPResult<>(code, msg, null); | ||
| 23 | } | 29 | } |
| 24 | 30 | ||
| 25 | - public T getData() { | ||
| 26 | - return data; | 31 | + public static <T> WVPResult<T> fail(String msg) { |
| 32 | + return fail(FAILED, msg); | ||
| 27 | } | 33 | } |
| 28 | 34 | ||
| 29 | - public void setData(T data) { | ||
| 30 | - this.data = data; | ||
| 31 | - } | ||
| 32 | } | 35 | } |
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceQuery.java
| @@ -10,8 +10,10 @@ import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; | @@ -10,8 +10,10 @@ import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; | ||
| 10 | import com.genersoft.iot.vmp.service.IDeviceService; | 10 | import com.genersoft.iot.vmp.service.IDeviceService; |
| 11 | import com.genersoft.iot.vmp.storager.IRedisCatchStorage; | 11 | import com.genersoft.iot.vmp.storager.IRedisCatchStorage; |
| 12 | import com.genersoft.iot.vmp.storager.IVideoManagerStorager; | 12 | import com.genersoft.iot.vmp.storager.IVideoManagerStorager; |
| 13 | +import com.genersoft.iot.vmp.vmanager.bean.DeviceChannelTree; | ||
| 13 | import com.genersoft.iot.vmp.vmanager.bean.WVPResult; | 14 | import com.genersoft.iot.vmp.vmanager.bean.WVPResult; |
| 14 | import com.github.pagehelper.PageInfo; | 15 | import com.github.pagehelper.PageInfo; |
| 16 | +import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport; | ||
| 15 | import io.swagger.annotations.Api; | 17 | import io.swagger.annotations.Api; |
| 16 | import io.swagger.annotations.ApiImplicitParam; | 18 | import io.swagger.annotations.ApiImplicitParam; |
| 17 | import io.swagger.annotations.ApiImplicitParams; | 19 | import io.swagger.annotations.ApiImplicitParams; |
| @@ -25,6 +27,7 @@ import org.springframework.util.StringUtils; | @@ -25,6 +27,7 @@ import org.springframework.util.StringUtils; | ||
| 25 | import org.springframework.web.bind.annotation.*; | 27 | import org.springframework.web.bind.annotation.*; |
| 26 | import org.springframework.web.context.request.async.DeferredResult; | 28 | import org.springframework.web.context.request.async.DeferredResult; |
| 27 | 29 | ||
| 30 | +import java.util.List; | ||
| 28 | import java.util.UUID; | 31 | import java.util.UUID; |
| 29 | 32 | ||
| 30 | @Api(tags = "国标设备查询", value = "国标设备查询") | 33 | @Api(tags = "国标设备查询", value = "国标设备查询") |
| @@ -431,5 +434,9 @@ public class DeviceQuery { | @@ -431,5 +434,9 @@ public class DeviceQuery { | ||
| 431 | return result; | 434 | return result; |
| 432 | } | 435 | } |
| 433 | 436 | ||
| 434 | - | 437 | + @GetMapping("/{deviceId}/tree") |
| 438 | + @ApiOperation(value = "通道树形结构", notes = "通道树形结构") | ||
| 439 | + public WVPResult<List<DeviceChannelTree>> tree(@PathVariable String deviceId) { | ||
| 440 | + return WVPResult.Data(storager.tree(deviceId)); | ||
| 441 | + } | ||
| 435 | } | 442 | } |
web_src/src/api/deviceApi.js
0 → 100644
| 1 | +import axios from 'axios'; | ||
| 2 | + | ||
| 3 | +export const tree = (deviceId) => { | ||
| 4 | + return axios({ | ||
| 5 | + url: `/api/device/query/${deviceId}/tree`, | ||
| 6 | + method: 'get' | ||
| 7 | + }) | ||
| 8 | +} | ||
| 9 | + | ||
| 10 | +export const deviceList = (page, count) => { | ||
| 11 | + return axios({ | ||
| 12 | + method: 'get', | ||
| 13 | + url:`/api/device/query/devices`, | ||
| 14 | + params: { | ||
| 15 | + page, | ||
| 16 | + count | ||
| 17 | + } | ||
| 18 | + }) | ||
| 19 | +} | ||
| 0 | \ No newline at end of file | 20 | \ No newline at end of file |
web_src/src/components/UiHeader.vue
| @@ -2,6 +2,7 @@ | @@ -2,6 +2,7 @@ | ||
| 2 | <div id="UiHeader"> | 2 | <div id="UiHeader"> |
| 3 | <el-menu router :default-active="activeIndex" menu-trigger="click" background-color="#545c64" text-color="#fff" active-text-color="#ffd04b" mode="horizontal"> | 3 | <el-menu router :default-active="activeIndex" menu-trigger="click" background-color="#545c64" text-color="#fff" active-text-color="#ffd04b" mode="horizontal"> |
| 4 | <el-menu-item index="/">控制台</el-menu-item> | 4 | <el-menu-item index="/">控制台</el-menu-item> |
| 5 | + <el-menu-item index="/live">实时监控</el-menu-item> | ||
| 5 | <el-menu-item index="/deviceList">设备列表</el-menu-item> | 6 | <el-menu-item index="/deviceList">设备列表</el-menu-item> |
| 6 | <el-menu-item index="/pushVideoList">推流列表</el-menu-item> | 7 | <el-menu-item index="/pushVideoList">推流列表</el-menu-item> |
| 7 | <el-menu-item index="/streamProxyList">拉流代理</el-menu-item> | 8 | <el-menu-item index="/streamProxyList">拉流代理</el-menu-item> |
web_src/src/components/channelTree.vue
0 → 100644
| 1 | +<template> | ||
| 2 | + <div> | ||
| 3 | + <el-tree :data="channelList" :props="props" @node-click="sendDevicePush"> | ||
| 4 | + <span slot-scope="{ node }"> | ||
| 5 | + <span v-if="node.isLeaf"> | ||
| 6 | + <i class="el-icon-video-camera" :style="{color:node.disabled==1?'#67C23A':'#F56C6C'}"></i> | ||
| 7 | + </span> | ||
| 8 | + <span v-else> | ||
| 9 | + <i class="el-icon-folder"></i> | ||
| 10 | + </span> | ||
| 11 | + <span> | ||
| 12 | + {{ node.label }} | ||
| 13 | + </span> | ||
| 14 | + </span> | ||
| 15 | + </el-tree> | ||
| 16 | + </div> | ||
| 17 | +</template> | ||
| 18 | +<script> | ||
| 19 | +import ChannelTreeItem from "@/components/channelTreeItem" | ||
| 20 | +import {tree} from '@/api/deviceApi' | ||
| 21 | + | ||
| 22 | +export default { | ||
| 23 | + components: { | ||
| 24 | + ChannelTreeItem, | ||
| 25 | + }, | ||
| 26 | + props:{ | ||
| 27 | + device: { | ||
| 28 | + type: Object, | ||
| 29 | + required: true | ||
| 30 | + } | ||
| 31 | + }, | ||
| 32 | + data() { | ||
| 33 | + return { | ||
| 34 | + loading: false, | ||
| 35 | + channelList: [], | ||
| 36 | + props: { | ||
| 37 | + label: 'title', | ||
| 38 | + children: 'children', | ||
| 39 | + isLeaf: 'hasChildren', | ||
| 40 | + disabled: 'status' | ||
| 41 | + }, | ||
| 42 | + } | ||
| 43 | + }, | ||
| 44 | + computed: { | ||
| 45 | + | ||
| 46 | + }, | ||
| 47 | + mounted() { | ||
| 48 | + this.leafs = [] | ||
| 49 | + this.getTree() | ||
| 50 | + }, | ||
| 51 | + methods: { | ||
| 52 | + getTree() { | ||
| 53 | + this.loading = true | ||
| 54 | + var that = this | ||
| 55 | + tree(this.device.deviceId).then(function (res) { | ||
| 56 | + console.log(res.data.data); | ||
| 57 | + that.channelList = res.data.data; | ||
| 58 | + that.loading = false; | ||
| 59 | + }).catch(function (error) { | ||
| 60 | + console.log(error); | ||
| 61 | + that.loading = false; | ||
| 62 | + }); | ||
| 63 | + }, | ||
| 64 | + sendDevicePush(c) { | ||
| 65 | + if(c.hasChildren) return | ||
| 66 | + this.$emit('sendDevicePush',c) | ||
| 67 | + } | ||
| 68 | + } | ||
| 69 | +} | ||
| 70 | +</script> | ||
| 0 | \ No newline at end of file | 71 | \ No newline at end of file |
web_src/src/components/channelTreeItem.vue
0 → 100644
| 1 | +<template> | ||
| 2 | + <div> | ||
| 3 | + <!-- <div :index="item.key" v-for="(item,i) in list" :key="i+'-'"> | ||
| 4 | + <el-submenu v-if="item.hasChildren"> | ||
| 5 | + <template slot="title"> | ||
| 6 | + <i class="el-icon-video-camera"></i> | ||
| 7 | + <span slot="title">{{item.title || item.deviceId}}</span> | ||
| 8 | + </template> | ||
| 9 | + <channel-list :list="item.children" @sendDevicePush="sendDevicePush"></channel-list> | ||
| 10 | + </el-submenu> | ||
| 11 | + <el-menu-item v-else :index="item.key" @click="sendDevicePush(item)"> | ||
| 12 | + <template slot="title" > | ||
| 13 | + <i class="el-icon-switch-button" :style="{color:item.status==1?'#67C23A':'#F56C6C'}"></i> | ||
| 14 | + <span slot="title">{{item.title}}</span> | ||
| 15 | + </template> | ||
| 16 | + </el-menu-item> | ||
| 17 | + </div> --> | ||
| 18 | + <div > | ||
| 19 | + <template v-if="!item.hasChildren"> | ||
| 20 | + <el-menu-item :index="item.key" @click="sendDevicePush(item)"> | ||
| 21 | + <i class="el-icon-video-camera" :style="{color:item.status==1?'#67C23A':'#F56C6C'}"></i> | ||
| 22 | + {{item.title}} | ||
| 23 | + </el-menu-item> | ||
| 24 | + </template> | ||
| 25 | + | ||
| 26 | + <el-submenu v-else :index="item.key"> | ||
| 27 | + <template slot="title" > | ||
| 28 | + <i class="el-icon-location-outline"></i> | ||
| 29 | + {{item.title}} | ||
| 30 | + </template> | ||
| 31 | + | ||
| 32 | + <template v-for="child in item.children"> | ||
| 33 | + <channel-item | ||
| 34 | + v-if="child.hasChildren" | ||
| 35 | + :item="child" | ||
| 36 | + :key="child.key" | ||
| 37 | + @sendDevicePush="sendDevicePush"/> | ||
| 38 | + <el-menu-item v-else :key="child.key" :index="child.key" @click="sendDevicePush(child)"> | ||
| 39 | + <i class="el-icon-video-camera" :style="{color:child.status==1?'#67C23A':'#F56C6C'}"></i> | ||
| 40 | + {{child.title}} | ||
| 41 | + </el-menu-item> | ||
| 42 | + </template> | ||
| 43 | + </el-submenu> | ||
| 44 | + </div> | ||
| 45 | + </div> | ||
| 46 | +</template> | ||
| 47 | +<script> | ||
| 48 | +export default { | ||
| 49 | + name:'ChannelItem', | ||
| 50 | + props:{ | ||
| 51 | + list:Array, | ||
| 52 | + channelId: String, | ||
| 53 | + item: { | ||
| 54 | + type: Object, | ||
| 55 | + required: true | ||
| 56 | + } | ||
| 57 | + }, | ||
| 58 | + data () { | ||
| 59 | + return { | ||
| 60 | + | ||
| 61 | + } | ||
| 62 | + }, | ||
| 63 | + watch: { | ||
| 64 | + channelId(val) { | ||
| 65 | + console.log(val); | ||
| 66 | + } | ||
| 67 | + }, | ||
| 68 | + methods: { | ||
| 69 | + sendDevicePush(c) { | ||
| 70 | + this.$emit('sendDevicePush',c) | ||
| 71 | + } | ||
| 72 | + } | ||
| 73 | +} | ||
| 74 | +</script> |
web_src/src/components/jessibuca.vue
0 → 100644
| 1 | +<template> | ||
| 2 | + <div :id="'jessibuca'+idx" style="width: 100%; height: 100%"> | ||
| 3 | + <div :id="'container'+idx" ref="container" style="width: 100%; height: 100%; background-color: #000" @dblclick="fullscreenSwich"> | ||
| 4 | + <div class="buttons-box" :id="'buttonsBox'+idx"> | ||
| 5 | + <div class="buttons-box-left"> | ||
| 6 | + <i v-if="!playing" class="iconfont icon-play jessibuca-btn" @click="playBtnClick"></i> | ||
| 7 | + <i v-if="playing" class="iconfont icon-pause jessibuca-btn" @click="pause"></i> | ||
| 8 | + <i class="iconfont icon-stop jessibuca-btn" @click="destroyButton"></i> | ||
| 9 | + <i v-if="isNotMute" class="iconfont icon-audio-high jessibuca-btn" @click="jessibuca.mute()"></i> | ||
| 10 | + <i v-if="!isNotMute" class="iconfont icon-audio-mute jessibuca-btn" @click="jessibuca.cancelMute()"></i> | ||
| 11 | + </div> | ||
| 12 | + <div class="buttons-box-right"> | ||
| 13 | + <span class="jessibuca-btn">{{kBps}} kb/s</span> | ||
| 14 | +<!-- <i class="iconfont icon-file-record1 jessibuca-btn"></i>--> | ||
| 15 | +<!-- <i class="iconfont icon-xiangqing2 jessibuca-btn" ></i>--> | ||
| 16 | + <i class="iconfont icon-camera1196054easyiconnet jessibuca-btn" @click="screenshot" style="font-size: 1rem !important"></i> | ||
| 17 | + <i class="iconfont icon-shuaxin11 jessibuca-btn" @click="playBtnClick"></i> | ||
| 18 | + <i v-if="!fullscreen" class="iconfont icon-weibiaoti10 jessibuca-btn" @click="fullscreenSwich"></i> | ||
| 19 | + <i v-if="fullscreen" class="iconfont icon-weibiaoti11 jessibuca-btn" @click="fullscreenSwich"></i> | ||
| 20 | + </div> | ||
| 21 | + </div> | ||
| 22 | + | ||
| 23 | + </div> | ||
| 24 | + </div> | ||
| 25 | +</template> | ||
| 26 | + | ||
| 27 | +<script> | ||
| 28 | +export default { | ||
| 29 | + name: 'jessibuca', | ||
| 30 | + data() { | ||
| 31 | + return { | ||
| 32 | + jessibuca: null, | ||
| 33 | + playing: false, | ||
| 34 | + isNotMute: false, | ||
| 35 | + quieting: false, | ||
| 36 | + fullscreen: false, | ||
| 37 | + loaded: false, // mute | ||
| 38 | + speed: 0, | ||
| 39 | + performance: "", // 工作情况 | ||
| 40 | + kBps: 0, | ||
| 41 | + btnDom: null, | ||
| 42 | + videoInfo: null, | ||
| 43 | + volume: 1, | ||
| 44 | + rotate: 0, | ||
| 45 | + vod: true, // 点播 | ||
| 46 | + forceNoOffscreen: false, | ||
| 47 | + }; | ||
| 48 | + }, | ||
| 49 | + props: ['videoUrl', 'error', 'hasAudio', 'height','idx'], | ||
| 50 | + mounted () { | ||
| 51 | + window.onerror = (msg) => { | ||
| 52 | + // console.error(msg) | ||
| 53 | + }; | ||
| 54 | + let paramUrl = decodeURIComponent(this.$route.params.url) | ||
| 55 | + this.$nextTick(() =>{ | ||
| 56 | + let dom = document.getElementById("container"+this.idx); | ||
| 57 | + // dom.style.height = (9/16 ) * dom.clientWidth + "px" | ||
| 58 | + if (typeof (this.videoUrl) == "undefined") { | ||
| 59 | + this.videoUrl = paramUrl; | ||
| 60 | + } | ||
| 61 | + this.btnDom = document.getElementById("buttonsBox"+this.idx); | ||
| 62 | + console.log("初始化时的地址为: " + this.videoUrl) | ||
| 63 | + this.play(this.videoUrl) | ||
| 64 | + }) | ||
| 65 | + }, | ||
| 66 | + watch:{ | ||
| 67 | + videoUrl(newData, oldData){ | ||
| 68 | + this.play(newData) | ||
| 69 | + }, | ||
| 70 | + immediate:true | ||
| 71 | + }, | ||
| 72 | + methods: { | ||
| 73 | + create(){ | ||
| 74 | + let options = {}; | ||
| 75 | + console.log(this.$refs.container) | ||
| 76 | + console.log("hasAudio " + !!this.hasAudio) | ||
| 77 | + | ||
| 78 | + this.jessibuca = new window.Jessibuca(Object.assign( | ||
| 79 | + { | ||
| 80 | + container: this.$refs.container, | ||
| 81 | + videoBuffer: 0.2, // 最大缓冲时长,单位秒 | ||
| 82 | + isResize: true, | ||
| 83 | + decoder: "./static/js/jessibuca/index.js", | ||
| 84 | + // text: "WVP-PRO", | ||
| 85 | + // background: "bg.jpg", | ||
| 86 | + loadingText: "加载中", | ||
| 87 | + hasAudio: !!this.hasAudio, | ||
| 88 | + debug: false, | ||
| 89 | + timeout:5, | ||
| 90 | + supportDblclickFullscreen: false, // 是否支持屏幕的双击事件,触发全屏,取消全屏事件。 | ||
| 91 | + operateBtns: { | ||
| 92 | + fullscreen: false, | ||
| 93 | + screenshot: false, | ||
| 94 | + play: false, | ||
| 95 | + audio: false, | ||
| 96 | + }, | ||
| 97 | + record: "record", | ||
| 98 | + vod: this.vod, | ||
| 99 | + forceNoOffscreen: this.forceNoOffscreen, | ||
| 100 | + isNotMute: this.isNotMute, | ||
| 101 | + }, | ||
| 102 | + options | ||
| 103 | + )); | ||
| 104 | + | ||
| 105 | + let _this = this; | ||
| 106 | + this.jessibuca.on("load", function () { | ||
| 107 | + console.log("on load init"); | ||
| 108 | + }); | ||
| 109 | + | ||
| 110 | + this.jessibuca.on("log", function (msg) { | ||
| 111 | + console.log("on log", msg); | ||
| 112 | + }); | ||
| 113 | + this.jessibuca.on("record", function (msg) { | ||
| 114 | + console.log("on record:", msg); | ||
| 115 | + }); | ||
| 116 | + this.jessibuca.on("pause", function () { | ||
| 117 | + _this.playing = false; | ||
| 118 | + }); | ||
| 119 | + this.jessibuca.on("play", function () { | ||
| 120 | + _this.playing = true; | ||
| 121 | + }); | ||
| 122 | + this.jessibuca.on("fullscreen", function (msg) { | ||
| 123 | + console.log("on fullscreen", msg); | ||
| 124 | + _this.fullscreen = msg | ||
| 125 | + }); | ||
| 126 | + | ||
| 127 | + this.jessibuca.on("mute", function (msg) { | ||
| 128 | + console.log("on mute", msg); | ||
| 129 | + _this.isNotMute = !msg; | ||
| 130 | + }); | ||
| 131 | + this.jessibuca.on("audioInfo", function (msg) { | ||
| 132 | + // console.log("audioInfo", msg); | ||
| 133 | + }); | ||
| 134 | + | ||
| 135 | + this.jessibuca.on("videoInfo", function (msg) { | ||
| 136 | + this.videoInfo = msg; | ||
| 137 | + // console.log("videoInfo", msg); | ||
| 138 | + | ||
| 139 | + }); | ||
| 140 | + | ||
| 141 | + this.jessibuca.on("bps", function (bps) { | ||
| 142 | + // console.log('bps', bps); | ||
| 143 | + | ||
| 144 | + }); | ||
| 145 | + let _ts = 0; | ||
| 146 | + this.jessibuca.on("timeUpdate", function (ts) { | ||
| 147 | + // console.log('timeUpdate,old,new,timestamp', _ts, ts, ts - _ts); | ||
| 148 | + _ts = ts; | ||
| 149 | + }); | ||
| 150 | + | ||
| 151 | + this.jessibuca.on("videoInfo", function (info) { | ||
| 152 | + console.log("videoInfo", info); | ||
| 153 | + }); | ||
| 154 | + | ||
| 155 | + this.jessibuca.on("error", (error) =>{ | ||
| 156 | + console.log("error", error); | ||
| 157 | + this.pause() | ||
| 158 | + }); | ||
| 159 | + | ||
| 160 | + this.jessibuca.on("timeout", ()=> { | ||
| 161 | + console.log("timeout"); | ||
| 162 | + // this.pause() | ||
| 163 | + this.play(this.videoUrl) | ||
| 164 | + }); | ||
| 165 | + | ||
| 166 | + this.jessibuca.on('start', function () { | ||
| 167 | + console.log('start'); | ||
| 168 | + }) | ||
| 169 | + | ||
| 170 | + this.jessibuca.on("performance", function (performance) { | ||
| 171 | + let show = "卡顿"; | ||
| 172 | + if (performance === 2) { | ||
| 173 | + show = "非常流畅"; | ||
| 174 | + } else if (performance === 1) { | ||
| 175 | + show = "流畅"; | ||
| 176 | + } | ||
| 177 | + _this.performance = show; | ||
| 178 | + }); | ||
| 179 | + this.jessibuca.on('buffer', function (buffer) { | ||
| 180 | + // console.log('buffer', buffer); | ||
| 181 | + }) | ||
| 182 | + | ||
| 183 | + this.jessibuca.on('stats', function (stats) { | ||
| 184 | + // console.log('stats', stats); | ||
| 185 | + }) | ||
| 186 | + | ||
| 187 | + this.jessibuca.on('kBps', function (kBps) { | ||
| 188 | + _this.kBps = Math.round(kBps); | ||
| 189 | + }); | ||
| 190 | + | ||
| 191 | + // 显示时间戳 PTS | ||
| 192 | + this.jessibuca.on('videoFrame', function () { | ||
| 193 | + | ||
| 194 | + }) | ||
| 195 | + | ||
| 196 | + // | ||
| 197 | + this.jessibuca.on('metadata', function () { | ||
| 198 | + | ||
| 199 | + }); | ||
| 200 | + }, | ||
| 201 | + playBtnClick: function (event){ | ||
| 202 | + this.play(this.videoUrl) | ||
| 203 | + }, | ||
| 204 | + play: function (url) { | ||
| 205 | + console.log(url) | ||
| 206 | + | ||
| 207 | + if (this.jessibuca) { | ||
| 208 | + this.destroy(); | ||
| 209 | + } | ||
| 210 | + if(!url){ | ||
| 211 | + return | ||
| 212 | + } | ||
| 213 | + this.create(); | ||
| 214 | + this.jessibuca.on("play", () => { | ||
| 215 | + this.playing = true; | ||
| 216 | + this.loaded = true; | ||
| 217 | + this.quieting = this.jessibuca.quieting; | ||
| 218 | + }); | ||
| 219 | + if (this.jessibuca.hasLoaded()) { | ||
| 220 | + this.jessibuca.play(url); | ||
| 221 | + } else { | ||
| 222 | + this.jessibuca.on("load", () => { | ||
| 223 | + console.log("load 播放") | ||
| 224 | + this.jessibuca.play(url); | ||
| 225 | + }); | ||
| 226 | + } | ||
| 227 | + }, | ||
| 228 | + pause: function () { | ||
| 229 | + if (this.jessibuca) { | ||
| 230 | + this.jessibuca.pause(); | ||
| 231 | + } | ||
| 232 | + this.playing = false; | ||
| 233 | + this.err = ""; | ||
| 234 | + this.performance = ""; | ||
| 235 | + }, | ||
| 236 | + destroy: function () { | ||
| 237 | + if (this.jessibuca) { | ||
| 238 | + this.jessibuca.destroy(); | ||
| 239 | + } | ||
| 240 | + if (document.getElementById("buttonsBox"+this.idx) == null) { | ||
| 241 | + document.getElementById("container"+this.idx).appendChild(this.btnDom) | ||
| 242 | + } | ||
| 243 | + this.jessibuca = null; | ||
| 244 | + this.playing = false; | ||
| 245 | + this.err = ""; | ||
| 246 | + this.performance = ""; | ||
| 247 | + | ||
| 248 | + }, | ||
| 249 | + eventcallbacK: function(type, message) { | ||
| 250 | + // console.log("player 事件回调") | ||
| 251 | + // console.log(type) | ||
| 252 | + // console.log(message) | ||
| 253 | + }, | ||
| 254 | + fullscreenSwich: function (){ | ||
| 255 | + let isFull = this.isFullscreen() | ||
| 256 | + this.jessibuca.setFullscreen(!isFull) | ||
| 257 | + this.fullscreen = !isFull; | ||
| 258 | + }, | ||
| 259 | + isFullscreen: function (){ | ||
| 260 | + return document.fullscreenElement || | ||
| 261 | + document.msFullscreenElement || | ||
| 262 | + document.mozFullScreenElement || | ||
| 263 | + document.webkitFullscreenElement || false; | ||
| 264 | + }, | ||
| 265 | + resize(){ | ||
| 266 | + this.jessibuca.resize() | ||
| 267 | + }, | ||
| 268 | + screenshot(){ | ||
| 269 | + this.jessibuca.screenshot('截图','png',0.5) | ||
| 270 | + // let base64 = this.jessibuca.screenshot("shot","jpeg",0.5,'base64') | ||
| 271 | + // this.$emit('screenshot',base64) | ||
| 272 | + }, | ||
| 273 | + destroyButton() { | ||
| 274 | + this.$emit('destroy', this.idx) | ||
| 275 | + this.destroy() | ||
| 276 | + } | ||
| 277 | + }, | ||
| 278 | + destroyed() { | ||
| 279 | + if (this.jessibuca) { | ||
| 280 | + this.jessibuca.destroy(); | ||
| 281 | + } | ||
| 282 | + this.playing = false; | ||
| 283 | + this.loaded = false; | ||
| 284 | + this.performance = ""; | ||
| 285 | + }, | ||
| 286 | +} | ||
| 287 | +</script> | ||
| 288 | + | ||
| 289 | +<style> | ||
| 290 | + .buttons-box{ | ||
| 291 | + width: 100%; | ||
| 292 | + height: 28px; | ||
| 293 | + background-color: rgba(43, 51, 63, 0.7); | ||
| 294 | + position: absolute; | ||
| 295 | + display: -webkit-box; | ||
| 296 | + display: -ms-flexbox; | ||
| 297 | + display: flex; | ||
| 298 | + left: 0; | ||
| 299 | + bottom: 0; | ||
| 300 | + user-select: none; | ||
| 301 | + z-index: 10; | ||
| 302 | + } | ||
| 303 | + .jessibuca-btn{ | ||
| 304 | + width: 20px; | ||
| 305 | + color: rgb(255, 255, 255); | ||
| 306 | + line-height: 27px; | ||
| 307 | + margin: 0px 10px; | ||
| 308 | + padding: 0px 2px; | ||
| 309 | + cursor: pointer; | ||
| 310 | + text-align: center; | ||
| 311 | + font-size: 0.8rem !important; | ||
| 312 | + } | ||
| 313 | + .buttons-box-right { | ||
| 314 | + position: absolute; | ||
| 315 | + right: 0; | ||
| 316 | + } | ||
| 317 | +</style> |
web_src/src/components/live.vue
0 → 100644
| 1 | +<template> | ||
| 2 | + <div id="devicePosition" style="height: 100%"> | ||
| 3 | + <el-container style="height: 100%"> | ||
| 4 | + <el-header> | ||
| 5 | + <uiHeader></uiHeader> | ||
| 6 | + </el-header> | ||
| 7 | + <el-container v-loading="loading" element-loading-text="拼命加载中"> | ||
| 8 | + <el-aside width="300px" style="background-color: #ffffff"> | ||
| 9 | + <div style="text-align: center;padding-top: 20px;">设备列表</div> | ||
| 10 | + <el-menu v-loading="loading"> | ||
| 11 | + <el-submenu v-for="device in deviceList" :key="device.deviceId" :index="device.deviceId" @click="sendDevicePush(item)"> | ||
| 12 | + <template slot="title" > | ||
| 13 | + <i class="el-icon-location-outline"></i> | ||
| 14 | + {{device.name}} | ||
| 15 | + </template> | ||
| 16 | + <ChannelTree :device="device" @sendDevicePush="sendDevicePush"></ChannelTree> | ||
| 17 | + </el-submenu> | ||
| 18 | + </el-menu> | ||
| 19 | + </el-aside> | ||
| 20 | + <el-container> | ||
| 21 | + <!-- <LivePlay></LivePlay> --> | ||
| 22 | + <el-header height="40px" style="text-align: left;font-size: 17px;line-height: 40px;"> | ||
| 23 | + 分屏: | ||
| 24 | + <i class="el-icon-full-screen btn" :class="{active:spilt==1}" @click="spilt=1"/> | ||
| 25 | + <i class="el-icon-menu btn" :class="{active:spilt==4}" @click="spilt=4"/> | ||
| 26 | + <i class="el-icon-s-grid btn" :class="{active:spilt==9}" @click="spilt=9"/> | ||
| 27 | + </el-header> | ||
| 28 | + <el-main> | ||
| 29 | + <div style="width: 100%;height: calc( 100vh - 110px );display: flex;flex-wrap: wrap;background-color: #000;"> | ||
| 30 | + <div v-for="i in spilt" :key="i" class="play-box" | ||
| 31 | + :style="liveStyle" :class="{redborder:playerIdx == (i-1)}" | ||
| 32 | + @click="playerIdx = (i-1)" | ||
| 33 | + > | ||
| 34 | + <div v-if="!videoUrl[i-1]" style="color: #ffffff;font-size: 30px;font-weight: bold;">{{i}}</div> | ||
| 35 | + <player v-else :ref="'player'+i" :videoUrl="videoUrl[i-1]" fluent autoplay :height="true" | ||
| 36 | + :idx="'player'+i" @screenshot="shot" @destroy="destroy"></player> | ||
| 37 | + <!-- <player v-else ref="'player'+i" :idx="'player'+i" :visible.sync="showVideoDialog" :videoUrl="videoUrl[i-1]" :height="true" :hasAudio="hasAudio" fluent autoplay live ></player> --> | ||
| 38 | + </div> | ||
| 39 | + </div> | ||
| 40 | + </el-main> | ||
| 41 | + </el-container> | ||
| 42 | + </el-container> | ||
| 43 | + </el-container> | ||
| 44 | + </div> | ||
| 45 | +</template> | ||
| 46 | + | ||
| 47 | +<script> | ||
| 48 | + import uiHeader from "./UiHeader.vue"; | ||
| 49 | + import player from './jessibuca.vue' | ||
| 50 | + import ChannelTree from './channelTree.vue' | ||
| 51 | + | ||
| 52 | + export default { | ||
| 53 | + name: "live", | ||
| 54 | + components: { | ||
| 55 | + uiHeader, player, ChannelTree | ||
| 56 | + }, | ||
| 57 | + data() { | ||
| 58 | + return { | ||
| 59 | + showVideoDialog: true, | ||
| 60 | + hasAudio: false, | ||
| 61 | + videoUrl:[''], | ||
| 62 | + spilt:1,//分屏 | ||
| 63 | + playerIdx:0,//激活播放器 | ||
| 64 | + | ||
| 65 | + deviceList: [], //设备列表 | ||
| 66 | + currentDevice: {}, //当前操作设备对象 | ||
| 67 | + | ||
| 68 | + videoComponentList: [], | ||
| 69 | + updateLooper: 0, //数据刷新轮训标志 | ||
| 70 | + currentDeviceChannelsLenth:0, | ||
| 71 | + winHeight: window.innerHeight - 200, | ||
| 72 | + currentPage:1, | ||
| 73 | + count:15, | ||
| 74 | + total:0, | ||
| 75 | + getDeviceListLoading: false, | ||
| 76 | + | ||
| 77 | + //channel | ||
| 78 | + searchSrt: "", | ||
| 79 | + channelType: "", | ||
| 80 | + online: "", | ||
| 81 | + channelTotal:0, | ||
| 82 | + deviceChannelList:[], | ||
| 83 | + loading:false | ||
| 84 | + }; | ||
| 85 | + }, | ||
| 86 | + mounted() { | ||
| 87 | + this.initData(); | ||
| 88 | + | ||
| 89 | + }, | ||
| 90 | + created(){ | ||
| 91 | + this.checkPlayByParam() | ||
| 92 | + }, | ||
| 93 | + | ||
| 94 | + computed:{ | ||
| 95 | + liveStyle(){ | ||
| 96 | + if(this.spilt==1){ | ||
| 97 | + return {width:'100%',height:'100%'} | ||
| 98 | + }else if(this.spilt==4){ | ||
| 99 | + return {width:'49%',height:'49%'} | ||
| 100 | + }else if(this.spilt==9){ | ||
| 101 | + return {width:'32%',height:'32%'} | ||
| 102 | + } | ||
| 103 | + } | ||
| 104 | + }, | ||
| 105 | + watch:{ | ||
| 106 | + spilt(newValue){ | ||
| 107 | + console.log("切换画幅;"+newValue) | ||
| 108 | + let that = this | ||
| 109 | + for (let i = 1; i <= newValue; i++) { | ||
| 110 | + if(!that.$refs['player'+i]){ | ||
| 111 | + continue | ||
| 112 | + } | ||
| 113 | + this.$nextTick(()=>{ | ||
| 114 | + if(that.$refs['player'+i] instanceof Array){ | ||
| 115 | + that.$refs['player'+i][0].resize() | ||
| 116 | + }else { | ||
| 117 | + that.$refs['player'+i].resize() | ||
| 118 | + } | ||
| 119 | + }) | ||
| 120 | + | ||
| 121 | + } | ||
| 122 | + window.localStorage.setItem('split',newValue) | ||
| 123 | + }, | ||
| 124 | + '$route.fullPath':'checkPlayByParam' | ||
| 125 | + }, | ||
| 126 | + destroyed() { | ||
| 127 | + clearTimeout(this.updateLooper); | ||
| 128 | + }, | ||
| 129 | + methods: { | ||
| 130 | + initData: function () { | ||
| 131 | + this.getDeviceList(); | ||
| 132 | + | ||
| 133 | + }, | ||
| 134 | + destroy(idx) { | ||
| 135 | + console.log(idx); | ||
| 136 | + this.clear(idx.substring(idx.length-1)) | ||
| 137 | + }, | ||
| 138 | + getDeviceList: function() { | ||
| 139 | + let that = this; | ||
| 140 | + this.$axios({ | ||
| 141 | + method: 'get', | ||
| 142 | + url:`/api/device/query/devices`, | ||
| 143 | + params: { | ||
| 144 | + page: that.currentPage, | ||
| 145 | + count: that.count | ||
| 146 | + } | ||
| 147 | + }).then(function (res) { | ||
| 148 | + console.log(res.data.list); | ||
| 149 | + that.total = res.data.total; | ||
| 150 | + | ||
| 151 | + that.deviceList = res.data.list.map(item=>{return {deviceChannelList:[],...item}}); | ||
| 152 | + that.getDeviceListLoading = false; | ||
| 153 | + }).catch(function (error) { | ||
| 154 | + console.log(error); | ||
| 155 | + that.getDeviceListLoading = false; | ||
| 156 | + }); | ||
| 157 | + }, | ||
| 158 | + //通知设备上传媒体流 | ||
| 159 | + sendDevicePush: function (itemData) { | ||
| 160 | + if(itemData.status===0){ | ||
| 161 | + this.$message.error('设备离线!'); | ||
| 162 | + return | ||
| 163 | + } | ||
| 164 | + this.save(itemData) | ||
| 165 | + let deviceId = itemData.deviceId; | ||
| 166 | + // this.isLoging = true; | ||
| 167 | + let channelId = itemData.channelId; | ||
| 168 | + console.log("通知设备推流1:" + deviceId + " : " + channelId ); | ||
| 169 | + let idxTmp = this.playerIdx | ||
| 170 | + let that = this; | ||
| 171 | + this.loading = true | ||
| 172 | + this.$axios({ | ||
| 173 | + method: 'get', | ||
| 174 | + url: '/api/play/start/' + deviceId + '/' + channelId | ||
| 175 | + }).then(function (res) { | ||
| 176 | + // that.isLoging = false; | ||
| 177 | + console.log('=====----=====') | ||
| 178 | + console.log(res) | ||
| 179 | + if (res.data.code == 0 && res.data.data) { | ||
| 180 | + itemData.playUrl = res.data.data.httpsFlv | ||
| 181 | + that.setPlayUrl(res.data.data.ws_flv,idxTmp) | ||
| 182 | + }else { | ||
| 183 | + that.$message.error(res.data.msg); | ||
| 184 | + } | ||
| 185 | + }).catch(function (e) { | ||
| 186 | + }).finally(()=>{ | ||
| 187 | + that.loading = false | ||
| 188 | + }); | ||
| 189 | + }, | ||
| 190 | + setPlayUrl(url,idx){ | ||
| 191 | + this.$set(this.videoUrl,idx,url) | ||
| 192 | + let _this = this | ||
| 193 | + setTimeout(()=>{ | ||
| 194 | + window.localStorage.setItem('videoUrl',JSON.stringify(_this.videoUrl)) | ||
| 195 | + },100) | ||
| 196 | + | ||
| 197 | + }, | ||
| 198 | + checkPlayByParam(){ | ||
| 199 | + let {deviceId,channelId} = this.$route.query | ||
| 200 | + if(deviceId && channelId){ | ||
| 201 | + this.sendDevicePush({deviceId,channelId}) | ||
| 202 | + } | ||
| 203 | + }, | ||
| 204 | + convertImageToCanvas(image) { | ||
| 205 | + var canvas = document.createElement("canvas"); | ||
| 206 | + canvas.width = image.width; | ||
| 207 | + canvas.height = image.height; | ||
| 208 | + canvas.getContext("2d").drawImage(image, 0, 0); | ||
| 209 | + return canvas; | ||
| 210 | + }, | ||
| 211 | + shot(e){ | ||
| 212 | + // console.log(e) | ||
| 213 | + // send({code:'image',data:e}) | ||
| 214 | + var base64ToBlob = function(code) { | ||
| 215 | + let parts = code.split(';base64,'); | ||
| 216 | + let contentType = parts[0].split(':')[1]; | ||
| 217 | + let raw = window.atob(parts[1]); | ||
| 218 | + let rawLength = raw.length; | ||
| 219 | + let uInt8Array = new Uint8Array(rawLength); | ||
| 220 | + for(let i = 0; i < rawLength; ++i) { | ||
| 221 | + uInt8Array[i] = raw.charCodeAt(i); | ||
| 222 | + } | ||
| 223 | + return new Blob([uInt8Array], { | ||
| 224 | + type: contentType | ||
| 225 | + }); | ||
| 226 | + }; | ||
| 227 | + let aLink = document.createElement('a'); | ||
| 228 | + let blob = base64ToBlob(e); //new Blob([content]); | ||
| 229 | + let evt = document.createEvent("HTMLEvents"); | ||
| 230 | + evt.initEvent("click", true, true); //initEvent 不加后两个参数在FF下会报错 事件类型,是否冒泡,是否阻止浏览器的默认行为 | ||
| 231 | + aLink.download = '截图'; | ||
| 232 | + aLink.href = URL.createObjectURL(blob); | ||
| 233 | + aLink.click(); | ||
| 234 | + }, | ||
| 235 | + save(item){ | ||
| 236 | + let dataStr = window.localStorage.getItem('playData') || '[]' | ||
| 237 | + let data = JSON.parse(dataStr); | ||
| 238 | + data[this.playerIdx] = item | ||
| 239 | + window.localStorage.setItem('playData',JSON.stringify(data)) | ||
| 240 | + }, | ||
| 241 | + clear(idx) { | ||
| 242 | + let dataStr = window.localStorage.getItem('playData') || '[]' | ||
| 243 | + let data = JSON.parse(dataStr); | ||
| 244 | + data[idx-1] = null; | ||
| 245 | + console.log(data); | ||
| 246 | + window.localStorage.setItem('playData',JSON.stringify(data)) | ||
| 247 | + }, | ||
| 248 | + loadAndPlay(){ | ||
| 249 | + let dataStr = window.localStorage.getItem('playData') || '[]' | ||
| 250 | + let data = JSON.parse(dataStr); | ||
| 251 | + | ||
| 252 | + data.forEach((item,i)=>{ | ||
| 253 | + if(item){ | ||
| 254 | + this.playerIdx = i | ||
| 255 | + this.sendDevicePush(item) | ||
| 256 | + } | ||
| 257 | + }) | ||
| 258 | + } | ||
| 259 | + } | ||
| 260 | + }; | ||
| 261 | +</script> | ||
| 262 | +<style> | ||
| 263 | + .btn{ | ||
| 264 | + margin: 0 10px; | ||
| 265 | + | ||
| 266 | + } | ||
| 267 | + .btn:hover{ | ||
| 268 | + color: #409EFF; | ||
| 269 | + } | ||
| 270 | + .btn.active{ | ||
| 271 | + color: #409EFF; | ||
| 272 | + | ||
| 273 | + } | ||
| 274 | + .redborder{ | ||
| 275 | + border: 2px solid red !important; | ||
| 276 | + } | ||
| 277 | + .play-box{ | ||
| 278 | + background-color: #000000; | ||
| 279 | + border: 2px solid #505050; | ||
| 280 | + display: flex; | ||
| 281 | + align-items: center; | ||
| 282 | + justify-content: center; | ||
| 283 | + } | ||
| 284 | +</style> | ||
| 285 | +<style> | ||
| 286 | + .videoList { | ||
| 287 | + display: flex; | ||
| 288 | + flex-wrap: wrap; | ||
| 289 | + align-content: flex-start; | ||
| 290 | + } | ||
| 291 | + | ||
| 292 | + .video-item { | ||
| 293 | + position: relative; | ||
| 294 | + width: 15rem; | ||
| 295 | + height: 10rem; | ||
| 296 | + margin-right: 1rem; | ||
| 297 | + background-color: #000000; | ||
| 298 | + } | ||
| 299 | + | ||
| 300 | + .video-item-img { | ||
| 301 | + position: absolute; | ||
| 302 | + top: 0; | ||
| 303 | + bottom: 0; | ||
| 304 | + left: 0; | ||
| 305 | + right: 0; | ||
| 306 | + margin: auto; | ||
| 307 | + width: 100%; | ||
| 308 | + height: 100%; | ||
| 309 | + } | ||
| 310 | + | ||
| 311 | + .video-item-img:after { | ||
| 312 | + content: ""; | ||
| 313 | + display: inline-block; | ||
| 314 | + position: absolute; | ||
| 315 | + z-index: 2; | ||
| 316 | + top: 0; | ||
| 317 | + bottom: 0; | ||
| 318 | + left: 0; | ||
| 319 | + right: 0; | ||
| 320 | + margin: auto; | ||
| 321 | + width: 3rem; | ||
| 322 | + height: 3rem; | ||
| 323 | + background-image: url("../assets/loading.png"); | ||
| 324 | + background-size: cover; | ||
| 325 | + background-color: #000000; | ||
| 326 | + } | ||
| 327 | + | ||
| 328 | + .video-item-title { | ||
| 329 | + position: absolute; | ||
| 330 | + bottom: 0; | ||
| 331 | + color: #000000; | ||
| 332 | + background-color: #ffffff; | ||
| 333 | + line-height: 1.5rem; | ||
| 334 | + padding: 0.3rem; | ||
| 335 | + width: 14.4rem; | ||
| 336 | + } | ||
| 337 | + | ||
| 338 | + .baidumap { | ||
| 339 | + width: 100%; | ||
| 340 | + height: 100%; | ||
| 341 | + border: none; | ||
| 342 | + position: absolute; | ||
| 343 | + left: 0; | ||
| 344 | + top: 0; | ||
| 345 | + right: 0; | ||
| 346 | + bottom: 0; | ||
| 347 | + margin: auto; | ||
| 348 | + } | ||
| 349 | + | ||
| 350 | + /* 去除百度地图版权那行字 和 百度logo */ | ||
| 351 | + .baidumap > .BMap_cpyCtrl { | ||
| 352 | + display: none !important; | ||
| 353 | + } | ||
| 354 | + .baidumap > .anchorBL { | ||
| 355 | + display: none !important; | ||
| 356 | + } | ||
| 357 | +</style> |
web_src/src/router/index.js
| @@ -15,6 +15,7 @@ import test from '../components/test.vue' | @@ -15,6 +15,7 @@ import test from '../components/test.vue' | ||
| 15 | import web from '../components/setting/Web.vue' | 15 | import web from '../components/setting/Web.vue' |
| 16 | import sip from '../components/setting/Sip.vue' | 16 | import sip from '../components/setting/Sip.vue' |
| 17 | import media from '../components/setting/Media.vue' | 17 | import media from '../components/setting/Media.vue' |
| 18 | +import live from '../components/live.vue' | ||
| 18 | 19 | ||
| 19 | import wasmPlayer from '../components/dialog/jessibuca.vue' | 20 | import wasmPlayer from '../components/dialog/jessibuca.vue' |
| 20 | import rtcPlayer from '../components/dialog/rtcPlayer.vue' | 21 | import rtcPlayer from '../components/dialog/rtcPlayer.vue' |
| @@ -35,6 +36,10 @@ export default new VueRouter({ | @@ -35,6 +36,10 @@ export default new VueRouter({ | ||
| 35 | component: control, | 36 | component: control, |
| 36 | }, | 37 | }, |
| 37 | { | 38 | { |
| 39 | + path: '/live', | ||
| 40 | + component: live, | ||
| 41 | + }, | ||
| 42 | + { | ||
| 38 | path: '/deviceList', | 43 | path: '/deviceList', |
| 39 | component: deviceList, | 44 | component: deviceList, |
| 40 | }, | 45 | }, |