| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617 | <template>  <div id="channelList" style="width: 100%">    <div v-if="!editId" class="page-header">      <div class="page-title">        <el-button icon="el-icon-back" size="mini" style="font-size: 20px; color: #000;" type="text" @click="showDevice" ></el-button>        <el-divider direction="vertical"></el-divider>        通道列表      </div>      <div class="page-header-btn">        <div v-if="!showTree" style="display: inline;">          搜索:          <el-input @input="search" style="margin-right: 1rem; width: auto;" size="mini" placeholder="关键字"                    prefix-icon="el-icon-search" v-model="searchSrt" clearable></el-input>          通道类型:          <el-select size="mini" @change="search" style="width: 8rem; margin-right: 1rem;" v-model="channelType" placeholder="请选择"                     default-first-option>            <el-option label="全部" value=""></el-option>            <el-option label="设备" value="false"></el-option>            <el-option label="子目录" value="true"></el-option>          </el-select>          在线状态:          <el-select size="mini" style="width: 8rem; margin-right: 1rem;" @change="search" v-model="online" placeholder="请选择"                     default-first-option>            <el-option label="全部" value=""></el-option>            <el-option label="在线" value="true"></el-option>            <el-option label="离线" value="false"></el-option>          </el-select>          码流类型重置:          <el-select size="mini" style="width: 16rem; margin-right: 1rem;" @change="subStreamChange" v-model="subStream"                     placeholder="请选择码流类型" default-first-option >            <el-option label="stream:0(主码流)" value="stream:0"></el-option>            <el-option label="stream:1(子码流)" value="stream:1"></el-option>            <el-option label="streamnumber:0(主码流-2022)" value="streamnumber:0"></el-option>            <el-option label="streamnumber:1(子码流-2022)" value="streamnumber:1"></el-option>            <el-option label="streamprofile:0(主码流-大华)" value="streamprofile:0"></el-option>            <el-option label="streamprofile:1(子码流-大华)" value="streamprofile:1"></el-option>            <el-option label="streamMode:main(主码流-水星+TP-LINK)" value="streamMode:main"></el-option>            <el-option label="streamMode:sub(子码流-水星+TP-LINK)" value="streamMode:sub"></el-option>          </el-select>        </div>        <el-button icon="el-icon-refresh-right" circle size="mini" @click="refresh()"></el-button>      </div>    </div>    <devicePlayer ref="devicePlayer"></devicePlayer>    <el-container v-if="!editId" v-loading="isLoging" style="height: 82vh;">      <el-aside width="auto" style="height: 82vh; background-color: #ffffff; overflow: auto" v-if="showTree">        <DeviceTree ref="deviceTree" :device="device" :onlyCatalog="true" :clickEvent="treeNodeClickEvent"></DeviceTree>      </el-aside>      <el-main style="padding: 5px;">        <el-table size="medium"  ref="channelListTable" :data="deviceChannelList" :height="winHeight" style="width: 100%"                  header-row-class-name="table-header">          <el-table-column prop="name" label="名称" min-width="180">          </el-table-column>          <el-table-column prop="deviceId" label="编号" min-width="180">          </el-table-column>          <el-table-column label="快照" min-width="100">            <template v-slot:default="scope">              <el-image                :src="getSnap(scope.row)"                :preview-src-list="getBigSnap(scope.row)"                @error="getSnapErrorEvent(scope.row.deviceId, scope.row.channelId)"                :fit="'contain'"                style="width: 60px">                <div slot="error" class="image-slot">                  <i class="el-icon-picture-outline"></i>                </div>              </el-image>            </template>          </el-table-column>          <el-table-column prop="subCount" label="子节点数" min-width="100">          </el-table-column>          <el-table-column prop="manufacturer" label="厂家" min-width="100">          </el-table-column>          <el-table-column prop="location" label="位置信息" min-width="120">          </el-table-column>          <el-table-column prop="ptzType" label="云台类型" min-width="100">            <template v-slot:default="scope">              <div >{{ scope.row.ptzTypeText }}</div>            </template>          </el-table-column>          <el-table-column label="开启音频" min-width="100">            <template slot-scope="scope">              <el-switch @change="updateChannel(scope.row)" v-model="scope.row.hasAudio" active-color="#409EFF">              </el-switch>            </template>          </el-table-column>          <el-table-column label="码流类型" min-width="180">            <template slot-scope="scope">              <el-select size="mini" style="margin-right: 1rem;" @change="channelSubStreamChange(scope.row)" v-model="scope.row.streamIdentification"                         placeholder="请选择码流类型" default-first-option >                <el-option label="stream:0(主码流)" value="stream:0"></el-option>                <el-option label="stream:1(子码流)" value="stream:1"></el-option>                <el-option label="streamnumber:0(主码流-2022)" value="streamnumber:0"></el-option>                <el-option label="streamnumber:1(子码流-2022)" value="streamnumber:1"></el-option>                <el-option label="streamprofile:0(主码流-大华)" value="streamprofile:0"></el-option>                <el-option label="streamprofile:1(子码流-大华)" value="streamprofile:1"></el-option>                <el-option label="streamMode:main(主码流-水星+TP-LINK)" value="streamMode:main"></el-option>                <el-option label="streamMode:sub(子码流-水星+TP-LINK)" value="streamMode:sub"></el-option>              </el-select>            </template>          </el-table-column>          <el-table-column label="状态" min-width="100">            <template slot-scope="scope">              <div slot="reference" class="name-wrapper">                <el-tag size="medium" v-if="scope.row.status === 'ON'">在线</el-tag>                <el-tag size="medium" type="info" v-if="scope.row.status !== 'ON'">离线</el-tag>              </div>            </template>          </el-table-column>          <el-table-column label="操作" min-width="340" fixed="right">            <template slot-scope="scope">              <el-button size="medium" v-bind:disabled="device == null || device.online === 0" icon="el-icon-video-play"                         type="text" @click="sendDevicePush(scope.row)">播放              </el-button>              <el-button size="medium" v-bind:disabled="device == null || device.online === 0"                         icon="el-icon-switch-button"                         type="text" style="color: #f56c6c" v-if="!!scope.row.streamId"                         @click="stopDevicePush(scope.row)">停止              </el-button>              <el-divider direction="vertical"></el-divider>              <el-button                size="medium"                type="text"                icon="el-icon-edit"                @click="handleEdit(scope.row)"              >                编辑              </el-button>              <el-divider direction="vertical"></el-divider>              <el-button size="medium" icon="el-icon-s-open" type="text"                         v-if="scope.row.subCount > 0 || scope.row.parental === 1 || scope.row.deviceId.length <= 8"                         @click="changeSubchannel(scope.row)">查看              </el-button>              <el-divider v-if="scope.row.subCount > 0 || scope.row.parental === 1 || scope.row.deviceId.length <= 8" direction="vertical"></el-divider>              <el-dropdown @command="(command)=>{moreClick(command, scope.row)}">                <el-button size="medium" type="text" >                  更多功能<i class="el-icon-arrow-down el-icon--right"></i>                </el-button>                <el-dropdown-menu slot="dropdown">                  <el-dropdown-item command="records" v-bind:disabled="device == null || device.online === 0">                    设备录像</el-dropdown-item>                  <el-dropdown-item command="cloudRecords" v-bind:disabled="device == null || device.online === 0" >                    云端录像</el-dropdown-item>                </el-dropdown-menu>              </el-dropdown>            </template>          </el-table-column>        </el-table>        <el-pagination          style="text-align: right"          @size-change="handleSizeChange"          @current-change="currentChange"          :current-page="currentPage"          :page-size="count"          :page-sizes="[15, 25, 35, 50]"          layout="total, sizes, prev, pager, next"          :total="total">        </el-pagination>      </el-main>    </el-container>    <channel-edit v-if="editId" :id="editId" :closeEdit="closeEdit"></channel-edit>    <!--设备列表-->  </div></template><script>import devicePlayer from './dialog/devicePlayer.vue'import uiHeader from '../layout/UiHeader.vue'import DeviceService from "./service/DeviceService";import DeviceTree from "./common/DeviceTree";import ChannelEdit from "./ChannelEdit";export default {  name: 'channelList',  components: {    devicePlayer,    uiHeader,    DeviceTree,    ChannelEdit,  },  data() {    return {      deviceService: new DeviceService(),      device: null,      deviceId: this.$route.params.deviceId,      parentChannelId: this.$route.params.parentChannelId,      deviceChannelList: [],      videoComponentList: [],      currentPlayerInfo: {}, //当前播放对象      updateLooper: 0, //数据刷新轮训标志      searchSrt: "",      channelType: "",      online: "",      subStream: "",      winHeight: window.innerHeight - 200,      currentPage: 1,      count: 15,      total: 0,      beforeUrl: "/deviceList",      isLoging: false,      showTree: false,      editId: null,      loadSnap: {},      ptzTypes: {        0: "未知",        1: "球机",        2: "半球",        3: "固定枪机",        4: "遥控枪机"      }    };  },  mounted() {    if (this.deviceId) {      this.deviceService.getDevice(this.deviceId, (result) => {        this.device = result;      }, (error) => {        console.log("获取设备信息失败")        console.error(error)      })    }    this.initData();  },  destroyed() {    this.$destroy('videojs');    clearTimeout(this.updateLooper);  },  methods: {    initData: function () {      if (typeof (this.parentChannelId) == "undefined" || this.parentChannelId == 0) {        this.getDeviceChannelList();      } else {        this.showSubchannels();      }    },    initParam: function () {      this.deviceId = this.$route.params.deviceId;      this.parentChannelId = this.$route.params.parentChannelId;      this.currentPage = 1;      this.count = 15;      if (this.parentChannelId == "" || this.parentChannelId == 0) {        this.beforeUrl = "/deviceList"      }    },    currentChange: function (val) {      this.currentPage = val;      this.initData();    },    handleSizeChange: function (val) {      this.count = val;      this.getDeviceChannelList();    },    getDeviceChannelList: function () {      let that = this;      if (typeof (this.$route.params.deviceId) == "undefined") return;      this.$axios({        method: 'get',        url: `/api/device/query/devices/${this.$route.params.deviceId}/channels`,        params: {          page: that.currentPage,          count: that.count,          query: that.searchSrt,          online: that.online,          channelType: that.channelType        }      }).then(function (res) {        if (res.data.code === 0) {          that.total = res.data.data.total;          that.deviceChannelList = res.data.data.list;          that.deviceChannelList.forEach(e => {            e.ptzType = e.ptzType + "";            that.$set(e, "location", "");            if (e.longitude && e.latitude) {              that.$set(e, "location", e.longitude + "," + e.latitude);            }          });          // 防止出现表格错位          that.$nextTick(() => {            that.$refs.channelListTable.doLayout();          })        }      }).catch(function (error) {        console.log(error);      });    },    //通知设备上传媒体流    sendDevicePush: function (itemData) {      let deviceId = this.deviceId;      this.isLoging = true;      let channelId = itemData.deviceId;      console.log("通知设备推流1:" + deviceId + " : " + channelId);      let that = this;      this.$axios({        method: 'get',        url: '/api/play/start/' + deviceId + '/' + channelId,        params: {          isSubStream: this.isSubStream        }      }).then(function (res) {        console.log(res)        that.isLoging = false;        if (res.data.code === 0) {          setTimeout(() => {            let snapId = deviceId + "_" + channelId;            that.loadSnap[deviceId + channelId] = 0;            that.getSnapErrorEvent(snapId)          }, 5000)          itemData.streamId = res.data.data.stream;          that.$refs.devicePlayer.openDialog("media", deviceId, channelId, {            streamInfo: res.data.data,            hasAudio: itemData.hasAudio          });          setTimeout(() => {            that.initData();          }, 1000)        } else {          that.$message.error(res.data.msg);        }      }).catch(function (e) {        console.error(e)        that.isLoging = false;        // that.$message.error("请求超时");      });    },    moreClick: function (command, itemData) {      if (command === "records") {        this.queryRecords(itemData)      }else if (command === "cloudRecords") {        this.queryCloudRecords(itemData)      }    },    queryRecords: function (itemData) {      let deviceId = this.deviceId;      let channelId = itemData.deviceId;      this.$router.push(`/gbRecordDetail/${deviceId}/${channelId}`)    },    queryCloudRecords: function (itemData) {      let deviceId = this.deviceId;      let channelId = itemData.deviceId;      this.$router.push(`/cloudRecordDetail/rtp/${deviceId}_${channelId}`)    },    stopDevicePush: function (itemData) {      var that = this;      this.$axios({        method: 'get',        url: '/api/play/stop/' + this.deviceId + "/" + itemData.deviceId,        params: {          isSubStream: this.isSubStream        }      }).then(function (res) {        that.initData();      }).catch(function (error) {        if (error.response.status === 402) { // 已经停止过          that.initData();        } else {          console.log(error)        }      });    },    getSnap: function (row) {      let baseUrl = window.baseUrl ? window.baseUrl : "";      return ((process.env.NODE_ENV === 'development') ? process.env.BASE_API : baseUrl) + '/api/device/query/snap/' + this.deviceId + '/' + row.deviceId;    },    getBigSnap: function (row) {      return [this.getSnap(row)]    },    getSnapErrorEvent: function (deviceId, channelId) {      if (typeof (this.loadSnap[deviceId + channelId]) != "undefined") {        console.log("下载截图" + this.loadSnap[deviceId + channelId])        if (this.loadSnap[deviceId + channelId] > 5) {          delete this.loadSnap[deviceId + channelId];          return;        }        setTimeout(() => {          let url = (process.env.NODE_ENV === 'development' ? "debug" : "") + '/api/device/query/snap/' + deviceId + '/' + channelId          this.loadSnap[deviceId + channelId]++          document.getElementById(deviceId + channelId).setAttribute("src", url + '?' + new Date().getTime())        }, 1000)      }    },    showDevice: function () {      this.$router.push(this.beforeUrl).then(() => {        this.initParam();        this.initData();      })    },    changeSubchannel(itemData) {      this.beforeUrl = this.$router.currentRoute.path;      var url = `/${this.$router.currentRoute.name}/${this.$router.currentRoute.params.deviceId}/${itemData.deviceId}`      this.$router.push(url).then(() => {        this.searchSrt = "";        this.channelType = "";        this.online = "";        this.initParam();        this.initData();      })    },    showSubchannels: function (channelId) {      if (!this.showTree) {        this.$axios({          method: 'get',          url: `/api/device/query/sub_channels/${this.deviceId}/${this.parentChannelId}/channels`,          params: {            page: this.currentPage,            count: this.count,            query: this.searchSrt,            online: this.online,            channelType: this.channelType          }        }).then((res) => {          if (res.data.code === 0) {            this.total = res.data.data.total;            this.deviceChannelList = res.data.data.list;            this.deviceChannelList.forEach(e => {              e.ptzType = e.ptzType + "";              this.$set(e, "location", "");              if (e.longitude && e.latitude) {                this.$set(e, "location", e.longitude + "," + e.latitude);              }            });            // 防止出现表格错位            this.$nextTick(() => {              this.$refs.channelListTable.doLayout();            })          }        }).catch(function (error) {          console.log(error);        });      } else {        this.$axios({          method: 'get',          url: `/api/device/query/tree/channel/${this.deviceId}`,          params: {            parentId: this.parentChannelId,            page: this.currentPage,            count: this.count,          }        }).then((res) => {          if (res.data.code === 0) {            this.total = res.data.total;            this.deviceChannelList = res.data.list;            // 防止出现表格错位            this.$nextTick(() => {              this.$refs.channelListTable.doLayout();            })          }        }).catch(function (error) {          console.log(error);        });      }    },    search: function () {      this.currentPage = 1;      this.total = 0;      this.initData();    },    updateChannel: function (row) {      this.$axios({        method: 'post',        url: `/api/device/query/channel/audio`,        params: {          channelId: row.id,          audio: row.hasAudio        }      }).then(function (res) {        console.log(JSON.stringify(res));      });    },    subStreamChange: function () {      this.$confirm('确定重置所有通道的码流类型?', '提示', {        confirmButtonText: '确定',        cancelButtonText: '取消',        type: 'warning'      }).then(() => {        this.$axios({          method: 'post',          url: `/api/device/query/channel/stream/identification/update/`,          params: {            deviceDbId: this.device.id,            streamIdentification: this.subStream          }        }).then((res)=> {          console.log(JSON.stringify(res));          this.initData()        }).finally(()=>{          this.subStream = ""        })      }).catch(() => {        this.subStream = ""      });    },    channelSubStreamChange: function (row) {      this.$axios({        method: 'post',        url: `/api/device/query/channel/stream/identification/update/`,        params: {          deviceDbId: row.deviceDbId,          id: row.id,          streamIdentification: row.streamIdentification        }      }).then(function (res) {        console.log(JSON.stringify(res));      });    },    refresh: function () {      this.initData();    },    switchTree: function () {      this.showTree = true;      this.deviceChannelList = [];      this.parentChannelId = 0;      this.currentPage = 1;    },    switchList: function () {      this.showTree = false;      this.deviceChannelList = [];      this.parentChannelId = 0;      this.currentPage = 1;      this.initData();    },    treeNodeClickEvent: function (device, data, isCatalog) {      console.log(device)      if (!!!data.channelId) {        this.parentChannelId = device.deviceId;      } else {        this.parentChannelId = data.channelId;      }      this.initData();    },    // 编辑    handleEdit(row) {      this.editId = row.id    },    // 结束编辑    closeEdit: function (){      this.editId = null      this.getDeviceChannelList()    }  }};</script><style>.videoList {  display: flex;  flex-wrap: wrap;  align-content: flex-start;}.video-item {  position: relative;  width: 15rem;  height: 10rem;  margin-right: 1rem;  background-color: #000000;}.video-item-img {  position: absolute;  top: 0;  bottom: 0;  left: 0;  right: 0;  margin: auto;  width: 100%;  height: 100%;}.video-item-img:after {  content: "";  display: inline-block;  position: absolute;  z-index: 2;  top: 0;  bottom: 0;  left: 0;  right: 0;  margin: auto;  width: 3rem;  height: 3rem;  background-image: url("../assets/loading.png");  background-size: cover;  background-color: #000000;}.video-item-title {  position: absolute;  bottom: 0;  color: #000000;  background-color: #ffffff;  line-height: 1.5rem;  padding: 0.3rem;  width: 14.4rem;}</style>
 |