| | |
| | | </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> |
| | |
| | | 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;
|
| | |
|
| | |
| | | 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);
|
| | |
|
| | | /**
|
| | | * 获取某个设备的通道列表
|
| | | *
|
| | | * @param deviceId 设备ID
|
| | |
| | | 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; |
| | |
| | | |
| | | @Select("SELECT * FROM device_channel WHERE deviceId=#{deviceId} AND status=1") |
| | | List<DeviceChannel> queryOnlineChannelsByDeviceId(String deviceId); |
| | | |
| | | List<DeviceChannelTree> tree(String deviceId); |
| | | } |
New file |
| | |
| | | <?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> |
| | |
| | | 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; |
| | |
| | | } |
| | | |
| | | @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); |
| | | } |
New file |
| | |
| | | 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); |
| | | }); |
| | | } |
| | | } |
New file |
| | |
| | | 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)); |
| | | } |
| | | } |
| | | } |
New file |
| | |
| | | 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; |
| | | } |
| | | } |
| | | |
| | | } |
New file |
| | |
| | | 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; |
| | | } |
| | | |
| | | } |
New file |
| | |
| | | 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; |
| | | } |
| | | |
| | | } |
New file |
| | |
| | | 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(); |
| | | } |
| | | } |
New file |
| | |
| | | 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; |
| | | } |
| | | |
| | | } |
New file |
| | |
| | | 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; |
| | | } |
New file |
| | |
| | | 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; |
| | | } |
| | | } |
New file |
| | |
| | | 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; |
| | | } |
| | |
| | | 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; |
| | | } |
| | | } |
| | |
| | | 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; |
| | |
| | | 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 = "国标设备查询") |
| | |
| | | return result; |
| | | } |
| | | |
| | | |
| | | @GetMapping("/{deviceId}/tree") |
| | | @ApiOperation(value = "通道树形结构", notes = "通道树形结构") |
| | | public WVPResult<List<DeviceChannelTree>> tree(@PathVariable String deviceId) { |
| | | return WVPResult.Data(storager.tree(deviceId)); |
| | | } |
| | | } |
New file |
| | |
| | | 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 |
| | | } |
| | | }) |
| | | } |
| | |
| | | <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> |
New file |
| | |
| | | <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> |
New file |
| | |
| | | <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> |
New file |
| | |
| | | <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> |
New file |
| | |
| | | <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> |
| | |
| | | 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' |
| | |
| | | component: control, |
| | | }, |
| | | { |
| | | path: '/live', |
| | | component: live, |
| | | }, |
| | | { |
| | | path: '/deviceList', |
| | | component: deviceList, |
| | | }, |