Sfoglia il codice sorgente

添加iot三个页面

凉纪 1 anno fa
parent
commit
9cb0ee3439

BIN
src/assets/images/header.png


+ 2 - 4
src/components/waterCom.vue

@@ -44,8 +44,8 @@ export default {
       let text = this.userName;
       let orgName = this.$store.getters.orgShortName;
       const canvas = document.getElementById("canvas"); //获取canvas
-      canvas.width = 600; //设置画布宽度
-      canvas.height = 600; //设置画布高度
+      canvas.width = 300; //设置画布宽度
+      canvas.height = 300; //设置画布高度
       canvas.style.display = "none"; //隐藏画布本身
       //加层级权重
 
@@ -54,8 +54,6 @@ export default {
       //   设置文字大小
       ctx.fillStyle = "rgba(0,0,0,.1)"; //设置文字颜色及透明度
       ctx.rotate(-0.4); //设置文字旋转角度
-      
-
       ctx.fillText(`${text}${orgName}`, -20,180,); //设置显示文字内容
       const img = canvas.toDataURL("image/png"); //参数默认为 image/png,可以是其他image/jpeg等,该方法返回值是一个url,是base64组成的图片的源数据、可以直接赋值给图片的src属性
       const style = `background-image:url(${img});z-index:100000;display:none;`; //定义样式

+ 2 - 2
src/layout/index.vue

@@ -2,7 +2,7 @@
   <div :class="classObj" class="app-wrapper" :style="{'--current-color': theme}">
     <div v-if="device==='mobile'&&sidebar.opened" class="drawer-bg" @click="handleClickOutside"/>
     <sidebar v-if="!sidebar.hide" class="sidebar-container"/>
-   
+
     <div :class="{hasTagsView:needTagsView,sidebarHide:sidebar.hide}" class="main-container">
       <div :class="{'fixed-header':fixedHeader}">
         <navbar/>
@@ -28,7 +28,7 @@ export default {
     AppMain,
     Navbar,
     RightPanel,
-   
+
     Settings,
     Sidebar,
     TagsView

+ 144 - 0
src/views/iot/videoDay/dialog.detail.vue

@@ -0,0 +1,144 @@
+<template>
+  <div class="videoDaysDetail">
+    <el-dialog title="录像天数详情" :visible.sync="isShow" width="1000px" :destroy-on-close="true">
+      <el-descriptions  :column="2" border>
+        <el-descriptions-item label="组织机构" >{{data.orgName}}</el-descriptions-item>
+        <el-descriptions-item label="上报时间" >{{data.updateTime}}</el-descriptions-item>
+        <el-descriptions-item label="监控主机" >{{data.equipmentName}}</el-descriptions-item>
+        <el-descriptions-item label="摄像头" >{{data.channelName}}</el-descriptions-item>
+        <el-descriptions-item label="通道号" >{{data.channelCode}}</el-descriptions-item>
+        <el-descriptions-item label="计划存储天数" >{{data.planDays}}</el-descriptions-item>
+        <el-descriptions-item label="实际存储天数" >{{data.realDays}}</el-descriptions-item>
+        <el-descriptions-item label="计划录像开始时间" >{{data.planStartTime}}</el-descriptions-item>
+        <el-descriptions-item label="计划录像结束时间" >{{data.planEndTime}}</el-descriptions-item>
+      </el-descriptions>
+
+      <div class="time-line">
+        <el-timeline :reverse="false">
+          <el-timeline-item v-for="(activity, index) in data.loseDateList" :key="index" color="rgb(208 230 253)"
+                            size="large" type="primary">
+            <div class="time">
+              <div>
+                      <span class="year">
+                        {{ activity.month }}
+                      </span>
+              </div>
+              <!-- <div class="day">{{ activity.time.substring(0, 4) }}年</div> -->
+            </div>
+            <div class="ml10">
+
+              <el-tag v-for="(date, number) in activity.lostDates" :key="number">
+                <div>{{ date }}
+                  <div class="reTip">
+                  </div>
+                </div>
+              </el-tag>
+            </div>
+          </el-timeline-item>
+        </el-timeline>
+      </div>
+
+      <div slot="footer" class="dialog-footer">
+        <el-button @click="onHide">关闭</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+<script>
+
+export default {
+  data() {
+    return {
+      isShow: false,
+      data: {},
+      id: null,
+    };
+  },
+  methods: {
+    async show(id) {
+      this.id = id;
+      //this.data = await this.$api.videoDaysCheck.one(id);
+      this.isShow = true;
+    },
+    onHide() {
+      this.isShow = false;
+    },
+  },
+};
+</script>
+
+<style lang="scss">
+.videoDaysDetail {
+  .block {
+    display: block;
+  }
+
+  .time-line {
+    margin-top: 10px;
+    margin-left: 80px;
+  }
+
+  .list-title {
+    font-size: 16px;
+    font-family: PingFangSC-Medium, PingFang SC;
+    font-weight: 500;
+    color: #181b1e;
+  }
+
+  .list-company {
+    font-size: 14px;
+    font-family: PingFangSC-Regular, PingFang SC;
+    font-weight: 400;
+    color: #2991ff;
+    margin-top: 15px;
+    margin-bottom: 15px;
+  }
+
+  .list-desc {
+    font-size: 14px;
+    font-family: PingFangSC-Regular, PingFang SC;
+    font-weight: 400;
+    color: #596878;
+  }
+
+  .el-tag--small {
+    margin-bottom: 10px;
+  }
+
+  .reTip {
+    display: block;
+    background: #f00;
+    border-radius: 50%;
+    width: 4px;
+    height: 4px;
+    top: -21px;
+    right: -26px;
+    position: relative;
+    z-index: 4;
+  }
+
+  //左侧时间
+  .time {
+    color: rgb(181 215 251);
+    position: absolute;
+    left: -35px;
+    top: -1px;
+
+    .year {
+      font-size: 14px;
+      font-family: PingFangSC-Regular, PingFang SC;
+      font-weight: 400;
+      color: #20354a;
+    }
+
+    .day {
+      font-size: 14px;
+      font-family: PingFangSC-Regular, PingFang SC;
+      font-weight: 400;
+      color: #596878;
+      text-align: center;
+      margin-top: 10px;
+    }
+  }
+}
+</style>

+ 190 - 0
src/views/iot/videoDay/index.vue

@@ -0,0 +1,190 @@
+<template>
+  <div class="app-container">
+    <div class="main-right-box">
+      <div class="main-search-box">
+        <el-form v-show="showSearch" ref="queryForm" :inline="true" :model="queryParams" size="small">
+          <el-form-item label="机构名称">
+            <org-tree
+              v-model="queryParams.orgId"
+              @defaultKey="getDefaultKey"
+              @checkChange="changeCheckBox"
+              @click="handleNodeClick"
+              :showCheckSub="false"
+              ref="orgTree"
+            ></org-tree>
+          </el-form-item>
+          <el-form-item label="监控主机" prop="reasons">
+            <el-input
+              v-model="queryParams.reasons"
+              clearable
+              placeholder="请输入关键字"
+              @keyup.enter.native="handleQuery"
+            />
+          </el-form-item>
+          <el-form-item label="摄像头" prop="type">
+            <el-select v-model="queryParams.type" clearable placeholder="请选择">
+              <el-option
+                v-for="dict in dict.type.out_in_type"
+                :key="dict.value"
+                :label="dict.label"
+                :value="dict.value"
+              />
+            </el-select>
+          </el-form-item>
+          <el-form-item label="上报时间">
+            <DataRangePicker
+              style="width: 240px"
+              v-model="queryParams.dateRange"
+              key="daterange"
+              type="daterange"
+              :editable="false"
+              :clearable="false"
+              range-separator="-"
+              start-placeholder="开始日期"
+              end-placeholder="结束日期"
+            />
+          </el-form-item>
+          <el-form-item label="丢失日期">
+              <el-date-picker
+                v-model="queryParams.date"
+                type="month"
+                value-format="yyyy-MM-dd"
+                placeholder="请选择保存日期"
+              >
+              </el-date-picker>
+          </el-form-item>
+          <el-form-item label="天数情况" prop="type">
+            <el-select v-model="queryParams.type" clearable placeholder="请选择">
+              <el-option
+                v-for="dict in dict.type.out_in_type"
+                :key="dict.value"
+                :label="dict.label"
+                :value="dict.value"
+              />
+            </el-select>
+          </el-form-item>
+        </el-form>
+
+        <el-row :gutter="10">
+          <el-col :span="1.5">
+
+            <el-button icon="el-icon-search" size="mini" type="primary" @click="handleQuery">搜索</el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button icon="el-icon-refresh" size="mini" type="primary" @click="resetQuery">重置</el-button>
+          </el-col>
+          <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+        </el-row>
+      </div>
+      <el-table v-loading="loading" :data="dataList" border height="646" size="small">
+        <el-table-column label="序号" type="index" align="center" width="70"></el-table-column>
+        <el-table-column :show-overflow-tooltip="true" align="center" label="组织机构" prop="type" width="200"></el-table-column>
+        <el-table-column :show-overflow-tooltip="true" align="center" label="监控主机" prop="reasons"/>
+        <el-table-column align="center" label="摄像头" prop="letterNo" ></el-table-column>
+        <el-table-column align="center" label="通道号" prop="letterNo" ></el-table-column>
+        <el-table-column align="center" label="实际/计划存储天数" prop="letterNo" ></el-table-column>
+        <el-table-column align="center" label="天数状态" prop="letterNo" ></el-table-column>
+        <el-table-column align="center" label="最早录像日期" prop="letterNo" ></el-table-column>
+        <el-table-column align="center" label="上报日期" width="150">
+          <template slot-scope="scope">
+            <span>{{ scope.row.startTime }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="操作" align="center"  width="120" >
+          <template slot-scope="r">
+            <el-button type="text" @click="onDetail(r.row.id)">详情</el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <pagination
+        v-show="total>0"
+        :limit.sync="queryParams.pageSize"
+        :page.sync="queryParams.pageNum"
+        :total="total"
+        @pagination="getList"
+      />
+
+    </div>
+
+    <dialog-detail ref="DialogDetail" @success="refresh(true)"></dialog-detail>
+
+  </div>
+</template>
+
+<script>
+import OrgTree from "@/components/orgTree/orgQuerySelector.vue";
+import DataRangePicker from "@/components/dateTime/daterange.picker.vue";
+import DialogDetail from "./dialog.detail.vue";
+import {getLetterApproveList} from "@/api/core/letter";
+import dayjs from 'dayjs';
+export default {
+  components: {DataRangePicker, OrgTree,DialogDetail },
+  dicts: ['letter_status', 'out_in_type', 'out_in_approve_status'],
+  data() {
+    return {
+      loading:false,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        reasons: null,
+        type: null,
+        approveStatus: null,
+        orgId: this.$store.getters.orgId,
+      },
+      dataList:[],
+    };
+  },
+  mounted() {
+    this.getList();
+  },
+  methods: {
+    dayjs,
+    onDetail(id) {
+      this.$refs.DialogDetail.show(id);
+    },
+    refresh(isCurrent) {
+      this.$refs.st[isCurrent ? "refresh" : "search"]();
+    },
+    getDefaultKey(key) {
+      this.queryParams.orgId = key;
+      this.getList();
+    },
+    // 节点单击事件
+    handleNodeClick(data) {
+      this.queryParams.orgId = data.id;
+      this.handleQuery();
+    },
+    /** 下穿状态改变*/
+    changeCheckBox() {
+      this.queryParams.checkSub = !this.queryParams.checkSub;
+      this.getList();
+    },
+
+    /** 查询介绍信列表 */
+    getList() {
+      this.loading = true;
+      getLetterApproveList(this.queryParams).then(response => {
+        this.dataList = response.rows;
+        this.total = response.total;
+        this.loading = false;
+      });
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.resetForm("queryForm");
+      this.handleQuery();
+    },
+  }
+};
+</script>

+ 192 - 0
src/views/iot/videoIntegrity/index.vue

@@ -0,0 +1,192 @@
+<template>
+  <div class="app-container">
+    <div class="main-right-box">
+      <div class="main-search-box">
+        <el-form v-show="showSearch" ref="queryForm" :inline="true" :model="queryParams" size="small">
+          <el-form-item label="机构名称">
+            <org-tree
+              v-model="queryParams.orgId"
+              @defaultKey="getDefaultKey"
+              @checkChange="changeCheckBox"
+              @click="handleNodeClick"
+              :showCheckSub="false"
+              ref="orgTree"
+            ></org-tree>
+          </el-form-item>
+          <el-form-item label="监控主机" prop="reasons">
+            <el-input
+              v-model="queryParams.reasons"
+              clearable
+              placeholder="请输入关键字"
+              @keyup.enter.native="handleQuery"
+            />
+          </el-form-item>
+          <el-form-item label="摄像头" prop="type">
+            <el-select v-model="queryParams.type" clearable placeholder="请选择介绍信类型">
+              <el-option
+                v-for="dict in dict.type.out_in_type"
+                :key="dict.value"
+                :label="dict.label"
+                :value="dict.value"
+              />
+            </el-select>
+          </el-form-item>
+          <el-form-item label="计划存储天数" prop="approveStatus">
+            <el-select v-model="queryParams.approveStatus" clearable placeholder="请选择审批状态">
+              <el-option
+                v-for="dict in dict.type.out_in_approve_status"
+                :key="dict.value"
+                :label="dict.label"
+                :value="dict.value"
+              />
+            </el-select>
+          </el-form-item>
+          <el-form-item label="状态" prop="approveStatus">
+            <el-select v-model="queryParams.approveStatus" clearable placeholder="请选择审批状态">
+              <el-option
+                v-for="dict in dict.type.out_in_approve_status"
+                :key="dict.value"
+                :label="dict.label"
+                :value="dict.value"
+              />
+            </el-select>
+          </el-form-item>
+          <el-form-item label="检查日期">
+            <DataRangePicker
+              style="width: 240px"
+              v-model="queryParams.dateRange"
+              key="daterange"
+              type="daterange"
+              :editable="false"
+              :clearable="false"
+              range-separator="-"
+              start-placeholder="开始日期"
+              end-placeholder="结束日期"
+            />
+          </el-form-item>
+        </el-form>
+
+        <el-row :gutter="10">
+          <el-col :span="1.5">
+
+            <el-button icon="el-icon-search" size="mini" type="primary" @click="handleQuery">搜索</el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button icon="el-icon-refresh" size="mini" type="primary" @click="resetQuery">重置</el-button>
+          </el-col>
+          <!-- <el-col :span="1.5">
+            <el-button
+              type="primary"
+              icon="el-icon-plus"
+              size="mini"
+              @click="handleAdd"
+              v-hasPermi="['core:letter:add']"
+            >录入介绍信</el-button>
+          </el-col> -->
+          <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+        </el-row>
+      </div>
+      <el-table v-loading="loading" :data="dataList" border height="646" size="small">
+        <el-table-column label="序号" type="index" align="center" width="70"></el-table-column>
+        <el-table-column :show-overflow-tooltip="true" align="center" label="组织机构" prop="type" width="200"></el-table-column>
+        <el-table-column :show-overflow-tooltip="true" align="center" label="监控主机" prop="reasons"/>
+        <el-table-column align="center" label="摄像头" prop="letterNo" ></el-table-column>
+        <el-table-column align="center" label="IP地址" prop="letterNo" ></el-table-column>
+        <el-table-column align="center" label="计划存储天数" prop="letterNo" ></el-table-column>
+        <el-table-column align="center" label="检查日期" width="150">
+          <template slot-scope="scope">
+            <span>{{ scope.row.startTime }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="录像详情" ref="tabs" width="730px">
+          <template slot="header">
+            <img src="../../../assets/images/header.png" alt="" width="700px" />
+          </template>
+          <template slot-scope="r">
+<!--            <TimeLine :data="r.row" :rems="700" />-->
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <pagination
+        v-show="total>0"
+        :limit.sync="queryParams.pageSize"
+        :page.sync="queryParams.pageNum"
+        :total="total"
+        @pagination="getList"
+      />
+
+    </div>
+  </div>
+</template>
+
+<script>
+import OrgTree from "@/components/orgTree/orgQuerySelector.vue";
+import DataRangePicker from "@/components/dateTime/daterange.picker.vue";
+import TimeLine from "./timeLine.vue";
+import {getLetterApproveList} from "@/api/core/letter";
+import dayjs from 'dayjs';
+export default {
+  components: {DataRangePicker, OrgTree,TimeLine },
+  dicts: ['letter_status', 'out_in_type', 'out_in_approve_status'],
+  data() {
+    return {
+      loading:false,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        reasons: null,
+        type: null,
+        approveStatus: null,
+        orgId: this.$store.getters.orgId,
+      },
+      dataList:[],
+    };
+  },
+  mounted() {
+    this.getList();
+  },
+  methods: {
+    dayjs,
+    getDefaultKey(key) {
+      this.queryParams.orgId = key;
+      this.getList();
+    },
+    // 节点单击事件
+    handleNodeClick(data) {
+      this.queryParams.orgId = data.id;
+      this.handleQuery();
+    },
+    /** 下穿状态改变*/
+    changeCheckBox() {
+      this.queryParams.checkSub = !this.queryParams.checkSub;
+      this.getList();
+    },
+
+    /** 查询介绍信列表 */
+    getList() {
+      this.loading = true;
+      getLetterApproveList(this.queryParams).then(response => {
+        this.dataList = response.rows;
+        this.total = response.total;
+        this.loading = false;
+      });
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.resetForm("queryForm");
+      this.handleQuery();
+    },
+  }
+};
+</script>

+ 408 - 0
src/views/iot/videoIntegrity/timeLine.vue

@@ -0,0 +1,408 @@
+<template>
+  <div>
+    <div id="loseTimeSpan"
+         style="width:700px"
+         class="loseTimeSpan">
+      <i v-for="(item,index) in checkSpan"
+         :key="index + 'x'"
+         :style="{width:item.width + 'px',left:item.start + 'px'}"
+         :title="'检查模版' + item.st + '~' +  item.et"></i>
+      <i v-for="(item,index) in loseSpan"
+         :key="index + 'y'"
+         class="lose"
+         :style="{width:item.width + 'px',left:item.start + 'px'}"
+         :title="'录像丢失' + item.st + '~' +  item.et"></i>
+      <i v-for="(item,index) in warnSpan"
+         :key="index + 'z'"
+         class="warn"
+         :style="{width:item.width + 'px',left:item.start + 'px'}"
+         :title="'无录像计划' + item.st + '~' +  item.et"></i>
+    </div>
+  </div>
+</template>
+
+<script>
+import axios from 'axios'
+export default {
+  name: "Login",
+  data () {
+    return {
+      DAY_SECOND: 86400,
+      rowData: this.data,
+      checkSpan: [],
+      loseSpan: [],
+      warnSpan: [],
+      widthrem:this.rems
+    }
+  },
+  props:{
+    data:{
+    },
+    rems:{
+      }
+  },
+  watch: {
+    data(rowData) {
+      if(!rowData) return;
+      this.rowData=rowData;
+      let data = this.renderVideoRuler();
+      this.checkSpan = data.checkSpan;
+      this.loseSpan = data.loseSpan;
+      this.warnSpan = data.warnSpan;
+    }
+  },
+  methods: {
+    //计算录像模版长度
+    calculateCheckSpanUI (spans = [], widthPx) {
+      const tmpTemplates = [];
+      const tmpSpans = [];
+      spans.forEach((item) => {
+        const st = this.datestr2number(item.st);
+        const et = this.datestr2number(item.et);
+
+        //转换为秒数
+        tmpTemplates.push({
+          st: st,
+          et: et
+        })
+
+        //计算宽度
+        let width = widthPx * ((et - st) / this.DAY_SECOND);
+
+        if (width < 1) {
+          width = 1;
+        }
+
+        //计算开始位置
+        let start = widthPx * (st / this.DAY_SECOND);
+
+
+        tmpSpans.push({
+          width: width,
+          start: start,
+          st: item.st,
+          et: item.et,
+        })
+      })
+
+      return {
+        templates: tmpTemplates,
+        spans: tmpSpans
+      }
+    },
+    //计算丢失数据的信息
+    calculateWarnSpanUI (spans = [], widthPx) {
+      let warnSpans = [];
+      // console.log(spans)
+      if (spans.length > 0) {
+        let flag = "00:00:00";
+        spans.forEach((item, index) => {
+          if (index==0&&item.st > flag) {
+            const st = this.datestr2number(flag) + 1;
+            const et = this.datestr2number(item.st) - 1;
+
+            //计算宽度
+            let width = widthPx * ((et - st) / this.DAY_SECOND);
+            if (width < 1) {
+              width = 1;
+            }
+
+            //计算开始位置
+            let start = widthPx * (st / this.DAY_SECOND);
+
+            warnSpans.push({
+              width: width,
+              start: start,
+              st: flag,
+              et: item.st
+            })
+          }else if(index>0&&item.st > spans[index-1].et){
+            const st = this.datestr2number(spans[index-1].et) + 1;
+            const et = this.datestr2number(item.st) - 1;
+
+            //计算宽度
+            let width = widthPx * ((et - st) / this.DAY_SECOND);
+            if (width < 1) {
+              width = 1;
+            }
+
+            //计算开始位置
+            let start = widthPx * (st / this.DAY_SECOND);
+
+            warnSpans.push({
+              width: width,
+              start: start,
+              st: spans[index-1].et,
+              et: item.st
+            })
+          }else {
+            flag
+          }
+
+          if (index + 1 === spans.length && item.et !== "23:59:59") {
+            const st = this.datestr2number(item.et) + 1;
+            const et = this.datestr2number("23:59:59");
+
+            //计算宽度
+            let width = widthPx * ((et - st) / this.DAY_SECOND);
+            if (width < 1) {
+              width = 1;
+            }
+
+            let start = widthPx * (st / this.DAY_SECOND);
+
+            warnSpans.push({
+              width: width,
+              start: start,
+              st: item.et,
+              et: "24:00:00"
+            })
+          }
+
+        });
+      }
+      return {
+        spans: warnSpans
+      }
+    },
+
+    //判断指定的丢失片段是否存在模版范围之中
+    judgeLoseSpan (checkSpanTemplate = [], st, et) {
+      let flag = false;
+      checkSpanTemplate.forEach((val) => {
+        if (st >= val.st && et <= val.et) {
+          flag = true;
+          return flag;
+        }
+      });
+      return flag;
+    },
+    //计算丢失录像
+    calculateLoseSpanUI (spans = [], checkSpanTemplate = [], widthPx) {
+
+      //计算每个span的边界
+      const tmpSpans = [], tmpWarnSpan = [];
+
+      if(spans.length===0)
+      {
+        return {
+        spans: tmpSpans,
+        warnSpans: tmpWarnSpan
+      }
+      }
+      //将检测模版和丢失片段都按照开始日期升序排序
+      checkSpanTemplate.sort(function (a, b) {
+        return a.st - b.st;
+      });
+
+
+      spans.sort(function (a, b) {
+        let datestr2number = (dateStr) => {
+          if (typeof dateStr !== "string") {
+            return 0;
+          }
+          const arr = dateStr.split(":");
+          if (arr.length !== 3) {
+            return 0;
+          }
+          return (Number.parseInt(arr[0]) * 3600) + (Number.parseInt(arr[1]) * 60) + (Number.parseInt(arr[2]));
+        }
+
+        return datestr2number(a.st) - datestr2number(b.st);
+      })
+
+
+
+
+      // let test = new Array();
+      // test.push(spans[1]);
+      // console.log(test);
+      spans.forEach((item) => {
+        let st = this.datestr2number(item.st);
+        let et = this.datestr2number(item.et);
+
+        if ((et - st) <= 5) {
+          return;
+        }
+
+        //判断丢失时间段是否在录像计划的范围内
+        const normalFlag = this.judgeLoseSpan(checkSpanTemplate, st, et);
+        // console.log('是否越界' + normalFlag)
+        if (!normalFlag) {
+          //异常丢失日期,超越了边界,去掉超过检查模版的边缘
+          //找到第一个复核丢失片段的检查模版
+          let checkSpan = undefined;
+          for (let i = 0; i < checkSpanTemplate.length; i++) {
+            const val = checkSpanTemplate[i];
+            if (!checkSpan && st <= val.st) {
+              checkSpan = val;
+              break;
+            }
+          }
+
+      // console.log('是否赋值' + JSON.stringify(checkSpan));
+
+
+          if (!checkSpan) {
+            //没有匹配到模版,重新使用ET匹配
+            for (let i = (checkSpanTemplate.length - 1); i >= 0; i--) {
+              const val = checkSpanTemplate[i];
+              if (!checkSpan && et >= val.et) {
+                checkSpan = val;
+                break;
+              }
+            }
+          }
+
+
+
+          if (!!checkSpan) {
+            let warnSt = 0, warnEt = 0;//告警录像
+            let loseSt = 0, loseEt = 0;//丢失录像
+
+            if (st < checkSpan.st) {
+              //左边越界
+              warnSt = st;
+              warnEt = checkSpan.st;
+              loseSt = checkSpan.st;
+              loseEt = et;
+            } else {
+              //右边越界
+              loseSt = st;
+              loseEt = checkSpan.et;
+              warnSt = checkSpan.et;
+              warnEt = et;
+            }
+
+            let width_1 = widthPx * ((warnEt - warnSt) / this.DAY_SECOND);
+
+            if (width_1 < 1) {
+              width_1 = 1;
+            }
+
+            tmpWarnSpan.push({
+              width: width_1,
+              start: widthPx * (warnSt / this.DAY_SECOND),
+              st: this.number2datestr(warnSt),
+              et: this.number2datestr(warnEt)
+            });
+
+            let width_2 = widthPx * ((loseEt - loseSt) / this.DAY_SECOND);
+            if (width_2 < 1) {
+              width_2 = 1;
+            }
+
+            tmpSpans.push({
+              width: width_2,
+              start: widthPx * (loseSt / this.DAY_SECOND),
+              st: this.number2datestr(loseSt),
+              et: this.number2datestr(loseEt)
+            })
+
+            return;
+
+          }
+
+        } else {
+
+          //正常片段
+          let width = widthPx * ((et - st) / this.DAY_SECOND);
+          if (width < 1) {
+            width = 1;
+          }
+
+          tmpSpans.push({
+            width: width,
+            start: widthPx * (st / this.DAY_SECOND),
+            st: item.st,
+            et: item.et
+          })
+        }
+
+
+      });
+
+      return {
+        spans: tmpSpans,
+        warnSpans: tmpWarnSpan
+      }
+
+    },
+
+    renderVideoRuler () {
+      if(!this.rowData) return ;
+      this.rowData.checkSpan=JSON.parse(this.rowData.checkSpan)
+      this.rowData.loseSpan=JSON.parse(this.rowData.loseSpan)
+      const checkSpan = this.calculateCheckSpanUI(this.rowData.checkSpan, this.widthrem);
+      const loseSpan = this.calculateLoseSpanUI(this.rowData.loseSpan, checkSpan.templates, this.widthrem);
+      const warnSpan = this.calculateWarnSpanUI(this.rowData.checkSpan, this.widthrem);
+      // debugger
+      return {
+        //存放检测模版气质时间得秒数
+        checkSpanTemplate: checkSpan.templates,
+        //检测模版数据
+        checkSpan: checkSpan.spans,
+        //录像丢失数据
+        loseSpan: loseSpan.spans,
+        //无录像计划的数据
+        warnSpan: warnSpan.spans
+      }
+
+    },
+    number2datestr (senconds) {
+      const hour = Math.floor((senconds % this.DAY_SECOND) / 3600);
+      const minute = Math.floor((senconds % 3600) / 60);
+      const second = senconds % 60;
+      return (hour < 10 ? '0' : '') + hour + ":" +
+        (minute < 10 ? '0' : '') + minute + ":" +
+        (second < 10 ? '0' : '') + second;
+    },
+    datestr2number (dateStr) {
+      if (typeof dateStr !== "string") {
+        return 0;
+      }
+      const arr = dateStr.split(":");
+      if (arr.length !== 3) {
+        return 0;
+      }
+      return (Number.parseInt(arr[0]) * 3600) + (Number.parseInt(arr[1]) * 60) + (Number.parseInt(arr[2]));
+    }
+  },
+  mounted () {
+    let data = this.renderVideoRuler();
+    if(!data) return;
+    this.checkSpan = data.checkSpan;
+    this.loseSpan = data.loseSpan;
+    this.warnSpan = data.warnSpan;
+  },
+  beforeDestroy () {
+  }
+}
+</script>
+
+<style>
+.loseTimeSpan {
+  background: transparent;
+  height: 20px;
+  overflow: hidden;
+  position: relative;
+}
+.loseTimeSpan i {
+  cursor: pointer;
+  margin: 0px;
+  padding: 0px;
+  display: inline-block;
+  z-index: 5;
+  position: absolute;
+  height: 100%;
+  background: #51ca65;
+}
+.loseTimeSpan .lose {
+  z-index: 20;
+  background: #f5a623;
+}
+.loseTimeSpan .warn {
+  z-index: 4;
+  background: #e0e0e0;
+}
+</style>

+ 183 - 0
src/views/iot/videoQuality/index.vue

@@ -0,0 +1,183 @@
+<template>
+  <div class="app-container">
+    <div class="main-right-box">
+      <div class="main-search-box">
+        <el-form v-show="showSearch" ref="queryForm" :inline="true" :model="queryParams" size="small">
+          <el-form-item label="机构名称">
+            <org-tree
+              v-model="queryParams.orgId"
+              @defaultKey="getDefaultKey"
+              @checkChange="changeCheckBox"
+              @click="handleNodeClick"
+              :showCheckSub="false"
+              ref="orgTree"
+            ></org-tree>
+          </el-form-item>
+          <el-form-item label="监控主机" prop="reasons">
+            <el-input
+              v-model="queryParams.reasons"
+              clearable
+              placeholder="请输入关键字"
+              @keyup.enter.native="handleQuery"
+            />
+          </el-form-item>
+          <el-form-item label="摄像头" prop="type">
+            <el-select v-model="queryParams.type" clearable placeholder="请选择介绍信类型">
+              <el-option
+                v-for="dict in dict.type.out_in_type"
+                :key="dict.value"
+                :label="dict.label"
+                :value="dict.value"
+              />
+            </el-select>
+          </el-form-item>
+          <el-form-item label="报警状态" prop="approveStatus">
+            <el-select v-model="queryParams.approveStatus" clearable placeholder="请选择审批状态">
+              <el-option
+                v-for="dict in dict.type.out_in_approve_status"
+                :key="dict.value"
+                :label="dict.label"
+                :value="dict.value"
+              />
+            </el-select>
+          </el-form-item>
+          <el-form-item label="告警类型" prop="approveStatus">
+            <el-select v-model="queryParams.approveStatus" clearable placeholder="请选择审批状态">
+              <el-option
+                v-for="dict in dict.type.out_in_approve_status"
+                :key="dict.value"
+                :label="dict.label"
+                :value="dict.value"
+              />
+            </el-select>
+          </el-form-item>
+          <el-form-item label="报警/恢复时间">
+            <DataRangePicker
+              style="width: 240px"
+              v-model="queryParams.dateRange"
+              key="daterange"
+              type="daterange"
+              :editable="false"
+              :clearable="false"
+              range-separator="-"
+              start-placeholder="开始日期"
+              end-placeholder="结束日期"
+            />
+          </el-form-item>
+        </el-form>
+
+        <el-row :gutter="10">
+          <el-col :span="1.5">
+            <el-button icon="el-icon-search" size="mini" type="primary" @click="handleQuery">搜索</el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button icon="el-icon-refresh" size="mini" type="primary" @click="resetQuery">重置</el-button>
+          </el-col>
+          <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+        </el-row>
+      </div>
+      <el-table v-loading="loading" :data="dataList" border height="646" size="small">
+        <el-table-column label="序号" type="index" align="center" width="70"></el-table-column>
+        <el-table-column :show-overflow-tooltip="true" align="center" label="组织机构" prop="type" width="180"></el-table-column>
+        <el-table-column :show-overflow-tooltip="true" align="center" label="监控主机" prop="reasons" width="140"/>
+        <el-table-column :show-overflow-tooltip="true" align="center" label="摄像头" prop="letterNo" width="140"></el-table-column>
+        <el-table-column :show-overflow-tooltip="true" align="center" label="通道号" prop="letterNo" width="140"></el-table-column>
+        <el-table-column align="center" label="报警/恢复时间" prop="letterNo" width="180"></el-table-column>
+        <el-table-column align="center" label="信号丢失" prop="letterNo" width="100"></el-table-column>
+        <el-table-column align="center" label="遮挡" prop="letterNo" ></el-table-column>
+        <el-table-column align="center" label="亮度" prop="letterNo" ></el-table-column>
+        <el-table-column align="center" label="偏色" prop="letterNo" ></el-table-column>
+        <el-table-column align="center" label="雪花" prop="letterNo" ></el-table-column>
+        <el-table-column align="center" label="条纹" prop="letterNo" ></el-table-column>
+        <el-table-column align="center" label="对比度" prop="letterNo" ></el-table-column>
+        <el-table-column align="center" label="模糊" prop="letterNo" ></el-table-column>
+        <el-table-column label="操作" align="center"  width="120" >
+          <template slot-scope="r">
+            <el-button type="text">告警历史记录</el-button>
+          </template>
+        </el-table-column>
+
+      </el-table>
+
+      <pagination
+        v-show="total>0"
+        :limit.sync="queryParams.pageSize"
+        :page.sync="queryParams.pageNum"
+        :total="total"
+        @pagination="getList"
+      />
+
+    </div>
+  </div>
+</template>
+
+<script>
+import OrgTree from "@/components/orgTree/orgQuerySelector.vue";
+import DataRangePicker from "@/components/dateTime/daterange.picker.vue";
+import TimeLine from "../videoIntegrity/timeLine.vue";
+import {getLetterApproveList} from "@/api/core/letter";
+import dayjs from 'dayjs';
+export default {
+  components: {DataRangePicker, OrgTree,TimeLine },
+  dicts: ['letter_status', 'out_in_type', 'out_in_approve_status'],
+  data() {
+    return {
+      loading:false,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        reasons: null,
+        type: null,
+        approveStatus: null,
+        orgId: this.$store.getters.orgId,
+      },
+      dataList:[],
+    };
+  },
+  mounted() {
+    this.getList();
+  },
+  methods: {
+    dayjs,
+    getDefaultKey(key) {
+      this.queryParams.orgId = key;
+      this.getList();
+    },
+    // 节点单击事件
+    handleNodeClick(data) {
+      this.queryParams.orgId = data.id;
+      this.handleQuery();
+    },
+    /** 下穿状态改变*/
+    changeCheckBox() {
+      this.queryParams.checkSub = !this.queryParams.checkSub;
+      this.getList();
+    },
+
+    /** 查询介绍信列表 */
+    getList() {
+      this.loading = true;
+      getLetterApproveList(this.queryParams).then(response => {
+        this.dataList = response.rows;
+        this.total = response.total;
+        this.loading = false;
+      });
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.resetForm("queryForm");
+      this.handleQuery();
+    },
+  }
+};
+</script>