diff --git a/src/main/java/com/genersoft/iot/vmp/common/VideoManagerConstants.java b/src/main/java/com/genersoft/iot/vmp/common/VideoManagerConstants.java index 98592bd2..f80fd0ea 100644 --- a/src/main/java/com/genersoft/iot/vmp/common/VideoManagerConstants.java +++ b/src/main/java/com/genersoft/iot/vmp/common/VideoManagerConstants.java @@ -18,6 +18,8 @@ public class VideoManagerConstants { public static final String PLAYER_PREFIX = "VMP_player_"; + public static final String PLAY_BLACK_PREFIX = "VMP_playback_"; + public static final String EVENT_ONLINE_REGISTER = "1"; public static final String EVENT_ONLINE_KEEPLIVE = "2"; diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/RecordItem.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/RecordItem.java index 20605b17..1533b664 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/RecordItem.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/RecordItem.java @@ -1,12 +1,18 @@ package com.genersoft.iot.vmp.gb28181.bean; +import org.jetbrains.annotations.NotNull; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; + /** * @Description:设备录像bean * @author: swwheihei * @date: 2020年5月8日 下午2:06:54 */ -public class RecordItem { +public class RecordItem implements Comparable{ private String deviceId; @@ -97,4 +103,21 @@ public class RecordItem { public void setRecorderId(String recorderId) { this.recorderId = recorderId; } + + @Override + public int compareTo(@NotNull RecordItem recordItem) { + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + try { + Date startTime_now = sdf.parse(startTime); + Date startTime_param = sdf.parse(recordItem.getStartTime()); + if (startTime_param.compareTo(startTime_now) > 0) { + return -1; + }else { + return 1; + } + } catch (ParseException e) { + e.printStackTrace(); + } + return 0; + } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java index 0fdc8975..7d5d10ce 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java @@ -80,7 +80,7 @@ public interface ISIPCommander { * @param startTime 开始时间,格式要求:yyyy-MM-dd HH:mm:ss * @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss */ - public String playbackStreamCmd(Device device,String channelId, String startTime, String endTime); + public StreamInfo playbackStreamCmd(Device device,String channelId, String startTime, String endTime); /** * 视频流停止 diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java index e2c11cb6..047918cb 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java @@ -279,7 +279,7 @@ public class SIPCommander implements ISIPCommander { * @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss */ @Override - public String playbackStreamCmd(Device device, String channelId, String startTime, String endTime) { + public StreamInfo playbackStreamCmd(Device device, String channelId, String startTime, String endTime) { try { MediaServerConfig mediaInfo = storager.getMediaInfo(); String ssrc = streamSession.createPlayBackSsrc(); @@ -324,7 +324,13 @@ public class SIPCommander implements ISIPCommander { ClientTransaction transaction = transmitRequest(device, request); streamSession.put(ssrc, transaction); - return ssrc; + + StreamInfo streamInfo = new StreamInfo(); + streamInfo.setSsrc(ssrc); + streamInfo.setCahnnelId(channelId); + streamInfo.setDeviceID(device.getDeviceId()); + boolean b = storager.startPlayBlack(streamInfo); + return streamInfo; } catch ( SipException | ParseException | InvalidArgumentException e) { e.printStackTrace(); diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/MessageRequestProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/MessageRequestProcessor.java index f4411e1c..ec19cd5f 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/MessageRequestProcessor.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/MessageRequestProcessor.java @@ -2,11 +2,7 @@ package com.genersoft.iot.vmp.gb28181.transmit.request.impl; import java.io.ByteArrayInputStream; import java.text.ParseException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; +import java.util.*; import javax.sip.InvalidArgumentException; import javax.sip.RequestEvent; @@ -316,6 +312,7 @@ public class MessageRequestProcessor extends SIPRequestAbstractProcessor { record.setRecorderId(XmlUtil.getText(itemRecord,"RecorderID")); recordList.add(record); } +// recordList.sort(Comparator.naturalOrder()); recordInfo.setRecordList(recordList); } @@ -349,9 +346,13 @@ public class MessageRequestProcessor extends SIPRequestAbstractProcessor { // 走到这里,有以下可能:1、没有录像信息,第一次收到recordinfo的消息即返回响应数据,无redis操作 // 2、有录像数据,且第一次即收到完整数据,返回响应数据,无redis操作 // 3、有录像数据,在超时时间内收到多次包组装后数量足够,返回数据 + + // 对记录进行排序 RequestMessage msg = new RequestMessage(); msg.setDeviceId(deviceId); msg.setType(DeferredResultHolder.CALLBACK_CMD_RECORDINFO); + // 自然顺序排序, 元素进行升序排列 + recordInfo.getRecordList().sort(Comparator.naturalOrder()); msg.setData(recordInfo); deferredResultHolder.invokeResult(msg); } catch (DocumentException e) { diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java index 96b4af21..6fd4cf5b 100644 --- a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java @@ -130,17 +130,27 @@ public class ZLMHttpHookListener { String streamId = json.getString("id"); -// String ssrc = String.format("%10d", Integer.parseInt(streamId, 16)); // ZLM 要求大写且首位补零 String ssrc = new DecimalFormat("0000000000").format(Integer.parseInt(streamId, 16)); - StreamInfo streamInfo = storager.queryPlayBySSRC(ssrc); - if ("rtp".equals(app) && streamInfo != null ) { + StreamInfo streamInfoForPlay = storager.queryPlayBySSRC(ssrc); + if ("rtp".equals(app) && streamInfoForPlay != null ) { + MediaServerConfig mediaInfo = storager.getMediaInfo(); + streamInfoForPlay.setFlv(String.format("http://%s:%s/rtp/%s.flv", mediaInfo.getLocalIP(), mediaInfo.getHttpPort(), streamId)); + streamInfoForPlay.setWs_flv(String.format("ws://%s:%s/rtp/%s.flv", mediaInfo.getLocalIP(), mediaInfo.getHttpPort(), streamId)); + streamInfoForPlay.setRtmp(String.format("rtmp://%s:%s/rtp/%s", mediaInfo.getLocalIP(), mediaInfo.getRtmpPort(), streamId)); + streamInfoForPlay.setHls(String.format("http://%s:%s/rtp/%s/hls.m3u8", mediaInfo.getLocalIP(), mediaInfo.getHttpPort(), streamId)); + streamInfoForPlay.setRtsp(String.format("rtsp://%s:%s/rtp/%s", mediaInfo.getLocalIP(), mediaInfo.getRtspPort(), streamId)); + storager.startPlay(streamInfoForPlay); + } + + StreamInfo streamInfoForPlayBack = storager.queryPlayBlackBySSRC(ssrc); + if ("rtp".equals(app) && streamInfoForPlayBack != null ) { MediaServerConfig mediaInfo = storager.getMediaInfo(); - streamInfo.setFlv(String.format("http://%s:%s/rtp/%s.flv", mediaInfo.getLocalIP(), mediaInfo.getHttpPort(), streamId)); - streamInfo.setWs_flv(String.format("ws://%s:%s/rtp/%s.flv", mediaInfo.getLocalIP(), mediaInfo.getHttpPort(), streamId)); - streamInfo.setRtmp(String.format("rtmp://%s:%s/rtp/%s", mediaInfo.getLocalIP(), mediaInfo.getRtmpPort(), streamId)); - streamInfo.setHls(String.format("http://%s:%s/rtp/%s/hls.m3u8", mediaInfo.getLocalIP(), mediaInfo.getHttpPort(), streamId)); - streamInfo.setRtsp(String.format("rtsp://%s:%s/rtp/%s", mediaInfo.getLocalIP(), mediaInfo.getRtspPort(), streamId)); - storager.startPlay(streamInfo); + streamInfoForPlayBack.setFlv(String.format("http://%s:%s/rtp/%s.flv", mediaInfo.getLocalIP(), mediaInfo.getHttpPort(), streamId)); + streamInfoForPlayBack.setWs_flv(String.format("ws://%s:%s/rtp/%s.flv", mediaInfo.getLocalIP(), mediaInfo.getHttpPort(), streamId)); + streamInfoForPlayBack.setRtmp(String.format("rtmp://%s:%s/rtp/%s", mediaInfo.getLocalIP(), mediaInfo.getRtmpPort(), streamId)); + streamInfoForPlayBack.setHls(String.format("http://%s:%s/rtp/%s/hls.m3u8", mediaInfo.getLocalIP(), mediaInfo.getHttpPort(), streamId)); + streamInfoForPlayBack.setRtsp(String.format("rtsp://%s:%s/rtp/%s", mediaInfo.getLocalIP(), mediaInfo.getRtspPort(), streamId)); + storager.startPlayBlack(streamInfoForPlayBack); } // TODO Auto-generated method stub diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMUtils.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMUtils.java index 422f6220..f44e7df7 100644 --- a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMUtils.java +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMUtils.java @@ -30,11 +30,6 @@ public class ZLMUtils { param.put("stream_id", streamId); JSONObject jsonObject = zlmresTfulUtils.openRtpServer(param); if (jsonObject.getInteger("code") == 0) { - System.out.println(11111111); - System.out.println(streamId); - System.out.println(ssrc); - System.out.println(newPort); - System.out.println(jsonObject.toJSONString()); return newPort; } else { return getNewRTPPort(ssrc); diff --git a/src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorager.java b/src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorager.java index 0894fe2a..26809220 100644 --- a/src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorager.java +++ b/src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorager.java @@ -183,4 +183,12 @@ public interface IVideoManagerStorager { StreamInfo queryPlayByDevice(String deviceId, String code); Map queryPlayByDeviceId(String deviceId); + + boolean startPlayBlack(StreamInfo streamInfo); + + boolean stopPlayBlack(StreamInfo streamInfo); + + StreamInfo queryPlayBlackByDevice(String deviceId, String channelId); + + StreamInfo queryPlayBlackBySSRC(String ssrc); } diff --git a/src/main/java/com/genersoft/iot/vmp/storager/jdbc/VideoManagerJdbcStoragerImpl.java b/src/main/java/com/genersoft/iot/vmp/storager/jdbc/VideoManagerJdbcStoragerImpl.java index 1e68095c..de94f865 100644 --- a/src/main/java/com/genersoft/iot/vmp/storager/jdbc/VideoManagerJdbcStoragerImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/storager/jdbc/VideoManagerJdbcStoragerImpl.java @@ -190,6 +190,27 @@ public class VideoManagerJdbcStoragerImpl implements IVideoManagerStorager { @Override public Map queryPlayByDeviceId(String deviceId) { + + return null; + } + + @Override + public boolean startPlayBlack(StreamInfo streamInfo) { + return false; + } + + @Override + public boolean stopPlayBlack(StreamInfo streamInfo) { + return false; + } + + @Override + public StreamInfo queryPlayBlackByDevice(String deviceId, String channelId) { + return null; + } + + @Override + public StreamInfo queryPlayBlackBySSRC(String ssrc) { return null; } } diff --git a/src/main/java/com/genersoft/iot/vmp/storager/redis/VideoManagerRedisStoragerImpl.java b/src/main/java/com/genersoft/iot/vmp/storager/redis/VideoManagerRedisStoragerImpl.java index 10f38159..e69a6451 100644 --- a/src/main/java/com/genersoft/iot/vmp/storager/redis/VideoManagerRedisStoragerImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/storager/redis/VideoManagerRedisStoragerImpl.java @@ -410,6 +410,14 @@ public class VideoManagerRedisStoragerImpl implements IVideoManagerStorager { return (StreamInfo)redis.get(playLeys.get(0).toString()); } + @Override + public StreamInfo queryPlayBlackBySSRC(String ssrc) { +// List playLeys = redis.keys(String.format("%S_%s_*", VideoManagerConstants.PLAYER_PREFIX, ssrc)); + List playLeys = redis.scan(String.format("%S_%s_*", VideoManagerConstants.PLAY_BLACK_PREFIX, ssrc)); + if (playLeys == null || playLeys.size() == 0) return null; + return (StreamInfo)redis.get(playLeys.get(0).toString()); + } + @Override public StreamInfo queryPlayByDevice(String deviceId, String code) { // List playLeys = redis.keys(String.format("%S_*_%s_%s", VideoManagerConstants.PLAYER_PREFIX, @@ -498,5 +506,37 @@ public class VideoManagerRedisStoragerImpl implements IVideoManagerStorager { } + @Override + public boolean startPlayBlack(StreamInfo stream) { + return redis.set(String.format("%S_%s_%s_%s", VideoManagerConstants.PLAY_BLACK_PREFIX, stream.getSsrc(),stream.getDeviceID(), stream.getCahnnelId()), + stream); + } + + + @Override + public boolean stopPlayBlack(StreamInfo streamInfo) { + if (streamInfo == null) return false; + DeviceChannel deviceChannel = queryChannel(streamInfo.getDeviceID(), streamInfo.getCahnnelId()); + if (deviceChannel != null) { + deviceChannel.setSsrc(null); + deviceChannel.setPlay(false); + updateChannel(streamInfo.getDeviceID(), deviceChannel); + } + return redis.del(String.format("%S_%s_%s_%s", VideoManagerConstants.PLAY_BLACK_PREFIX, + streamInfo.getSsrc(), + streamInfo.getDeviceID(), + streamInfo.getCahnnelId())); + } + @Override + public StreamInfo queryPlayBlackByDevice(String deviceId, String code) { + String format = String.format("%S_*_%s_%s", VideoManagerConstants.PLAY_BLACK_PREFIX, + deviceId, + code); + List playLeys = redis.scan(String.format("%S_*_%s_%s", VideoManagerConstants.PLAY_BLACK_PREFIX, + deviceId, + code)); + if (playLeys == null || playLeys.size() == 0) return null; + return (StreamInfo)redis.get(playLeys.get(0).toString()); + } } diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/play/PlayController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/play/PlayController.java index 134e692c..ea2a062e 100644 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/play/PlayController.java +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/play/PlayController.java @@ -70,7 +70,7 @@ public class PlayController { }else { streamInfo = storager.queryPlayByDevice(deviceId, channelId); JSONObject rtpInfo = zlmresTfulUtils.getRtpInfo(streamId); - if (rtpInfo != null && rtpInfo.getBoolean("exist") && streamInfo.getFlv() != null){ + if (rtpInfo != null && rtpInfo.getBoolean("exist") && streamInfo != null && streamInfo.getFlv() != null){ JSONObject mediaInfo = zlmresTfulUtils.getMediaInfo("rtp", "rtmp", streamId); if (mediaInfo.getInteger("code") == 0 && mediaInfo.getBoolean("online")) { lockFlag = false; diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/playback/PlaybackController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/playback/PlaybackController.java index cc3961be..e53cbbf2 100644 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/playback/PlaybackController.java +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/playback/PlaybackController.java @@ -1,5 +1,9 @@ package com.genersoft.iot.vmp.vmanager.playback; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONArray; +import com.genersoft.iot.vmp.common.StreamInfo; +import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -30,7 +34,10 @@ public class PlaybackController { @Autowired private IVideoManagerStorager storager; - + + @Autowired + private ZLMRESTfulUtils zlmresTfulUtils; + @GetMapping("/playback/{deviceId}/{channelId}") public ResponseEntity play(@PathVariable String deviceId,@PathVariable String channelId, String startTime, String endTime){ @@ -43,25 +50,70 @@ public class PlaybackController { logger.warn(log); return new ResponseEntity(log,HttpStatus.BAD_REQUEST); } - + Device device = storager.queryVideoDevice(deviceId); - String ssrc = cmder.playbackStreamCmd(device, channelId, startTime, endTime); + StreamInfo streamInfo = storager.queryPlayBlackByDevice(deviceId, channelId); + + if (streamInfo != null) { + cmder.streamByeCmd(streamInfo.getSsrc()); + } + +// }else { +// String streamId = String.format("%08x", Integer.parseInt(streamInfo.getSsrc())).toUpperCase(); +// JSONObject rtpInfo = zlmresTfulUtils.getRtpInfo(streamId); +// if (rtpInfo.getBoolean("exist")) { +// return new ResponseEntity(JSON.toJSONString(streamInfo),HttpStatus.OK); +// }else { +// storager.stopPlayBlack(streamInfo); +// streamInfo = cmder.playbackStreamCmd(device, channelId, startTime, endTime); +// } +// } + streamInfo = cmder.playbackStreamCmd(device, channelId, startTime, endTime); + + String streamId = String.format("%08x", Integer.parseInt(streamInfo.getSsrc())).toUpperCase(); if (logger.isDebugEnabled()) { - logger.debug("设备回放 API调用,ssrc:"+ssrc+",ZLMedia streamId:"+Integer.toHexString(Integer.parseInt(ssrc))); + logger.debug("设备回放 API调用,ssrc:" + streamInfo.getSsrc() + ",ZLMedia streamId:" + streamId); } - - if(ssrc!=null) { - JSONObject json = new JSONObject(); - json.put("ssrc", ssrc); - return new ResponseEntity(json.toString(),HttpStatus.OK); + // 等待推流, TODO 默认超时15s + boolean lockFlag = true; + long lockStartTime = System.currentTimeMillis(); + while (lockFlag) { + try { + if (System.currentTimeMillis() - lockStartTime > 75 * 1000) { + storager.stopPlayBlack(streamInfo); + return new ResponseEntity("timeout",HttpStatus.OK); + }else { + streamInfo = storager.queryPlayBlackByDevice(deviceId, channelId); + JSONObject rtpInfo = zlmresTfulUtils.getRtpInfo(streamId); + if (rtpInfo != null && rtpInfo.getBoolean("exist") && streamInfo.getFlv() != null){ + JSONObject mediaInfo = zlmresTfulUtils.getMediaInfo("rtp", "rtmp", streamId); + if (mediaInfo.getInteger("code") == 0 && mediaInfo.getBoolean("online")) { + lockFlag = false; + JSONArray tracks = mediaInfo.getJSONArray("tracks"); + streamInfo.setTracks(tracks); + storager.startPlayBlack(streamInfo); + }else { + + } + }else { + Thread.sleep(2000); + continue; + }; + } + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + if(streamInfo!=null) { + return new ResponseEntity(JSON.toJSONString(streamInfo),HttpStatus.OK); } else { logger.warn("设备回放API调用失败!"); return new ResponseEntity(HttpStatus.INTERNAL_SERVER_ERROR); } } - @PostMapping("/playback/{ssrc}/stop") + @RequestMapping("/playback/{ssrc}/stop") public ResponseEntity playStop(@PathVariable String ssrc){ cmder.streamByeCmd(ssrc); diff --git a/web_src/index.html b/web_src/index.html index cb0b3df9..9c4e39b2 100644 --- a/web_src/index.html +++ b/web_src/index.html @@ -3,7 +3,7 @@ - GB28181 + 国标28181 diff --git a/web_src/package-lock.json b/web_src/package-lock.json index f031dcce..e00c5cea 100644 --- a/web_src/package-lock.json +++ b/web_src/package-lock.json @@ -5843,6 +5843,11 @@ "minimist": "^1.2.5" } }, + "moment": { + "version": "2.29.1", + "resolved": "https://registry.npm.taobao.org/moment/download/moment-2.29.1.tgz?cache=0&sync_timestamp=1601983423917&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fmoment%2Fdownload%2Fmoment-2.29.1.tgz", + "integrity": "sha1-sr52n6MZQL6e7qZGnAdeNQBvo9M=" + }, "move-concurrently": { "version": "1.0.1", "resolved": "https://registry.npm.taobao.org/move-concurrently/download/move-concurrently-1.0.1.tgz", diff --git a/web_src/package.json b/web_src/package.json index 8f2b3e22..05178d42 100644 --- a/web_src/package.json +++ b/web_src/package.json @@ -15,6 +15,7 @@ "core-js": "^2.6.5", "echarts": "^4.7.0", "element-ui": "2.10.1", + "moment": "^2.29.1", "vue": "^2.6.11", "vue-clipboard2": "^0.3.1", "vue-cookies": "^1.7.4", diff --git a/web_src/src/components/Loading.vue b/web_src/src/components/Loading.vue deleted file mode 100644 index 433705e8..00000000 --- a/web_src/src/components/Loading.vue +++ /dev/null @@ -1,98 +0,0 @@ -//loading效果组件 - - - - - - diff --git a/web_src/src/components/Login.vue b/web_src/src/components/Login.vue index 030d3eec..f047c5db 100644 --- a/web_src/src/components/Login.vue +++ b/web_src/src/components/Login.vue @@ -10,16 +10,14 @@
-
+ -
diff --git a/web_src/src/main.js b/web_src/src/main.js index 1b31f7b6..6f88c2a9 100644 --- a/web_src/src/main.js +++ b/web_src/src/main.js @@ -6,8 +6,8 @@ import 'element-ui/lib/theme-chalk/index.css'; import router from './router/index.js'; import axios from 'axios'; import VueCookies from 'vue-cookies'; - import echarts from 'echarts'; + import VueClipboard from 'vue-clipboard2' Vue.use(VueClipboard) Vue.use(ElementUI);