CloudRecordDetail.vue 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674
  1. <template>
  2. <div id="recordDetail" style="width: 100%">
  3. <div class="page-header" style="margin-bottom: 0">
  4. <div class="page-title">
  5. <el-page-header @back="backToList" content="云端录像"></el-page-header>
  6. </div>
  7. <div class="page-header-btn" v-if="!this.$route.params.mediaServerId" style="padding-right: 1rem">
  8. <!-- 节点选择:-->
  9. <!-- <el-select size="mini" @change="chooseMediaChange" style="width: 16rem; margin-right: 1rem;" v-model="mediaServerId" placeholder="请选择" >-->
  10. <!-- <el-option-->
  11. <!-- key="undefined"-->
  12. <!-- label="全部"-->
  13. <!-- value="undefined">-->
  14. <!-- </el-option>-->
  15. <!-- <el-option-->
  16. <!-- v-for="item in mediaServerList"-->
  17. <!-- :key="item"-->
  18. <!-- :label="item"-->
  19. <!-- :value="item">-->
  20. <!-- </el-option>-->
  21. <!-- </el-select>-->
  22. <b>节点:</b> {{ mediaServerId }}
  23. </div>
  24. <div v-if="this.$route.params.mediaServerId" style="margin-right: 1rem;">
  25. <span>流媒体:{{ this.$route.params.mediaServerId }}</span>
  26. </div>
  27. </div>
  28. <el-container>
  29. <el-aside width="260px">
  30. <div class="record-list-box-box">
  31. <div style="margin-top: 20px">
  32. <el-date-picker size="mini" v-model="chooseDate" :picker-options="pickerOptions" type="date"
  33. value-format="yyyy-MM-dd" placeholder="日期" @change="dateChange()"></el-date-picker>
  34. <!-- <el-button :disabled="!mediaServerId" size="mini" type="primary" icon="fa fa-cloud-download" style="margin: auto; margin-left: 12px " title="裁剪合并" @click="drawerOpen"></el-button>-->
  35. </div>
  36. <div class="record-list-box" :style="recordListStyle">
  37. <ul v-if="detailFiles.length >0" class="infinite-list record-list" v-infinite-scroll="infiniteScroll" >
  38. <li v-for="(item,index) in detailFiles" :key="index" class="infinite-list-item record-list-item" >
  39. <el-tag v-if="choosedFile !== item.fileName" @click="chooseFile(item)">
  40. <i class="el-icon-video-camera" ></i>
  41. {{ getFileShowName(item) }}
  42. </el-tag>
  43. <el-tag type="danger" v-if="choosedFile === item.fileName">
  44. <i class="el-icon-video-camera" ></i>
  45. {{ getFileShowName(item) }}
  46. </el-tag>
  47. <a class="el-icon-download" style="color: #409EFF;font-weight: 600;margin-left: 10px;"
  48. :href="`${getFileBasePath(item)}/download.html?url=download/${app}/${stream}/${chooseDate}/${item.fileName}`"
  49. target="_blank"/>
  50. </li>
  51. </ul>
  52. </div>
  53. <div v-if="detailFiles.length === 0" class="record-list-no-val">暂无数据</div>
  54. </div>
  55. </el-aside>
  56. <el-main style="padding: 22px">
  57. <div class="playBox" :style="playerStyle">
  58. <player ref="recordVideoPlayer" :videoUrl="videoUrl" :height="true" style="width: 100%" ></player>
  59. </div>
  60. <div class="player-option-box" >
  61. <el-slider
  62. class="playtime-slider"
  63. v-model="playTime"
  64. id="playtimeSlider"
  65. :disabled="detailFiles.length === 0"
  66. :min="sliderMIn"
  67. :max="sliderMax"
  68. :format-tooltip="playTimeFormat"
  69. @change="playTimeChange"
  70. :marks="playTimeSliderMarks">
  71. </el-slider>
  72. <div class="slider-val-box">
  73. <div class="slider-val" v-for="(item,index) of detailFiles" :key="index" :style="'width:' + getDataWidth(item) + '%; left:' + getDataLeft(item) + '%'"></div>
  74. </div>
  75. </div>
  76. </el-main>
  77. </el-container>
  78. <el-drawer
  79. title="录像下载"
  80. :visible.sync="drawer"
  81. :direction="direction"
  82. :before-close="drawerClose">
  83. <div class="drawer-box">
  84. <el-button icon="el-icon-plus" size="mini" type="primary" @click="addTask"></el-button>
  85. <el-tabs type="border-card" style="height: 100%" v-model="tabVal" @tab-click="tabClick">
  86. <el-tab-pane name="running">
  87. <span slot="label"><i class="el-icon-scissors"></i>进行中</span>
  88. <ul class="task-list">
  89. <li class="task-list-item" v-for="(item,index) in taskListForRuning" :key="index">
  90. <div class="task-list-item-box">
  91. <span>{{ item.startTime.substr(10) }}-{{item.endTime.substr(10)}}</span>
  92. <el-progress :percentage="(parseFloat(item.percentage)*100).toFixed(1)"></el-progress>
  93. </div>
  94. </li>
  95. </ul>
  96. </el-tab-pane>
  97. <el-tab-pane name="ended">
  98. <span slot="label"><i class="el-icon-finished"></i>已完成</span>
  99. <ul class="task-list">
  100. <li class="task-list-item" v-for="(item, index) in taskListEnded" :key="index">
  101. <div class="task-list-item-box" style="height: 2rem;line-height: 2rem;">
  102. <span>{{ item.startTime.substr(10) }}-{{item.endTime.substr(10)}}</span>
  103. <a class="el-icon-download download-btn" :href="getFileBasePath() + '/download.html?url=download/' "
  104. target="_blank">
  105. </a>
  106. </div>
  107. </li>
  108. </ul>
  109. </el-tab-pane>
  110. </el-tabs>
  111. </div>
  112. </el-drawer>
  113. <el-dialog title="选择时间段" :visible.sync="showTaskBox">
  114. <el-date-picker
  115. type="datetimerange"
  116. v-model="taskTimeRange"
  117. range-separator="至"
  118. start-placeholder="开始时间"
  119. end-placeholder="结束时间"
  120. format="HH:mm:ss"
  121. placeholder="选择时间范围">
  122. </el-date-picker>
  123. <el-button size="mini" type="primary" @click="addTaskToServer">确认</el-button>
  124. </el-dialog>
  125. </div>
  126. </template>
  127. <script>
  128. // TODO 根据查询的时间列表设置滑轨的最大值与最小值,
  129. import uiHeader from '../layout/UiHeader.vue'
  130. import player from './common/easyPlayer.vue'
  131. import moment from 'moment'
  132. import axios from "axios";
  133. export default {
  134. name: 'app',
  135. components: {
  136. uiHeader, player
  137. },
  138. // props: [ 'mediaServerId',],
  139. data() {
  140. return {
  141. app: this.$route.params.app,
  142. stream: this.$route.params.stream,
  143. mediaServerId: this.$route.params.mediaServerId,
  144. dateFilesObj: [],
  145. mediaServerList: [],
  146. detailFiles: [],
  147. loading: false,
  148. chooseDate: null,
  149. videoUrl: null,
  150. choosedFile: null,
  151. queryDate: new Date(),
  152. currentPage: 1,
  153. count: 1000000, // TODO 分页导致滑轨视频有效值无法获取完全
  154. total: 0,
  155. direction: "ltr",
  156. drawer: false,
  157. showTaskBox: false,
  158. taskTimeRange: [],
  159. taskListEnded: [],
  160. taskListForRuning: [],
  161. sliderMIn: 0,
  162. sliderMax: 86400,
  163. autoPlay: true,
  164. taskUpdate: null,
  165. tabVal: "running",
  166. recordListStyle: {
  167. height: this.winHeight + "px",
  168. overflow: "auto",
  169. margin: "10px auto 10px auto"
  170. },
  171. playerStyle: {
  172. "margin": "auto",
  173. "margin-bottom": "20px",
  174. "height": this.winHeight + "px",
  175. },
  176. timeFormat:'00:00:00',
  177. winHeight: window.innerHeight - 240,
  178. playTime: 0,
  179. playTimeSliderMarks: {
  180. 0: "00:00",
  181. 3600: "01:00",
  182. 7200: "02:00",
  183. 10800: "03:00",
  184. 14400: "04:00",
  185. 18000: "05:00",
  186. 21600: "06:00",
  187. 25200: "07:00",
  188. 28800: "08:00",
  189. 32400: "09:00",
  190. 36000: "10:00",
  191. 39600: "11:00",
  192. 43200: "12:00",
  193. 46800: "13:00",
  194. 50400: "14:00",
  195. 54000: "15:00",
  196. 57600: "16:00",
  197. 61200: "17:00",
  198. 64800: "18:00",
  199. 68400: "19:00",
  200. 72000: "20:00",
  201. 75600: "21:00",
  202. 79200: "22:00",
  203. 82800: "23:00",
  204. 86400: "24:00",
  205. },
  206. pickerOptions:{
  207. cellClassName:(date) =>{
  208. // 通过显示一个点标识这一天有录像
  209. let time = moment(date).format('YYYY-MM-DD')
  210. if (this.dateFilesObj[time]){
  211. return "data-picker-true"
  212. }else {
  213. return "data-picker-false"
  214. }
  215. }
  216. }
  217. };
  218. },
  219. computed: {
  220. },
  221. mounted() {
  222. this.recordListStyle.height = this.winHeight + "px";
  223. this.playerStyle["height"] = this.winHeight + "px";
  224. console.log(this.app)
  225. console.log(this.stream)
  226. console.log(this.mediaServerId)
  227. // 查询当年有视频的日期
  228. this.getDateInYear(()=>{
  229. if (Object.values(this.dateFilesObj).length > 0){
  230. this.chooseDate = Object.values(this.dateFilesObj)[Object.values(this.dateFilesObj).length -1];
  231. this.dateChange();
  232. }
  233. })
  234. },
  235. destroyed() {
  236. this.$destroy('recordVideoPlayer');
  237. },
  238. methods: {
  239. dateChange(){
  240. this.playTime = 0;
  241. this.detailFiles = [];
  242. this.currentPage = 1;
  243. this.sliderMIn= 0;
  244. this.sliderMax= 86400;
  245. let chooseFullDate = new Date(this.chooseDate +" " + this.timeFormat);
  246. if (chooseFullDate.getFullYear() !== this.queryDate.getFullYear()
  247. || chooseFullDate.getMonth() !== this.queryDate.getMonth()){
  248. this.queryDate = chooseFullDate;
  249. this.getDateInYear()
  250. }
  251. this.queryRecordDetails(()=>{
  252. if (this.detailFiles.length > 0){
  253. console.log(this.detailFiles)
  254. let timeForFile = this.getTimeForFile(this.detailFiles[0]);
  255. let lastTimeForFile = this.getTimeForFile(this.detailFiles[this.detailFiles.length - 1]);
  256. let timeNum = timeForFile[0].getTime() - new Date(this.chooseDate + " " + this.timeFormat).getTime()
  257. console.log(timeNum)
  258. let lastTimeNum = lastTimeForFile[1].getTime() - new Date(this.chooseDate + " " + this.timeFormat).getTime()
  259. this.playTime = parseInt(timeNum/1000)
  260. this.sliderMIn = parseInt(timeNum/1000 - timeNum/1000%(60*60))
  261. console.log(this.sliderMIn )
  262. this.sliderMax = parseInt(lastTimeNum/1000 - lastTimeNum/1000%(60*60)) + 60*60
  263. console.log(this.sliderMax )
  264. }
  265. });
  266. },
  267. infiniteScroll(){
  268. if (this.total > this.detailFiles.length) {
  269. this.currentPage ++;
  270. this.queryRecordDetails();
  271. }
  272. },
  273. queryRecordDetails: function (callback){
  274. this.$axios({
  275. method: 'get',
  276. url: `/api/cloud/record/list`,
  277. params: {
  278. app: this.app,
  279. stream: this.stream,
  280. startTime: this.chooseDate + " 00:00:00",
  281. endTime: this.chooseDate + " 23:59:59",
  282. page: this.currentPage,
  283. count: this.count,
  284. mediaServerId: this.mediaServerId
  285. }
  286. }).then((res) => {
  287. if (res.data.code === 0) {
  288. this.total = res.data.data.total;
  289. this.detailFiles = this.detailFiles.concat(res.data.data.list);
  290. let temp = new Set()
  291. for (let i = 0; i < this.detailFiles.length; i++) {
  292. temp.add(this.detailFiles[i].mediaServerId)
  293. }
  294. this.mediaServerList = Array.from(temp)
  295. if (this.mediaServerList.length === 1) {
  296. this.mediaServerId = this.mediaServerList[0]
  297. }
  298. }
  299. this.loading = false;
  300. if (callback) callback();
  301. }).catch((error) => {
  302. console.log(error);
  303. this.loading = false;
  304. });
  305. },
  306. chooseFile(file){
  307. if (file == null) {
  308. this.videoUrl = "";
  309. this.choosedFile = "";
  310. }else {
  311. this.choosedFile = file.fileName;
  312. this.videoUrl = `${this.getFileBasePath(file)}/download/${this.app}/${this.stream}/${this.chooseDate}/${file.fileName}`
  313. console.log(this.videoUrl)
  314. }
  315. },
  316. backToList() {
  317. this.$router.back()
  318. },
  319. getFileShowName(item) {
  320. return moment.unix(item.startTime).format('HH:mm:ss') + "-" + moment.unix(item.endTime).format('HH:mm:ss')
  321. },
  322. chooseMediaChange() {
  323. },
  324. getRecordList() {
  325. },
  326. getFileBasePath(item) {
  327. let basePath = ""
  328. if (axios.defaults.baseURL.startsWith("http")) {
  329. basePath = `${axios.defaults.baseURL}/record_proxy/${item.mediaServerId}`
  330. }else {
  331. basePath = `${window.location.origin}${axios.defaults.baseURL}/record_proxy/${item.mediaServerId}`
  332. }
  333. return basePath;
  334. },
  335. getDataWidth(item){
  336. let timeForFile = this.getTimeForFile(item);
  337. let result = (timeForFile[2])/((this.sliderMax - this.sliderMIn)*1000)
  338. return result*100
  339. },
  340. getDataLeft(item){
  341. let timeForFile = this.getTimeForFile(item);
  342. let differenceTime = timeForFile[0].getTime() - new Date(this.chooseDate + " " + this.timeFormat).getTime()
  343. return parseFloat((differenceTime - this.sliderMIn * 1000)/((this.sliderMax - this.sliderMIn)*1000))*100 ;
  344. },
  345. playTimeChange(val){
  346. let minTime = this.getTimeForFile(this.detailFiles[0])[0]
  347. let maxTime = this.getTimeForFile(this.detailFiles[this.detailFiles.length - 1])[1];
  348. this.chooseFile(null);
  349. let timeMilli = new Date(this.chooseDate + " " + this.timeFormat).getTime() + val*1000
  350. if (timeMilli >= minTime.getTime() && timeMilli <= maxTime.getTime()){
  351. for (let i = 0; i < this.detailFiles.length; i++) {
  352. let timeForFile = this.getTimeForFile(this.detailFiles[i]);
  353. if (timeMilli >= timeForFile[0].getTime() && timeMilli <= timeForFile[1].getTime()){
  354. // TODO 当前未按照实际时间偏移,仅仅是找到对应的文静播放
  355. this.chooseFile(this.detailFiles[i])
  356. return;
  357. }
  358. }
  359. }
  360. },
  361. getTimeForFile(file){
  362. console.log(file)
  363. let starTime = new Date(file.startTime * 1000);
  364. let endTime = new Date(file.endTime * 1000);
  365. if(this.checkIsOver24h(starTime,endTime)){
  366. endTime = new Date(this.chooseDate + " " + "23:59:59");
  367. }
  368. return [starTime, endTime, endTime.getTime() - starTime.getTime()];
  369. },
  370. checkIsOver24h(starTime,endTime){
  371. return starTime > endTime;
  372. },
  373. playTimeFormat(val){
  374. let h = parseInt(val/3600);
  375. let m = parseInt((val - h*3600)/60);
  376. let s = parseInt(val - h*3600 - m*60);
  377. let hStr = h;
  378. let mStr = m;
  379. let sStr = s;
  380. if (h < 10) {
  381. hStr = "0" + hStr;
  382. }
  383. if (m < 10) {
  384. mStr = "0" + mStr;s
  385. }
  386. if (s < 10) {
  387. sStr = "0" + sStr;
  388. }
  389. return hStr + ":" + mStr + ":" + sStr
  390. },
  391. deleteRecord(){
  392. // TODO
  393. let that = this;
  394. this.$axios({
  395. method: 'delete',
  396. url:`/record_proxy/${that.mediaServerId}/api/record/delete`,
  397. params: {
  398. page: that.currentPage,
  399. count: that.count
  400. }
  401. }).then(function (res) {
  402. if (res.data.code === 0) {
  403. that.total = res.data.data.total;
  404. that.recordList = res.data.data.list;
  405. }
  406. }).catch(function (error) {
  407. console.log(error);
  408. });
  409. },
  410. getDateInYear(callback){
  411. this.dateFilesObj = {};
  412. this.$axios({
  413. method: 'get',
  414. url: `/api/cloud/record/date/list`,
  415. params: {
  416. app: this.app,
  417. stream: this.stream,
  418. year: this.queryDate.getFullYear(),
  419. month: this.queryDate.getMonth() + 1,
  420. mediaServerId: this.mediaServerId,
  421. }
  422. }).then((res) => {
  423. console.log(res)
  424. if (res.data.code === 0) {
  425. if (res.data.data.length > 0) {
  426. for (let i = 0; i < res.data.data.length; i++) {
  427. this.dateFilesObj[res.data.data[i]] = res.data.data[i]
  428. }
  429. console.log(this.dateFilesObj)
  430. }
  431. }
  432. if(callback)callback();
  433. }).catch((error) => {
  434. console.log(error);
  435. });
  436. },
  437. tabClick(){
  438. this.getTaskList(this.tabVal === "ended")
  439. },
  440. drawerClose(){
  441. this.drawer = false;
  442. if (this.taskUpdate != null) {
  443. window.clearInterval(this.taskUpdate)
  444. }
  445. },
  446. drawerOpen(){
  447. this.drawer = true;
  448. if (this.taskUpdate != null) {
  449. window.clearInterval(this.taskUpdate)
  450. }
  451. this.taskUpdate = setInterval(()=>{
  452. this.getTaskList(this.tabVal === "ended")
  453. }, 1000)
  454. },
  455. addTask(){
  456. this.showTaskBox = true;
  457. let startTimeStr = this.chooseDate + " " + this.detailFiles[0].fileName.substring(0, 8);
  458. let endTimeStr = this.chooseDate + " " + this.detailFiles[this.detailFiles.length - 1].fileName.substring(9, 17);
  459. this.taskTimeRange[0] = new Date(startTimeStr)
  460. this.taskTimeRange[1] = new Date(endTimeStr)
  461. },
  462. addTaskToServer(){
  463. let that = this;
  464. this.$axios({
  465. method: 'get',
  466. url:`/api/cloud/record/task/add`,
  467. params: {
  468. app: this.app,
  469. stream: this.stream,
  470. mediaServerId: this.mediaServerId,
  471. startTime: moment(this.taskTimeRange[0]).format('YYYY-MM-DD HH:mm:ss'),
  472. endTime: moment(this.taskTimeRange[1]).format('YYYY-MM-DD HH:mm:ss'),
  473. }
  474. }).then(function (res) {
  475. if (res.data.code === 0 ) {
  476. that.showTaskBox = false
  477. that.getTaskList(false);
  478. }else {
  479. that.$message.error(res.data.msg);
  480. }
  481. }).catch(function (error) {
  482. console.log(error);
  483. });
  484. },
  485. handleTabClick() {
  486. this.getTaskList(this.tabVal === "ended")
  487. },
  488. getTaskList(isEnd){
  489. let that = this;
  490. this.$axios({
  491. method: 'get',
  492. url:`/api/cloud/record/task/list`,
  493. params: {
  494. mediaServerId: this.mediaServerId,
  495. isEnd: isEnd,
  496. }
  497. }).then(function (res) {
  498. if (res.data.code === 0) {
  499. if (isEnd){
  500. that.taskListEnded = res.data.data;
  501. }else {
  502. that.taskListForRuning = res.data.data;
  503. }
  504. }
  505. }).catch(function (error) {
  506. console.log(error);
  507. });
  508. },
  509. goBack(){
  510. this.$router.push('/cloudRecord');
  511. }
  512. }
  513. };
  514. </script>
  515. <style>
  516. .el-slider__runway {
  517. background-color:rgba(206, 206, 206, 0.47) !important;
  518. }
  519. .el-slider__bar {
  520. background-color: rgba(153, 153, 153, 0) !important;
  521. }
  522. .playtime-slider {
  523. position: relative;
  524. z-index: 100;
  525. }
  526. .data-picker-true{
  527. }
  528. .data-picker-true:after{
  529. content: "";
  530. position: absolute;
  531. width: 4px;
  532. height: 4px;
  533. background-color: #606060;
  534. border-radius: 4px;
  535. left: 45%;
  536. top: 74%;
  537. }
  538. .data-picker-false{
  539. }
  540. .slider-val-box{
  541. height: 6px;
  542. position: relative;
  543. top: -22px;
  544. }
  545. .slider-val{
  546. height: 6px;
  547. background-color: #007CFF;
  548. position: absolute;
  549. }
  550. .record-list-box-box{
  551. width: 250px;
  552. float: left;
  553. }
  554. .record-list-box{
  555. overflow: auto;
  556. width: 220px;
  557. list-style: none;
  558. padding: 0;
  559. margin: 0;
  560. margin-top: 0px;
  561. padding: 1rem 0;
  562. background-color: #FFF;
  563. margin-top: 10px;
  564. }
  565. .record-list{
  566. list-style: none;
  567. padding: 0;
  568. margin: 0;
  569. background-color: #FFF;
  570. }
  571. .record-list-no-val {
  572. position: absolute;
  573. color: #9f9f9f;
  574. top: 50%;
  575. left: 110px;
  576. }
  577. .record-list-item{
  578. padding: 0;
  579. margin: 0;
  580. margin: 0.5rem 0;
  581. cursor: pointer;
  582. }
  583. .record-list-option {
  584. width: 10px;
  585. float: left;
  586. margin-top: 39px;
  587. }
  588. .drawer-box{
  589. height: 100%;
  590. }
  591. .task-list{
  592. list-style: none;
  593. padding: 0;
  594. margin: 0;
  595. background-color: #FFF;
  596. }
  597. .task-list-item{
  598. padding: 0;
  599. margin: 0;
  600. margin: 1.5rem 0;
  601. }
  602. .task-list-item-box{
  603. text-align: left;
  604. font-size: 13px;
  605. color: #555;
  606. }
  607. .download-btn{
  608. display: inline-block;
  609. line-height: 1;
  610. white-space: nowrap;
  611. cursor: pointer;
  612. background: #FFF;
  613. background-color: rgb(255, 255, 255);
  614. border: 1px solid #DCDFE6;
  615. border-top-color: rgb(220, 223, 230);
  616. border-right-color: rgb(220, 223, 230);
  617. border-bottom-color: rgb(220, 223, 230);
  618. border-left-color: rgb(220, 223, 230);
  619. border-top-color: rgb(220, 223, 230);
  620. border-right-color: rgb(220, 223, 230);
  621. border-bottom-color: rgb(220, 223, 230);
  622. border-left-color: rgb(220, 223, 230);
  623. -webkit-appearance: none;
  624. text-align: center;
  625. -webkit-box-sizing: border-box;
  626. box-sizing: border-box;
  627. outline: 0;
  628. margin: 0;
  629. -webkit-transition: .1s;
  630. transition: .1s;
  631. font-weight: 500;
  632. padding: 7px 14px;
  633. font-size: 0.875rem;
  634. border-radius: 4px;
  635. font-size: 0.75rem;
  636. border-radius: 3px;
  637. color: #FFF;
  638. background-color: #409EFF;
  639. border-color: #409EFF;
  640. float: right;
  641. }
  642. .download-btn:hover{
  643. background: #66b1ff;
  644. border-color: #66b1ff;
  645. color: #FFF;
  646. }
  647. .time-box{
  648. }
  649. </style>