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