新增实时监控功能
左边是设备通道树,右边是分屏预览
This commit is contained in:
parent
8cbbf1470b
commit
3789e8616f
11
pom.xml
11
pom.xml
@ -277,5 +277,16 @@
|
||||
</plugin>
|
||||
|
||||
</plugins>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
</resource>
|
||||
<resource>
|
||||
<directory>src/main/java</directory>
|
||||
<includes>
|
||||
<include>**/*.xml</include>
|
||||
</includes>
|
||||
</resource>
|
||||
</resources>
|
||||
</build>
|
||||
</project>
|
||||
|
@ -5,6 +5,7 @@ import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
|
||||
import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem;
|
||||
import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem;
|
||||
import com.genersoft.iot.vmp.service.bean.GPSMsgInfo;
|
||||
import com.genersoft.iot.vmp.vmanager.bean.DeviceChannelTree;
|
||||
import com.genersoft.iot.vmp.vmanager.gb28181.platform.bean.ChannelReduce;
|
||||
import com.github.pagehelper.PageInfo;
|
||||
|
||||
@ -93,6 +94,13 @@ public interface IVideoManagerStorager {
|
||||
|
||||
public List<DeviceChannel> queryChannelsByDeviceIdWithStartAndLimit(String deviceId, String query, Boolean hasSubChannel, Boolean online, int start, int limit);
|
||||
|
||||
/**
|
||||
* 获取某个设备的通道树
|
||||
* @param deviceId 设备ID
|
||||
* @return
|
||||
*/
|
||||
List<DeviceChannelTree> tree(String deviceId);
|
||||
|
||||
/**
|
||||
* 获取某个设备的通道列表
|
||||
*
|
||||
|
@ -1,6 +1,7 @@
|
||||
package com.genersoft.iot.vmp.storager.dao;
|
||||
|
||||
import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
|
||||
import com.genersoft.iot.vmp.vmanager.bean.DeviceChannelTree;
|
||||
import com.genersoft.iot.vmp.vmanager.gb28181.platform.bean.ChannelReduce;
|
||||
import org.apache.ibatis.annotations.*;
|
||||
import org.springframework.stereotype.Repository;
|
||||
@ -201,4 +202,6 @@ public interface DeviceChannelMapper {
|
||||
|
||||
@Select("SELECT * FROM device_channel WHERE deviceId=#{deviceId} AND status=1")
|
||||
List<DeviceChannel> queryOnlineChannelsByDeviceId(String deviceId);
|
||||
|
||||
List<DeviceChannelTree> tree(String deviceId);
|
||||
}
|
||||
|
@ -0,0 +1,37 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.genersoft.iot.vmp.storager.dao.DeviceChannelMapper">
|
||||
|
||||
<!-- 通用查询映射结果 -->
|
||||
<resultMap id="treeNodeResultMap" type="com.genersoft.iot.vmp.vmanager.bean.DeviceChannelTreeNode">
|
||||
<id column="id" property="id"/>
|
||||
<result column="parentId" property="parentId"/>
|
||||
<result column="status" property="status"/>
|
||||
<result column="title" property="title"/>
|
||||
<result column="value" property="value"/>
|
||||
<result column="key" property="key"/>
|
||||
<result column="deviceId" property="deviceId"/>
|
||||
<result column="channelId" property="channelId"/>
|
||||
<result column="longitude" property="lng"/>
|
||||
<result column="latitude" property="lat"/>
|
||||
</resultMap>
|
||||
|
||||
|
||||
<select id="tree" resultMap="treeNodeResultMap">
|
||||
SELECT
|
||||
channelId,
|
||||
channelId as id,
|
||||
deviceId,
|
||||
parentId,
|
||||
status,
|
||||
name as title,
|
||||
channelId as "value",
|
||||
channelId as "key",
|
||||
channelId,
|
||||
longitude,
|
||||
latitude
|
||||
from device_channel
|
||||
where deviceId = #{deviceId}
|
||||
</select>
|
||||
|
||||
</mapper>
|
@ -13,6 +13,8 @@ import com.genersoft.iot.vmp.service.bean.GPSMsgInfo;
|
||||
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
|
||||
import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
|
||||
import com.genersoft.iot.vmp.storager.dao.*;
|
||||
import com.genersoft.iot.vmp.utils.node.ForestNodeMerger;
|
||||
import com.genersoft.iot.vmp.vmanager.bean.DeviceChannelTree;
|
||||
import com.genersoft.iot.vmp.vmanager.gb28181.platform.bean.ChannelReduce;
|
||||
import com.github.pagehelper.PageHelper;
|
||||
import com.github.pagehelper.PageInfo;
|
||||
@ -328,6 +330,11 @@ public class VideoManagerStoragerImpl implements IVideoManagerStorager {
|
||||
return deviceChannelMapper.queryChannelsByDeviceIdWithStartAndLimit(deviceId, null, query, hasSubChannel, online, start, limit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DeviceChannelTree> tree(String deviceId) {
|
||||
return ForestNodeMerger.merge(deviceChannelMapper.tree(deviceId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DeviceChannel> queryChannelsByDeviceId(String deviceId) {
|
||||
return deviceChannelMapper.queryChannels(deviceId, null,null, null, null);
|
||||
|
@ -0,0 +1,12 @@
|
||||
package com.genersoft.iot.vmp.utils;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public class CollectionUtil {
|
||||
|
||||
public static <T> boolean contains(T[] array, final T element) {
|
||||
return array != null && Arrays.stream(array).anyMatch((x) -> {
|
||||
return ObjectUtils.nullSafeEquals(x, element);
|
||||
});
|
||||
}
|
||||
}
|
41
src/main/java/com/genersoft/iot/vmp/utils/ObjectUtils.java
Normal file
41
src/main/java/com/genersoft/iot/vmp/utils/ObjectUtils.java
Normal file
@ -0,0 +1,41 @@
|
||||
package com.genersoft.iot.vmp.utils;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public class ObjectUtils {
|
||||
public static boolean nullSafeEquals(Object o1, Object o2) {
|
||||
if (o1 == o2) {
|
||||
return true;
|
||||
} else if (o1 != null && o2 != null) {
|
||||
if (o1.equals(o2)) {
|
||||
return true;
|
||||
} else {
|
||||
return o1.getClass().isArray() && o2.getClass().isArray() && arrayEquals(o1, o2);
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean arrayEquals(Object o1, Object o2) {
|
||||
if (o1 instanceof Object[] && o2 instanceof Object[]) {
|
||||
return Arrays.equals((Object[])((Object[])o1), (Object[])((Object[])o2));
|
||||
} else if (o1 instanceof boolean[] && o2 instanceof boolean[]) {
|
||||
return Arrays.equals((boolean[])((boolean[])o1), (boolean[])((boolean[])o2));
|
||||
} else if (o1 instanceof byte[] && o2 instanceof byte[]) {
|
||||
return Arrays.equals((byte[])((byte[])o1), (byte[])((byte[])o2));
|
||||
} else if (o1 instanceof char[] && o2 instanceof char[]) {
|
||||
return Arrays.equals((char[])((char[])o1), (char[])((char[])o2));
|
||||
} else if (o1 instanceof double[] && o2 instanceof double[]) {
|
||||
return Arrays.equals((double[])((double[])o1), (double[])((double[])o2));
|
||||
} else if (o1 instanceof float[] && o2 instanceof float[]) {
|
||||
return Arrays.equals((float[])((float[])o1), (float[])((float[])o2));
|
||||
} else if (o1 instanceof int[] && o2 instanceof int[]) {
|
||||
return Arrays.equals((int[])((int[])o1), (int[])((int[])o2));
|
||||
} else if (o1 instanceof long[] && o2 instanceof long[]) {
|
||||
return Arrays.equals((long[])((long[])o1), (long[])((long[])o2));
|
||||
} else {
|
||||
return o1 instanceof short[] && o2 instanceof short[] && Arrays.equals((short[]) ((short[]) o1), (short[]) ((short[]) o2));
|
||||
}
|
||||
}
|
||||
}
|
54
src/main/java/com/genersoft/iot/vmp/utils/node/BaseNode.java
Normal file
54
src/main/java/com/genersoft/iot/vmp/utils/node/BaseNode.java
Normal file
@ -0,0 +1,54 @@
|
||||
package com.genersoft.iot.vmp.utils.node;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 节点基类
|
||||
*
|
||||
*/
|
||||
@Data
|
||||
public class BaseNode<T> implements INode<T> {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
protected String id;
|
||||
|
||||
/**
|
||||
* 父节点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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
package com.genersoft.iot.vmp.utils.node;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
|
||||
/**
|
||||
* 森林节点类
|
||||
*
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
public class ForestNode extends BaseNode<ForestNode> {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 节点内容
|
||||
*/
|
||||
private Object content;
|
||||
|
||||
public ForestNode(String id, String parentId, Object content) {
|
||||
this.id = id;
|
||||
this.parentId = parentId;
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,68 @@
|
||||
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::getId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据节点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.getId())) {
|
||||
roots.add(node);
|
||||
}
|
||||
});
|
||||
return roots;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
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.getId());
|
||||
}
|
||||
}
|
||||
});
|
||||
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.getId())){
|
||||
forestNodeManager.addParentId(forestNode.getId());
|
||||
} else {
|
||||
if (node != null){
|
||||
node.getChildren().add(forestNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
return forestNodeManager.getRoot();
|
||||
}
|
||||
}
|
42
src/main/java/com/genersoft/iot/vmp/utils/node/INode.java
Normal file
42
src/main/java/com/genersoft/iot/vmp/utils/node/INode.java
Normal file
@ -0,0 +1,42 @@
|
||||
package com.genersoft.iot.vmp.utils.node;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
*
|
||||
* 节点
|
||||
*/
|
||||
public interface INode<T> extends Serializable {
|
||||
|
||||
/**
|
||||
* 主键
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
String getId();
|
||||
|
||||
/**
|
||||
* 父主键
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
String getParentId();
|
||||
|
||||
/**
|
||||
* 子孙节点
|
||||
*
|
||||
* @return List<T>
|
||||
*/
|
||||
List<T> getChildren();
|
||||
|
||||
/**
|
||||
* 是否有子孙节点
|
||||
*
|
||||
* @return Boolean
|
||||
*/
|
||||
default Boolean getHasChildren() {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
21
src/main/java/com/genersoft/iot/vmp/utils/node/TreeNode.java
Normal file
21
src/main/java/com/genersoft/iot/vmp/utils/node/TreeNode.java
Normal file
@ -0,0 +1,21 @@
|
||||
package com.genersoft.iot.vmp.utils.node;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 树型节点类
|
||||
*
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
public class TreeNode extends BaseNode<TreeNode> {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private String title;
|
||||
|
||||
private String key;
|
||||
|
||||
private String value;
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
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 lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ApiModel(value = "DeviceChannelTree对象", description = "DeviceChannelTree对象")
|
||||
public class DeviceChannelTree extends DeviceChannel implements INode<DeviceChannelTree> {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* 父节点ID
|
||||
*/
|
||||
private String parentId;
|
||||
|
||||
private String parentName;
|
||||
|
||||
/**
|
||||
* 子孙节点
|
||||
*/
|
||||
@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;
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package com.genersoft.iot.vmp.vmanager.bean;
|
||||
|
||||
import com.genersoft.iot.vmp.utils.node.TreeNode;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class DeviceChannelTreeNode extends TreeNode {
|
||||
|
||||
private Integer status;
|
||||
|
||||
private String deviceId;
|
||||
|
||||
private String channelId;
|
||||
|
||||
private Double lng;
|
||||
|
||||
private Double lat;
|
||||
}
|
@ -1,32 +1,35 @@
|
||||
package com.genersoft.iot.vmp.vmanager.bean;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class WVPResult<T> {
|
||||
|
||||
private int code;
|
||||
private String msg;
|
||||
private T data;
|
||||
|
||||
public int getCode() {
|
||||
return code;
|
||||
private static final Integer SUCCESS = 200;
|
||||
private static final Integer FAILED = 400;
|
||||
|
||||
public static <T> WVPResult<T> Data(T t, String msg) {
|
||||
return new WVPResult<>(SUCCESS, msg, t);
|
||||
}
|
||||
|
||||
public void setCode(int code) {
|
||||
this.code = code;
|
||||
public static <T> WVPResult<T> Data(T t) {
|
||||
return Data(t, "成功");
|
||||
}
|
||||
|
||||
public String getMsg() {
|
||||
return msg;
|
||||
public static <T> WVPResult<T> fail(int code, String msg) {
|
||||
return new WVPResult<>(code, msg, null);
|
||||
}
|
||||
|
||||
public void setMsg(String msg) {
|
||||
this.msg = msg;
|
||||
public static <T> WVPResult<T> fail(String msg) {
|
||||
return fail(FAILED, msg);
|
||||
}
|
||||
|
||||
public T getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
public void setData(T data) {
|
||||
this.data = data;
|
||||
}
|
||||
}
|
||||
|
@ -10,8 +10,10 @@ import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
|
||||
import com.genersoft.iot.vmp.service.IDeviceService;
|
||||
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
|
||||
import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
|
||||
import com.genersoft.iot.vmp.vmanager.bean.DeviceChannelTree;
|
||||
import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
|
||||
import com.github.pagehelper.PageInfo;
|
||||
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiImplicitParam;
|
||||
import io.swagger.annotations.ApiImplicitParams;
|
||||
@ -25,6 +27,7 @@ import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.context.request.async.DeferredResult;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@Api(tags = "国标设备查询", value = "国标设备查询")
|
||||
@ -431,5 +434,9 @@ public class DeviceQuery {
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@GetMapping("/{deviceId}/tree")
|
||||
@ApiOperation(value = "通道树形结构", notes = "通道树形结构")
|
||||
public WVPResult<List<DeviceChannelTree>> tree(@PathVariable String deviceId) {
|
||||
return WVPResult.Data(storager.tree(deviceId));
|
||||
}
|
||||
}
|
||||
|
19
web_src/src/api/deviceApi.js
Normal file
19
web_src/src/api/deviceApi.js
Normal file
@ -0,0 +1,19 @@
|
||||
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
|
||||
}
|
||||
})
|
||||
}
|
@ -2,6 +2,7 @@
|
||||
<div id="UiHeader">
|
||||
<el-menu router :default-active="activeIndex" menu-trigger="click" background-color="#545c64" text-color="#fff" active-text-color="#ffd04b" mode="horizontal">
|
||||
<el-menu-item index="/">控制台</el-menu-item>
|
||||
<el-menu-item index="/live">实时监控</el-menu-item>
|
||||
<el-menu-item index="/deviceList">设备列表</el-menu-item>
|
||||
<el-menu-item index="/pushVideoList">推流列表</el-menu-item>
|
||||
<el-menu-item index="/streamProxyList">拉流代理</el-menu-item>
|
||||
|
70
web_src/src/components/channelTree.vue
Normal file
70
web_src/src/components/channelTree.vue
Normal file
@ -0,0 +1,70 @@
|
||||
<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>
|
74
web_src/src/components/channelTreeItem.vue
Normal file
74
web_src/src/components/channelTreeItem.vue
Normal file
@ -0,0 +1,74 @@
|
||||
<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>
|
317
web_src/src/components/jessibuca.vue
Normal file
317
web_src/src/components/jessibuca.vue
Normal file
@ -0,0 +1,317 @@
|
||||
<template>
|
||||
<div :id="'jessibuca'+idx" style="width: 100%; height: 100%">
|
||||
<div :id="'container'+idx" ref="container" style="width: 100%; height: 100%; background-color: #000" @dblclick="fullscreenSwich">
|
||||
<div class="buttons-box" :id="'buttonsBox'+idx">
|
||||
<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="destroyButton"></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="screenshot" 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>
|
||||
</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','idx'],
|
||||
mounted () {
|
||||
window.onerror = (msg) => {
|
||||
// console.error(msg)
|
||||
};
|
||||
let paramUrl = decodeURIComponent(this.$route.params.url)
|
||||
this.$nextTick(() =>{
|
||||
let dom = document.getElementById("container"+this.idx);
|
||||
// dom.style.height = (9/16 ) * dom.clientWidth + "px"
|
||||
if (typeof (this.videoUrl) == "undefined") {
|
||||
this.videoUrl = paramUrl;
|
||||
}
|
||||
this.btnDom = document.getElementById("buttonsBox"+this.idx);
|
||||
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.container)
|
||||
console.log("hasAudio " + !!this.hasAudio)
|
||||
|
||||
this.jessibuca = new window.Jessibuca(Object.assign(
|
||||
{
|
||||
container: this.$refs.container,
|
||||
videoBuffer: 0.2, // 最大缓冲时长,单位秒
|
||||
isResize: true,
|
||||
decoder: "./static/js/jessibuca/index.js",
|
||||
// text: "WVP-PRO",
|
||||
// background: "bg.jpg",
|
||||
loadingText: "加载中",
|
||||
hasAudio: !!this.hasAudio,
|
||||
debug: false,
|
||||
timeout:5,
|
||||
supportDblclickFullscreen: false, // 是否支持屏幕的双击事件,触发全屏,取消全屏事件。
|
||||
operateBtns: {
|
||||
fullscreen: false,
|
||||
screenshot: false,
|
||||
play: false,
|
||||
audio: 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", (error) =>{
|
||||
console.log("error", error);
|
||||
this.pause()
|
||||
});
|
||||
|
||||
this.jessibuca.on("timeout", ()=> {
|
||||
console.log("timeout");
|
||||
// this.pause()
|
||||
this.play(this.videoUrl)
|
||||
});
|
||||
|
||||
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();
|
||||
}
|
||||
if(!url){
|
||||
return
|
||||
}
|
||||
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"+this.idx) == null) {
|
||||
document.getElementById("container"+this.idx).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;
|
||||
},
|
||||
resize(){
|
||||
this.jessibuca.resize()
|
||||
},
|
||||
screenshot(){
|
||||
this.jessibuca.screenshot('截图','png',0.5)
|
||||
// let base64 = this.jessibuca.screenshot("shot","jpeg",0.5,'base64')
|
||||
// this.$emit('screenshot',base64)
|
||||
},
|
||||
destroyButton() {
|
||||
this.$emit('destroy', this.idx)
|
||||
this.destroy()
|
||||
}
|
||||
},
|
||||
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>
|
357
web_src/src/components/live.vue
Normal file
357
web_src/src/components/live.vue
Normal file
@ -0,0 +1,357 @@
|
||||
<template>
|
||||
<div id="devicePosition" style="height: 100%">
|
||||
<el-container style="height: 100%">
|
||||
<el-header>
|
||||
<uiHeader></uiHeader>
|
||||
</el-header>
|
||||
<el-container v-loading="loading" element-loading-text="拼命加载中">
|
||||
<el-aside width="300px" style="background-color: #ffffff">
|
||||
<div style="text-align: center;padding-top: 20px;">设备列表</div>
|
||||
<el-menu v-loading="loading">
|
||||
<el-submenu v-for="device in deviceList" :key="device.deviceId" :index="device.deviceId" @click="sendDevicePush(item)">
|
||||
<template slot="title" >
|
||||
<i class="el-icon-location-outline"></i>
|
||||
{{device.name}}
|
||||
</template>
|
||||
<ChannelTree :device="device" @sendDevicePush="sendDevicePush"></ChannelTree>
|
||||
</el-submenu>
|
||||
</el-menu>
|
||||
</el-aside>
|
||||
<el-container>
|
||||
<!-- <LivePlay></LivePlay> -->
|
||||
<el-header height="40px" style="text-align: left;font-size: 17px;line-height: 40px;">
|
||||
分屏:
|
||||
<i class="el-icon-full-screen btn" :class="{active:spilt==1}" @click="spilt=1"/>
|
||||
<i class="el-icon-menu btn" :class="{active:spilt==4}" @click="spilt=4"/>
|
||||
<i class="el-icon-s-grid btn" :class="{active:spilt==9}" @click="spilt=9"/>
|
||||
</el-header>
|
||||
<el-main>
|
||||
<div style="width: 100%;height: calc( 100vh - 110px );display: flex;flex-wrap: wrap;background-color: #000;">
|
||||
<div v-for="i in spilt" :key="i" class="play-box"
|
||||
:style="liveStyle" :class="{redborder:playerIdx == (i-1)}"
|
||||
@click="playerIdx = (i-1)"
|
||||
>
|
||||
<div v-if="!videoUrl[i-1]" style="color: #ffffff;font-size: 30px;font-weight: bold;">{{i}}</div>
|
||||
<player v-else :ref="'player'+i" :videoUrl="videoUrl[i-1]" fluent autoplay :height="true"
|
||||
:idx="'player'+i" @screenshot="shot" @destroy="destroy"></player>
|
||||
<!-- <player v-else ref="'player'+i" :idx="'player'+i" :visible.sync="showVideoDialog" :videoUrl="videoUrl[i-1]" :height="true" :hasAudio="hasAudio" fluent autoplay live ></player> -->
|
||||
</div>
|
||||
</div>
|
||||
</el-main>
|
||||
</el-container>
|
||||
</el-container>
|
||||
</el-container>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import uiHeader from "./UiHeader.vue";
|
||||
import player from './jessibuca.vue'
|
||||
import ChannelTree from './channelTree.vue'
|
||||
|
||||
export default {
|
||||
name: "live",
|
||||
components: {
|
||||
uiHeader, player, ChannelTree
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showVideoDialog: true,
|
||||
hasAudio: false,
|
||||
videoUrl:[''],
|
||||
spilt:1,//分屏
|
||||
playerIdx:0,//激活播放器
|
||||
|
||||
deviceList: [], //设备列表
|
||||
currentDevice: {}, //当前操作设备对象
|
||||
|
||||
videoComponentList: [],
|
||||
updateLooper: 0, //数据刷新轮训标志
|
||||
currentDeviceChannelsLenth:0,
|
||||
winHeight: window.innerHeight - 200,
|
||||
currentPage:1,
|
||||
count:15,
|
||||
total:0,
|
||||
getDeviceListLoading: false,
|
||||
|
||||
//channel
|
||||
searchSrt: "",
|
||||
channelType: "",
|
||||
online: "",
|
||||
channelTotal:0,
|
||||
deviceChannelList:[],
|
||||
loading:false
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.initData();
|
||||
|
||||
},
|
||||
created(){
|
||||
this.checkPlayByParam()
|
||||
},
|
||||
|
||||
computed:{
|
||||
liveStyle(){
|
||||
if(this.spilt==1){
|
||||
return {width:'100%',height:'100%'}
|
||||
}else if(this.spilt==4){
|
||||
return {width:'49%',height:'49%'}
|
||||
}else if(this.spilt==9){
|
||||
return {width:'32%',height:'32%'}
|
||||
}
|
||||
}
|
||||
},
|
||||
watch:{
|
||||
spilt(newValue){
|
||||
console.log("切换画幅;"+newValue)
|
||||
let that = this
|
||||
for (let i = 1; i <= newValue; i++) {
|
||||
if(!that.$refs['player'+i]){
|
||||
continue
|
||||
}
|
||||
this.$nextTick(()=>{
|
||||
if(that.$refs['player'+i] instanceof Array){
|
||||
that.$refs['player'+i][0].resize()
|
||||
}else {
|
||||
that.$refs['player'+i].resize()
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
window.localStorage.setItem('split',newValue)
|
||||
},
|
||||
'$route.fullPath':'checkPlayByParam'
|
||||
},
|
||||
destroyed() {
|
||||
clearTimeout(this.updateLooper);
|
||||
},
|
||||
methods: {
|
||||
initData: function () {
|
||||
this.getDeviceList();
|
||||
|
||||
},
|
||||
destroy(idx) {
|
||||
console.log(idx);
|
||||
this.clear(idx.substring(idx.length-1))
|
||||
},
|
||||
getDeviceList: function() {
|
||||
let that = this;
|
||||
this.$axios({
|
||||
method: 'get',
|
||||
url:`/api/device/query/devices`,
|
||||
params: {
|
||||
page: that.currentPage,
|
||||
count: that.count
|
||||
}
|
||||
}).then(function (res) {
|
||||
console.log(res.data.list);
|
||||
that.total = res.data.total;
|
||||
|
||||
that.deviceList = res.data.list.map(item=>{return {deviceChannelList:[],...item}});
|
||||
that.getDeviceListLoading = false;
|
||||
}).catch(function (error) {
|
||||
console.log(error);
|
||||
that.getDeviceListLoading = false;
|
||||
});
|
||||
},
|
||||
//通知设备上传媒体流
|
||||
sendDevicePush: function (itemData) {
|
||||
if(itemData.status===0){
|
||||
this.$message.error('设备离线!');
|
||||
return
|
||||
}
|
||||
this.save(itemData)
|
||||
let deviceId = itemData.deviceId;
|
||||
// this.isLoging = true;
|
||||
let channelId = itemData.channelId;
|
||||
console.log("通知设备推流1:" + deviceId + " : " + channelId );
|
||||
let idxTmp = this.playerIdx
|
||||
let that = this;
|
||||
this.loading = true
|
||||
this.$axios({
|
||||
method: 'get',
|
||||
url: '/api/play/start/' + deviceId + '/' + channelId
|
||||
}).then(function (res) {
|
||||
// that.isLoging = false;
|
||||
console.log('=====----=====')
|
||||
console.log(res)
|
||||
if (res.data.code == 0 && res.data.data) {
|
||||
itemData.playUrl = res.data.data.httpsFlv
|
||||
that.setPlayUrl(res.data.data.ws_flv,idxTmp)
|
||||
}else {
|
||||
that.$message.error(res.data.msg);
|
||||
}
|
||||
}).catch(function (e) {
|
||||
}).finally(()=>{
|
||||
that.loading = false
|
||||
});
|
||||
},
|
||||
setPlayUrl(url,idx){
|
||||
this.$set(this.videoUrl,idx,url)
|
||||
let _this = this
|
||||
setTimeout(()=>{
|
||||
window.localStorage.setItem('videoUrl',JSON.stringify(_this.videoUrl))
|
||||
},100)
|
||||
|
||||
},
|
||||
checkPlayByParam(){
|
||||
let {deviceId,channelId} = this.$route.query
|
||||
if(deviceId && channelId){
|
||||
this.sendDevicePush({deviceId,channelId})
|
||||
}
|
||||
},
|
||||
convertImageToCanvas(image) {
|
||||
var canvas = document.createElement("canvas");
|
||||
canvas.width = image.width;
|
||||
canvas.height = image.height;
|
||||
canvas.getContext("2d").drawImage(image, 0, 0);
|
||||
return canvas;
|
||||
},
|
||||
shot(e){
|
||||
// console.log(e)
|
||||
// send({code:'image',data:e})
|
||||
var base64ToBlob = function(code) {
|
||||
let parts = code.split(';base64,');
|
||||
let contentType = parts[0].split(':')[1];
|
||||
let raw = window.atob(parts[1]);
|
||||
let rawLength = raw.length;
|
||||
let uInt8Array = new Uint8Array(rawLength);
|
||||
for(let i = 0; i < rawLength; ++i) {
|
||||
uInt8Array[i] = raw.charCodeAt(i);
|
||||
}
|
||||
return new Blob([uInt8Array], {
|
||||
type: contentType
|
||||
});
|
||||
};
|
||||
let aLink = document.createElement('a');
|
||||
let blob = base64ToBlob(e); //new Blob([content]);
|
||||
let evt = document.createEvent("HTMLEvents");
|
||||
evt.initEvent("click", true, true); //initEvent 不加后两个参数在FF下会报错 事件类型,是否冒泡,是否阻止浏览器的默认行为
|
||||
aLink.download = '截图';
|
||||
aLink.href = URL.createObjectURL(blob);
|
||||
aLink.click();
|
||||
},
|
||||
save(item){
|
||||
let dataStr = window.localStorage.getItem('playData') || '[]'
|
||||
let data = JSON.parse(dataStr);
|
||||
data[this.playerIdx] = item
|
||||
window.localStorage.setItem('playData',JSON.stringify(data))
|
||||
},
|
||||
clear(idx) {
|
||||
let dataStr = window.localStorage.getItem('playData') || '[]'
|
||||
let data = JSON.parse(dataStr);
|
||||
data[idx-1] = null;
|
||||
console.log(data);
|
||||
window.localStorage.setItem('playData',JSON.stringify(data))
|
||||
},
|
||||
loadAndPlay(){
|
||||
let dataStr = window.localStorage.getItem('playData') || '[]'
|
||||
let data = JSON.parse(dataStr);
|
||||
|
||||
data.forEach((item,i)=>{
|
||||
if(item){
|
||||
this.playerIdx = i
|
||||
this.sendDevicePush(item)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style>
|
||||
.btn{
|
||||
margin: 0 10px;
|
||||
|
||||
}
|
||||
.btn:hover{
|
||||
color: #409EFF;
|
||||
}
|
||||
.btn.active{
|
||||
color: #409EFF;
|
||||
|
||||
}
|
||||
.redborder{
|
||||
border: 2px solid red !important;
|
||||
}
|
||||
.play-box{
|
||||
background-color: #000000;
|
||||
border: 2px solid #505050;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
</style>
|
||||
<style>
|
||||
.videoList {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-content: flex-start;
|
||||
}
|
||||
|
||||
.video-item {
|
||||
position: relative;
|
||||
width: 15rem;
|
||||
height: 10rem;
|
||||
margin-right: 1rem;
|
||||
background-color: #000000;
|
||||
}
|
||||
|
||||
.video-item-img {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
margin: auto;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.video-item-img:after {
|
||||
content: "";
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
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;
|
||||
}
|
||||
|
||||
.video-item-title {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
color: #000000;
|
||||
background-color: #ffffff;
|
||||
line-height: 1.5rem;
|
||||
padding: 0.3rem;
|
||||
width: 14.4rem;
|
||||
}
|
||||
|
||||
.baidumap {
|
||||
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>
|
@ -15,6 +15,7 @@ import test from '../components/test.vue'
|
||||
import web from '../components/setting/Web.vue'
|
||||
import sip from '../components/setting/Sip.vue'
|
||||
import media from '../components/setting/Media.vue'
|
||||
import live from '../components/live.vue'
|
||||
|
||||
import wasmPlayer from '../components/dialog/jessibuca.vue'
|
||||
import rtcPlayer from '../components/dialog/rtcPlayer.vue'
|
||||
@ -34,6 +35,10 @@ export default new VueRouter({
|
||||
path: '/',
|
||||
component: control,
|
||||
},
|
||||
{
|
||||
path: '/live',
|
||||
component: live,
|
||||
},
|
||||
{
|
||||
path: '/deviceList',
|
||||
component: deviceList,
|
||||
|
Loading…
Reference in New Issue
Block a user