648540858
3 years ago
27 changed files with 761 additions and 591 deletions
@ -0,0 +1,139 @@ |
|||||
|
package com.genersoft.iot.vmp.media.zlm; |
||||
|
|
||||
|
import com.alibaba.fastjson.JSON; |
||||
|
import com.alibaba.fastjson.JSONObject; |
||||
|
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; |
||||
|
import okhttp3.*; |
||||
|
import okhttp3.logging.HttpLoggingInterceptor; |
||||
|
import org.jetbrains.annotations.NotNull; |
||||
|
import org.slf4j.Logger; |
||||
|
import org.slf4j.LoggerFactory; |
||||
|
import org.springframework.stereotype.Component; |
||||
|
import org.springframework.util.StringUtils; |
||||
|
|
||||
|
import java.io.File; |
||||
|
import java.io.FileOutputStream; |
||||
|
import java.io.IOException; |
||||
|
import java.net.ConnectException; |
||||
|
import java.util.HashMap; |
||||
|
import java.util.Map; |
||||
|
import java.util.Objects; |
||||
|
import java.util.concurrent.TimeUnit; |
||||
|
|
||||
|
@Component |
||||
|
public class AssistRESTfulUtils { |
||||
|
|
||||
|
private final static Logger logger = LoggerFactory.getLogger(AssistRESTfulUtils.class); |
||||
|
|
||||
|
public interface RequestCallback{ |
||||
|
void run(JSONObject response); |
||||
|
} |
||||
|
|
||||
|
private OkHttpClient getClient(){ |
||||
|
OkHttpClient.Builder httpClientBuilder = new OkHttpClient.Builder(); |
||||
|
if (logger.isDebugEnabled()) { |
||||
|
HttpLoggingInterceptor logging = new HttpLoggingInterceptor(message -> { |
||||
|
logger.debug("http请求参数:" + message); |
||||
|
}); |
||||
|
logging.setLevel(HttpLoggingInterceptor.Level.BASIC); |
||||
|
// OkHttp進行添加攔截器loggingInterceptor
|
||||
|
httpClientBuilder.addInterceptor(logging); |
||||
|
} |
||||
|
return httpClientBuilder.build(); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
public JSONObject sendGet(MediaServerItem mediaServerItem, String api, Map<String, Object> param, RequestCallback callback) { |
||||
|
OkHttpClient client = getClient(); |
||||
|
|
||||
|
if (mediaServerItem == null) { |
||||
|
return null; |
||||
|
} |
||||
|
if (StringUtils.isEmpty(mediaServerItem.getRecordAssistPort())) { |
||||
|
logger.warn("未启用Assist服务"); |
||||
|
return null; |
||||
|
} |
||||
|
StringBuffer stringBuffer = new StringBuffer(); |
||||
|
stringBuffer.append(String.format("http://%s:%s/%s", mediaServerItem.getIp(), mediaServerItem.getRecordAssistPort(), api)); |
||||
|
JSONObject responseJSON = null; |
||||
|
|
||||
|
if (param != null && param.keySet().size() > 0) { |
||||
|
stringBuffer.append("?"); |
||||
|
int index = 1; |
||||
|
for (String key : param.keySet()){ |
||||
|
if (param.get(key) != null) { |
||||
|
stringBuffer.append(key + "=" + param.get(key)); |
||||
|
if (index < param.size()) { |
||||
|
stringBuffer.append("&"); |
||||
|
} |
||||
|
} |
||||
|
index++; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
String url = stringBuffer.toString(); |
||||
|
Request request = new Request.Builder() |
||||
|
.get() |
||||
|
.url(url) |
||||
|
.build(); |
||||
|
if (callback == null) { |
||||
|
try { |
||||
|
Response response = client.newCall(request).execute(); |
||||
|
if (response.isSuccessful()) { |
||||
|
ResponseBody responseBody = response.body(); |
||||
|
if (responseBody != null) { |
||||
|
String responseStr = responseBody.string(); |
||||
|
responseJSON = JSON.parseObject(responseStr); |
||||
|
} |
||||
|
}else { |
||||
|
response.close(); |
||||
|
Objects.requireNonNull(response.body()).close(); |
||||
|
} |
||||
|
} catch (ConnectException e) { |
||||
|
logger.error(String.format("连接Assist失败: %s, %s", e.getCause().getMessage(), e.getMessage())); |
||||
|
logger.info("请检查media配置并确认Assist已启动..."); |
||||
|
}catch (IOException e) { |
||||
|
logger.error(String.format("[ %s ]请求失败: %s", url, e.getMessage())); |
||||
|
} |
||||
|
}else { |
||||
|
client.newCall(request).enqueue(new Callback(){ |
||||
|
|
||||
|
@Override |
||||
|
public void onResponse(@NotNull Call call, @NotNull Response response){ |
||||
|
if (response.isSuccessful()) { |
||||
|
try { |
||||
|
String responseStr = Objects.requireNonNull(response.body()).string(); |
||||
|
callback.run(JSON.parseObject(responseStr)); |
||||
|
} catch (IOException e) { |
||||
|
logger.error(String.format("[ %s ]请求失败: %s", url, e.getMessage())); |
||||
|
} |
||||
|
|
||||
|
}else { |
||||
|
response.close(); |
||||
|
Objects.requireNonNull(response.body()).close(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onFailure(@NotNull Call call, @NotNull IOException e) { |
||||
|
logger.error(String.format("连接Assist失败: %s, %s", e.getCause().getMessage(), e.getMessage())); |
||||
|
logger.info("请检查media配置并确认Assist已启动..."); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
|
return responseJSON; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
public JSONObject fileDuration(MediaServerItem mediaServerItem, String app, String stream, RequestCallback callback){ |
||||
|
Map<String, Object> param = new HashMap<>(); |
||||
|
param.put("app",app); |
||||
|
param.put("stream",stream); |
||||
|
param.put("recordIng",true); |
||||
|
return sendGet(mediaServerItem, "api/record/file/duration",param, callback); |
||||
|
} |
||||
|
|
||||
|
} |
@ -1,150 +0,0 @@ |
|||||
package com.genersoft.iot.vmp.vmanager.gb28181.playback; |
|
||||
|
|
||||
import com.genersoft.iot.vmp.common.StreamInfo; |
|
||||
import com.genersoft.iot.vmp.gb28181.bean.InviteStreamInfo; |
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder; |
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; |
|
||||
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; |
|
||||
import com.genersoft.iot.vmp.service.IMediaServerService; |
|
||||
import com.genersoft.iot.vmp.service.bean.SSRCInfo; |
|
||||
import com.genersoft.iot.vmp.storager.IRedisCatchStorage; |
|
||||
import com.genersoft.iot.vmp.service.IPlayService; |
|
||||
import io.swagger.annotations.Api; |
|
||||
import io.swagger.annotations.ApiImplicitParam; |
|
||||
import io.swagger.annotations.ApiImplicitParams; |
|
||||
import io.swagger.annotations.ApiOperation; |
|
||||
import org.slf4j.Logger; |
|
||||
import org.slf4j.LoggerFactory; |
|
||||
import org.springframework.beans.factory.annotation.Autowired; |
|
||||
import org.springframework.http.HttpStatus; |
|
||||
import org.springframework.http.ResponseEntity; |
|
||||
import org.springframework.web.bind.annotation.CrossOrigin; |
|
||||
import org.springframework.web.bind.annotation.GetMapping; |
|
||||
import org.springframework.web.bind.annotation.PathVariable; |
|
||||
import org.springframework.web.bind.annotation.RequestMapping; |
|
||||
import org.springframework.web.bind.annotation.RestController; |
|
||||
|
|
||||
import com.alibaba.fastjson.JSONObject; |
|
||||
import com.genersoft.iot.vmp.gb28181.bean.Device; |
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; |
|
||||
import com.genersoft.iot.vmp.storager.IVideoManagerStorager; |
|
||||
import org.springframework.web.context.request.async.DeferredResult; |
|
||||
|
|
||||
import javax.sip.message.Response; |
|
||||
import java.util.UUID; |
|
||||
|
|
||||
@Api(tags = "历史媒体下载") |
|
||||
@CrossOrigin |
|
||||
@RestController |
|
||||
@RequestMapping("/api/download") |
|
||||
public class DownloadController { |
|
||||
|
|
||||
private final static Logger logger = LoggerFactory.getLogger(DownloadController.class); |
|
||||
|
|
||||
@Autowired |
|
||||
private SIPCommander cmder; |
|
||||
|
|
||||
@Autowired |
|
||||
private IVideoManagerStorager storager; |
|
||||
|
|
||||
@Autowired |
|
||||
private IRedisCatchStorage redisCatchStorage; |
|
||||
|
|
||||
// @Autowired
|
|
||||
// private ZLMRESTfulUtils zlmresTfulUtils;
|
|
||||
|
|
||||
@Autowired |
|
||||
private IPlayService playService; |
|
||||
|
|
||||
@Autowired |
|
||||
private DeferredResultHolder resultHolder; |
|
||||
|
|
||||
@Autowired |
|
||||
private IMediaServerService mediaServerService; |
|
||||
|
|
||||
@ApiOperation("开始历史媒体下载") |
|
||||
@ApiImplicitParams({ |
|
||||
@ApiImplicitParam(name = "deviceId", value = "设备ID", dataTypeClass = String.class), |
|
||||
@ApiImplicitParam(name = "channelId", value = "通道ID", dataTypeClass = String.class), |
|
||||
@ApiImplicitParam(name = "startTime", value = "开始时间", dataTypeClass = String.class), |
|
||||
@ApiImplicitParam(name = "endTime", value = "结束时间", dataTypeClass = String.class), |
|
||||
@ApiImplicitParam(name = "downloadSpeed", value = "下载倍速", dataTypeClass = String.class), |
|
||||
}) |
|
||||
@GetMapping("/start/{deviceId}/{channelId}") |
|
||||
public DeferredResult<ResponseEntity<String>> play(@PathVariable String deviceId, @PathVariable String channelId, |
|
||||
String startTime, String endTime, String downloadSpeed) { |
|
||||
|
|
||||
if (logger.isDebugEnabled()) { |
|
||||
logger.debug(String.format("历史媒体下载 API调用,deviceId:%s,channelId:%s,downloadSpeed:%s", deviceId, channelId, downloadSpeed)); |
|
||||
} |
|
||||
String key = DeferredResultHolder.CALLBACK_CMD_DOWNLOAD + deviceId + channelId; |
|
||||
String uuid = UUID.randomUUID().toString(); |
|
||||
DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String>>(30000L); |
|
||||
// 超时处理
|
|
||||
result.onTimeout(()->{ |
|
||||
logger.warn(String.format("设备下载响应超时,deviceId:%s ,channelId:%s", deviceId, channelId)); |
|
||||
RequestMessage msg = new RequestMessage(); |
|
||||
msg.setId(uuid); |
|
||||
msg.setKey(key); |
|
||||
msg.setData("Timeout"); |
|
||||
resultHolder.invokeAllResult(msg); |
|
||||
}); |
|
||||
if(resultHolder.exist(key, null)) { |
|
||||
return result; |
|
||||
} |
|
||||
resultHolder.put(key, uuid, result); |
|
||||
Device device = storager.queryVideoDevice(deviceId); |
|
||||
|
|
||||
MediaServerItem newMediaServerItem = playService.getNewMediaServerItem(device); |
|
||||
if (newMediaServerItem == null) { |
|
||||
logger.warn(String.format("设备下载响应超时,deviceId:%s ,channelId:%s", deviceId, channelId)); |
|
||||
RequestMessage msg = new RequestMessage(); |
|
||||
msg.setId(uuid); |
|
||||
msg.setKey(key); |
|
||||
msg.setData("Timeout"); |
|
||||
resultHolder.invokeAllResult(msg); |
|
||||
return result; |
|
||||
} |
|
||||
|
|
||||
SSRCInfo ssrcInfo = mediaServerService.openRTPServer(newMediaServerItem, null, true); |
|
||||
|
|
||||
cmder.downloadStreamCmd(newMediaServerItem, ssrcInfo, device, channelId, startTime, endTime, downloadSpeed, (InviteStreamInfo inviteStreamInfo) -> { |
|
||||
logger.info("收到订阅消息: " + inviteStreamInfo.getResponse().toJSONString()); |
|
||||
playService.onPublishHandlerForDownload(inviteStreamInfo, deviceId, channelId, uuid); |
|
||||
}, event -> { |
|
||||
RequestMessage msg = new RequestMessage(); |
|
||||
msg.setId(uuid); |
|
||||
msg.setKey(key); |
|
||||
msg.setData(String.format("回放失败, 错误码: %s, %s", event.statusCode, event.msg)); |
|
||||
resultHolder.invokeAllResult(msg); |
|
||||
}); |
|
||||
|
|
||||
return result; |
|
||||
} |
|
||||
|
|
||||
@ApiOperation("停止历史媒体下载") |
|
||||
@ApiImplicitParams({ |
|
||||
@ApiImplicitParam(name = "deviceId", value = "设备ID", dataTypeClass = String.class), |
|
||||
@ApiImplicitParam(name = "channelId", value = "通道ID", dataTypeClass = String.class), |
|
||||
@ApiImplicitParam(name = "stream", value = "流ID", dataTypeClass = String.class), |
|
||||
}) |
|
||||
@GetMapping("/stop/{deviceId}/{channelId}/{stream}") |
|
||||
public ResponseEntity<String> playStop(@PathVariable String deviceId, @PathVariable String channelId, @PathVariable String stream) { |
|
||||
|
|
||||
cmder.streamByeCmd(deviceId, channelId, stream, null); |
|
||||
|
|
||||
if (logger.isDebugEnabled()) { |
|
||||
logger.debug(String.format("设备历史媒体下载停止 API调用,deviceId/channelId:%s_%s", deviceId, channelId)); |
|
||||
} |
|
||||
|
|
||||
if (deviceId != null && channelId != null) { |
|
||||
JSONObject json = new JSONObject(); |
|
||||
json.put("deviceId", deviceId); |
|
||||
json.put("channelId", channelId); |
|
||||
return new ResponseEntity<String>(json.toString(), HttpStatus.OK); |
|
||||
} else { |
|
||||
logger.warn("设备历史媒体下载停止API调用失败!"); |
|
||||
return new ResponseEntity<String>(HttpStatus.INTERNAL_SERVER_ERROR); |
|
||||
} |
|
||||
} |
|
||||
} |
|
@ -0,0 +1,195 @@ |
|||||
|
<template> |
||||
|
<div id="recordDownload" > |
||||
|
<el-dialog :title="title" v-if="showDialog" width="45rem" :append-to-body="true" :close-on-click-modal="false" :visible.sync="showDialog" :destroy-on-close="true" @close="close()" center> |
||||
|
<el-row> |
||||
|
<el-col :span="18" style="padding-top: 7px;"> |
||||
|
<el-progress :percentage="percentage"></el-progress> |
||||
|
</el-col> |
||||
|
<el-col :span="6" > |
||||
|
<!-- <el-dropdown size="mini" title="播放倍速" style="margin-left: 1px;" @command="gbScale">--> |
||||
|
<!-- <el-button-group>--> |
||||
|
<!-- <el-button size="mini" style="width: 100%">--> |
||||
|
<!-- {{scale}}倍速 <i class="el-icon-arrow-down el-icon--right"></i>--> |
||||
|
<!-- </el-button>--> |
||||
|
<!-- </el-button-group>--> |
||||
|
<!-- <el-dropdown-menu slot="dropdown">--> |
||||
|
<!-- <el-dropdown-item command="1">1倍速</el-dropdown-item>--> |
||||
|
<!-- <el-dropdown-item command="2">2倍速</el-dropdown-item>--> |
||||
|
<!-- <el-dropdown-item command="4">4倍速</el-dropdown-item>--> |
||||
|
<!-- </el-dropdown-menu>--> |
||||
|
<!-- </el-dropdown>--> |
||||
|
<el-button icon="el-icon-download" v-if="percentage < 100" size="mini" title="点击下载可将以缓存部分下载到本地" @click="download()">停止缓存并下载</el-button> |
||||
|
</el-col> |
||||
|
</el-row> |
||||
|
</el-dialog> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
|
||||
|
<script> |
||||
|
|
||||
|
import moment from "moment"; |
||||
|
|
||||
|
export default { |
||||
|
name: 'recordDownload', |
||||
|
created() { |
||||
|
|
||||
|
|
||||
|
}, |
||||
|
data() { |
||||
|
return { |
||||
|
title: "四倍速下载中...", |
||||
|
deviceId: "", |
||||
|
channelId: "", |
||||
|
app: "", |
||||
|
stream: "", |
||||
|
mediaServerId: "", |
||||
|
showDialog: false, |
||||
|
scale: 1, |
||||
|
percentage: 0.00, |
||||
|
streamInfo: null, |
||||
|
taskId: null, |
||||
|
getProgressRun: false, |
||||
|
getProgressForFileRun: false, |
||||
|
|
||||
|
}; |
||||
|
}, |
||||
|
methods: { |
||||
|
openDialog: function (deviceId, channelId, app, stream, mediaServerId) { |
||||
|
this.deviceId = deviceId; |
||||
|
this.channelId = channelId; |
||||
|
this.app = app; |
||||
|
this.stream = stream; |
||||
|
this.mediaServerId = mediaServerId; |
||||
|
this.showDialog = true; |
||||
|
this.getProgressRun = true; |
||||
|
this.percentage = 0.0; |
||||
|
this.getProgressTimer() |
||||
|
}, |
||||
|
getProgressTimer(){ |
||||
|
if (!this.getProgressRun) { |
||||
|
return; |
||||
|
} |
||||
|
if (this.percentage == 100 ) { |
||||
|
this.getFileDownload(); |
||||
|
return; |
||||
|
} |
||||
|
setTimeout( ()=>{ |
||||
|
if (!this.showDialog) return; |
||||
|
this.getProgress(this.getProgressTimer()) |
||||
|
}, 5000) |
||||
|
}, |
||||
|
getProgress: function (callback){ |
||||
|
this.$axios({ |
||||
|
method: 'get', |
||||
|
url: `/api/gb_record/download/progress/${this.deviceId}/${this.channelId}/${this.stream}` |
||||
|
}).then((res)=> { |
||||
|
console.log(res) |
||||
|
console.log(res.data.progress) |
||||
|
this.streamInfo = res.data; |
||||
|
if (parseFloat(res.data.progress) == 1) { |
||||
|
this.percentage = 100; |
||||
|
}else { |
||||
|
this.percentage = (res.data.progress*100).toFixed(1); |
||||
|
} |
||||
|
if (callback)callback(); |
||||
|
}).catch((e) =>{ |
||||
|
|
||||
|
}); |
||||
|
}, |
||||
|
close: function (){ |
||||
|
if (this.streamInfo.progress < 100) { |
||||
|
this.stopDownloadRecord(); |
||||
|
} |
||||
|
this.showDialog=false; |
||||
|
this.getProgressRun = false; |
||||
|
this.getProgressForFileRun = false; |
||||
|
}, |
||||
|
gbScale: function (scale){ |
||||
|
this.scale = scale; |
||||
|
}, |
||||
|
download: function (){ |
||||
|
this.getProgressRun = false; |
||||
|
if (this.streamInfo != null ) { |
||||
|
if (this.streamInfo.progress < 1) { |
||||
|
// 发送停止缓存 |
||||
|
this.stopDownloadRecord((res)=>{ |
||||
|
this.getFileDownload() |
||||
|
}) |
||||
|
}else { |
||||
|
this.getFileDownload() |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
stopDownloadRecord: function (callback) { |
||||
|
this.$axios({ |
||||
|
method: 'get', |
||||
|
url: '/api/gb_record/download/stop/' + this.deviceId + "/" + this.channelId+ "/" + this.stream |
||||
|
}).then((res)=> { |
||||
|
if (callback) callback(res) |
||||
|
}); |
||||
|
}, |
||||
|
getFileDownload: function (){ |
||||
|
this.$axios({ |
||||
|
method: 'get', |
||||
|
url:`/record_proxy/${this.mediaServerId}/api/record/file/download/task/add`, |
||||
|
params: { |
||||
|
app: this.app, |
||||
|
stream: this.stream, |
||||
|
startTime: null, |
||||
|
endTime: null, |
||||
|
} |
||||
|
}).then((res) =>{ |
||||
|
if (res.data.code === 0 && res.data.msg === "success") { |
||||
|
// 查询进度 |
||||
|
this.title = "录像文件处理中..." |
||||
|
this.taskId = res.data.data; |
||||
|
this.percentage = 0.0; |
||||
|
this.getProgressForFileRun = true; |
||||
|
this.getProgressForFileTimer(); |
||||
|
} |
||||
|
}).catch(function (error) { |
||||
|
console.log(error); |
||||
|
}); |
||||
|
}, |
||||
|
getProgressForFileTimer: function (){ |
||||
|
if (!this.getProgressForFileRun || this.percentage == 100) { |
||||
|
return; |
||||
|
} |
||||
|
setTimeout( ()=>{ |
||||
|
if (!this.showDialog) return; |
||||
|
this.getProgressForFile(this.getProgressForFileTimer()) |
||||
|
}, 1000) |
||||
|
}, |
||||
|
getProgressForFile: function (callback){ |
||||
|
this.$axios({ |
||||
|
method: 'get', |
||||
|
url:`/record_proxy/${this.mediaServerId}/api/record/file/download/task/list`, |
||||
|
params: { |
||||
|
app: this.app, |
||||
|
stream: this.stream, |
||||
|
taskId: this.taskId, |
||||
|
isEnd: true, |
||||
|
} |
||||
|
}).then((res) => { |
||||
|
if (res.data.code == 0) { |
||||
|
this.percentage = parseFloat(res.data.data.percentage)*100 |
||||
|
if (res.data.data[0].percentage === '1') { |
||||
|
this.getProgressForFileRun = false; |
||||
|
window.open(res.data.data[0].downloadFile) |
||||
|
this.close(); |
||||
|
}else { |
||||
|
if (callback)callback() |
||||
|
} |
||||
|
} |
||||
|
}).catch(function (error) { |
||||
|
console.log(error); |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
}; |
||||
|
</script> |
||||
|
|
||||
|
<style> |
||||
|
|
||||
|
</style> |
@ -1,198 +0,0 @@ |
|||||
<template> |
|
||||
<div id="test"> |
|
||||
<div class="timeQuery" id="timeQuery"> |
|
||||
<div class="timeQuery-background" ></div> |
|
||||
<div class="timeQuery-pointer"> |
|
||||
<div class="timeQuery-pointer-content" id="timeQueryPointer"> |
|
||||
<div class="timeQuery-pointer-handle" @mousedown.left="mousedownHandler" ></div> |
|
||||
</div> |
|
||||
</div> |
|
||||
|
|
||||
<div class="timeQuery-data" > |
|
||||
|
|
||||
<div class="timeQuery-data-cell" v-for="item of recordData" :style="'width:' + getDataWidth(item) + '%; left:' + getDataLeft(item) + '%'" ></div> |
|
||||
<!-- <div class="timeQuery-data-cell" style="width: 30%; left: 20%" @click="timeChoose"></div>--> |
|
||||
<!-- <div class="timeQuery-data-cell" style="width: 60%; left: 20%" @click="timeChoose"></div>--> |
|
||||
</div> |
|
||||
|
|
||||
<div class="timeQuery-label" > |
|
||||
<div class="timeQuery-label-cell" style="left: 0%"> |
|
||||
<div class="timeQuery-label-cell-label">0</div> |
|
||||
</div> |
|
||||
<div v-for="index of timeNode" class="timeQuery-label-cell" :style="'left:' + (100.0/timeNode*index).toFixed(4) + '%'"> |
|
||||
<div class="timeQuery-label-cell-label">{{24/timeNode * index}}</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
|
|
||||
</div> |
|
||||
</template> |
|
||||
|
|
||||
<script> |
|
||||
export default { |
|
||||
name: "test", |
|
||||
data() { |
|
||||
return { |
|
||||
mouseDown: false, |
|
||||
timeNode: 24, |
|
||||
recordData:[ |
|
||||
{ |
|
||||
startTime: "2021-04-18 00:00:00", |
|
||||
endTime: "2021-04-18 00:00:09", |
|
||||
}, |
|
||||
{ |
|
||||
startTime: "2021-04-18 00:00:09", |
|
||||
endTime: "2021-04-18 01:00:05", |
|
||||
}, |
|
||||
{ |
|
||||
startTime: "2021-04-18 02:00:01", |
|
||||
endTime: "2021-04-18 04:25:05", |
|
||||
}, |
|
||||
{ |
|
||||
startTime: "2021-04-18 05:00:01", |
|
||||
endTime: "2021-04-18 20:00:05", |
|
||||
}, |
|
||||
] |
|
||||
}; |
|
||||
}, |
|
||||
mounted() { |
|
||||
document.body.addEventListener("mouseup", this.mouseupHandler, false) |
|
||||
document.body.addEventListener("mousemove", this.mousemoveHandler, false) |
|
||||
}, |
|
||||
methods:{ |
|
||||
getTimeNode(){ |
|
||||
let mine = 20 |
|
||||
let width = document.getElementById("timeQuery").offsetWidth |
|
||||
if (width/20 > 24){ |
|
||||
return 24 |
|
||||
}else if (width/20 > 12) { |
|
||||
return 12 |
|
||||
}else if (width/20 > 6) { |
|
||||
return 6 |
|
||||
} |
|
||||
}, |
|
||||
timeChoose(event){ |
|
||||
console.log(event) |
|
||||
}, |
|
||||
getDataWidth(item){ |
|
||||
let startTime = new Date(item.startTime); |
|
||||
let endTime = new Date(item.endTime); |
|
||||
let result = parseFloat((endTime.getTime() - startTime.getTime())/(24*60*60*10)) |
|
||||
// console.log(result) |
|
||||
return parseFloat((endTime.getTime() - startTime.getTime())/(24*60*60*10)) |
|
||||
}, |
|
||||
getDataLeft(item){ |
|
||||
let startTime = new Date(item.startTime); |
|
||||
let differenceTime = startTime.getTime() - new Date(item.startTime.substr(0,10) + " 00:00:00").getTime() |
|
||||
let result = differenceTime/(24*60*60*10) |
|
||||
console.log(differenceTime) |
|
||||
console.log(result) |
|
||||
return parseFloat(differenceTime/(24*60*60*10)); |
|
||||
}, |
|
||||
mousedownHandler(event){ |
|
||||
this.mouseDown = true |
|
||||
}, |
|
||||
mousemoveHandler(event){ |
|
||||
if (this.mouseDown){ |
|
||||
document.getElementById("timeQueryPointer").style.left = (event.clientX - 20)+ "px" |
|
||||
} |
|
||||
}, |
|
||||
mouseupHandler(event){ |
|
||||
this.mouseDown = false |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
</script> |
|
||||
|
|
||||
<style scoped> |
|
||||
.timeQuery{ |
|
||||
width: 96%; |
|
||||
margin-left: 2%; |
|
||||
margin-right: 2%; |
|
||||
margin-top: 20%; |
|
||||
position: absolute; |
|
||||
} |
|
||||
.timeQuery-background{ |
|
||||
height: 16px; |
|
||||
width: 100%; |
|
||||
background-color: #ececec; |
|
||||
position: absolute; |
|
||||
left: 0; |
|
||||
top: 0; |
|
||||
z-index: 10; |
|
||||
box-shadow: #9d9d9d 0px 0px 10px inset; |
|
||||
} |
|
||||
.timeQuery-data{ |
|
||||
height: 16px; |
|
||||
width: 100%; |
|
||||
position: absolute; |
|
||||
left: 0; |
|
||||
top: 0; |
|
||||
z-index: 11; |
|
||||
} |
|
||||
.timeQuery-data-cell{ |
|
||||
height: 10px; |
|
||||
background-color: #888787; |
|
||||
position: absolute; |
|
||||
z-index: 11; |
|
||||
-webkit-box-shadow: #9d9d9d 0px 0px 10px inset; |
|
||||
margin-top: 3px; |
|
||||
top: 100%; |
|
||||
} |
|
||||
.timeQuery-label{ |
|
||||
height: 16px; |
|
||||
width: 100%; |
|
||||
position: absolute; |
|
||||
pointer-events: none; |
|
||||
left: 0; |
|
||||
top: 0; |
|
||||
z-index: 11; |
|
||||
} |
|
||||
.timeQuery-label-cell{ |
|
||||
height: 16px; |
|
||||
position: absolute; |
|
||||
z-index: 12; |
|
||||
width: 0px; |
|
||||
border-right: 1px solid #b7b7b7; |
|
||||
} |
|
||||
.timeQuery-label-cell-label { |
|
||||
width: 23px; |
|
||||
text-align: center; |
|
||||
height: 18px; |
|
||||
margin-left: -10px; |
|
||||
margin-top: -30px; |
|
||||
color: #444; |
|
||||
} |
|
||||
.timeQuery-pointer{ |
|
||||
width: 0px; |
|
||||
height: 18px; |
|
||||
position: absolute; |
|
||||
left: 0; |
|
||||
} |
|
||||
.timeQuery-pointer-content{ |
|
||||
width: 0px; |
|
||||
height: 70px; |
|
||||
position: absolute; |
|
||||
border-right: 2px solid #f60303; |
|
||||
z-index: 14; |
|
||||
top: -30px; |
|
||||
} |
|
||||
.timeQuery-pointer-handle { |
|
||||
width: 0; |
|
||||
height: 0; |
|
||||
border-top: 12px solid transparent; |
|
||||
border-right: 12px solid transparent; |
|
||||
border-bottom: 20px solid #ff0909; |
|
||||
border-left: 12px solid transparent; |
|
||||
cursor: no-drop; |
|
||||
position: absolute; |
|
||||
left: -11px; |
|
||||
top: 50px; |
|
||||
} |
|
||||
/*.timeQuery-cell:after{*/ |
|
||||
/* content: "";*/ |
|
||||
/* height: 14px;*/ |
|
||||
/* border: 1px solid #e70303;*/ |
|
||||
/* position: absolute;*/ |
|
||||
/*}*/ |
|
||||
</style> |
|
@ -1,190 +0,0 @@ |
|||||
<template> |
|
||||
<div id="test2"> |
|
||||
<div class="timeQuery" style="width: 100%; height: 300px" id="timeQuery"> |
|
||||
</div> |
|
||||
</div> |
|
||||
</template> |
|
||||
|
|
||||
<script> |
|
||||
|
|
||||
import * as echarts from 'echarts'; |
|
||||
|
|
||||
export default { |
|
||||
name: "test2", |
|
||||
data() { |
|
||||
return { |
|
||||
}; |
|
||||
}, |
|
||||
mounted() { |
|
||||
var base = +new Date("2021-02-02 00:00:00"); |
|
||||
var oneDay = 24 * 3600 * 1000; |
|
||||
|
|
||||
var data = [[base, 10]]; |
|
||||
|
|
||||
for (var i = 1; i < 24; i++) { |
|
||||
var now = new Date(base += oneDay); |
|
||||
data.push([ |
|
||||
new Date("2021-02-02 " + i+":00:00"), 10 |
|
||||
]); |
|
||||
} |
|
||||
// 基于准备好的dom,初始化echarts实例 |
|
||||
var myChart = echarts.init(document.getElementById('timeQuery')); |
|
||||
let option = { |
|
||||
|
|
||||
toolbox: { |
|
||||
feature: { |
|
||||
dataZoom: { |
|
||||
yAxisIndex: 'none' |
|
||||
}, |
|
||||
restore: {}, |
|
||||
saveAsImage: {} |
|
||||
} |
|
||||
}, |
|
||||
xAxis: { |
|
||||
type: 'time', |
|
||||
boundaryGap: false |
|
||||
}, |
|
||||
yAxis: { |
|
||||
type: 'value', |
|
||||
show: false, |
|
||||
splitLine:{show: false}, //去除网格线 |
|
||||
boundaryGap: [0, '100%'] |
|
||||
}, |
|
||||
dataZoom: [{ |
|
||||
type: 'inside', |
|
||||
start: 0, |
|
||||
end: 20 |
|
||||
}, { |
|
||||
start: 0, |
|
||||
end: 20 |
|
||||
}], |
|
||||
series: [ |
|
||||
{ |
|
||||
name: '模拟数据', |
|
||||
type: 'line', |
|
||||
smooth: false, |
|
||||
symbol: 'none', |
|
||||
areaStyle: {}, |
|
||||
data: data |
|
||||
} |
|
||||
] |
|
||||
}; |
|
||||
// 绘制图表 |
|
||||
myChart.setOption(option); |
|
||||
}, |
|
||||
methods:{ |
|
||||
getTimeNode(){ |
|
||||
let mine = 20 |
|
||||
let width = document.getElementById("timeQuery").offsetWidth |
|
||||
if (width/20 > 24){ |
|
||||
return 24 |
|
||||
}else if (width/20 > 12) { |
|
||||
return 12 |
|
||||
}else if (width/20 > 6) { |
|
||||
return 6 |
|
||||
} |
|
||||
}, |
|
||||
hoveEvent(event){ |
|
||||
console.log(2222222) |
|
||||
console.log(event) |
|
||||
}, |
|
||||
timeChoose(event){ |
|
||||
console.log(event) |
|
||||
}, |
|
||||
getDataWidth(item){ |
|
||||
let startTime = new Date(item.startTime); |
|
||||
let endTime = new Date(item.endTime); |
|
||||
let result = parseFloat((endTime.getTime() - startTime.getTime())/(24*60*60*10)) |
|
||||
// console.log(result) |
|
||||
return parseFloat((endTime.getTime() - startTime.getTime())/(24*60*60*10)) |
|
||||
}, |
|
||||
getDataLeft(item){ |
|
||||
let startTime = new Date(item.startTime); |
|
||||
let differenceTime = startTime.getTime() - new Date(item.startTime.substr(0,10) + " 00:00:00").getTime() |
|
||||
let result = differenceTime/(24*60*60*10) |
|
||||
console.log(differenceTime) |
|
||||
console.log(result) |
|
||||
return parseFloat(differenceTime/(24*60*60*10)); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
</script> |
|
||||
|
|
||||
<style scoped> |
|
||||
.timeQuery{ |
|
||||
width: 96%; |
|
||||
margin-left: 2%; |
|
||||
margin-right: 2%; |
|
||||
margin-top: 20%; |
|
||||
position: absolute; |
|
||||
} |
|
||||
.timeQuery-background{ |
|
||||
height: 16px; |
|
||||
width: 100%; |
|
||||
background-color: #ececec; |
|
||||
position: absolute; |
|
||||
left: 0; |
|
||||
top: 0; |
|
||||
z-index: 10; |
|
||||
box-shadow: #9d9d9d 0px 0px 10px inset; |
|
||||
} |
|
||||
.timeQuery-data{ |
|
||||
height: 16px; |
|
||||
width: 100%; |
|
||||
position: absolute; |
|
||||
left: 0; |
|
||||
top: 0; |
|
||||
z-index: 11; |
|
||||
} |
|
||||
.timeQuery-data-cell{ |
|
||||
height: 10px; |
|
||||
background-color: #888787; |
|
||||
position: absolute; |
|
||||
z-index: 11; |
|
||||
-webkit-box-shadow: #9d9d9d 0px 0px 10px inset; |
|
||||
margin-top: 3px; |
|
||||
} |
|
||||
.timeQuery-label{ |
|
||||
height: 16px; |
|
||||
width: 100%; |
|
||||
position: absolute; |
|
||||
pointer-events: none; |
|
||||
left: 0; |
|
||||
top: 0; |
|
||||
z-index: 11; |
|
||||
} |
|
||||
.timeQuery-label-cell{ |
|
||||
height: 16px; |
|
||||
position: absolute; |
|
||||
z-index: 12; |
|
||||
width: 0px; |
|
||||
border-right: 1px solid #b7b7b7; |
|
||||
} |
|
||||
.timeQuery-label-cell-label { |
|
||||
width: 23px; |
|
||||
text-align: center; |
|
||||
height: 18px; |
|
||||
margin-left: -10px; |
|
||||
margin-top: -30px; |
|
||||
color: #444; |
|
||||
} |
|
||||
.timeQuery-pointer{ |
|
||||
width: 0px; |
|
||||
height: 18px; |
|
||||
position: absolute; |
|
||||
left: 0; |
|
||||
} |
|
||||
.timeQuery-pointer-content{ |
|
||||
width: 0px; |
|
||||
height: 16px; |
|
||||
position: absolute; |
|
||||
border-right: 3px solid #f60303; |
|
||||
z-index: 14; |
|
||||
} |
|
||||
/*.timeQuery-cell:after{*/ |
|
||||
/* content: "";*/ |
|
||||
/* height: 14px;*/ |
|
||||
/* border: 1px solid #e70303;*/ |
|
||||
/* position: absolute;*/ |
|
||||
/*}*/ |
|
||||
</style> |
|
Loading…
Reference in new issue