panlinlin
2024-06-13 317c5455c6eeb7084460000055fa9d1dc6cecf4a
web_src/src/components/channelList.vue
old mode 100644 new mode 100755
@@ -13,109 +13,206 @@
                    prefix-icon="el-icon-search" v-model="searchSrt" clearable></el-input>
          通道类型:
          <el-select size="mini" @change="search" style="margin-right: 1rem;" v-model="channelType" placeholder="请选择"
          <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="margin-right: 1rem;" @change="search" v-model="online" placeholder="请选择"
          <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>
      <el-button v-if="showTree" icon="iconfont icon-list" circle size="mini" @click="switchList()"></el-button>
      <el-button v-if="!showTree"  icon="iconfont icon-tree" circle size="mini" @click="switchTree()"></el-button>
        <el-button icon="el-icon-refresh-right" circle size="mini" @click="refresh()"></el-button>
        <el-button v-if="showTree" icon="iconfont icon-list" circle size="mini" @click="switchList()"></el-button>
        <el-button v-if="!showTree" icon="iconfont icon-tree" circle size="mini" @click="switchTree()"></el-button>
      </div>
    </div>
  </div>
  <devicePlayer ref="devicePlayer" v-loading="isLoging"></devicePlayer>
  <el-container 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 ref="channelListTable" :data="deviceChannelList" :height="winHeight" style="width: 100%" header-row-class-name="table-header">
        <el-table-column prop="channelId" label="通道编号" min-width="200">
        </el-table-column>
        <el-table-column prop="deviceId" label="设备编号" min-width="200">
        </el-table-column>
        <el-table-column prop="name" label="通道名称" min-width="200">
        </el-table-column>
        <el-table-column label="快照" min-width="120">
          <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>
    <devicePlayer ref="devicePlayer"></devicePlayer>
    <el-container 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 ref="channelListTable" :data="deviceChannelList" :height="winHeight" style="width: 100%"
                  header-row-class-name="table-header">
          <el-table-column prop="channelId" label="通道编号" min-width="180">
          </el-table-column>
          <el-table-column prop="deviceId" label="设备编号" min-width="180">
          </el-table-column>
          <el-table-column prop="name" label="通道名称" min-width="180">
            <template v-slot:default="scope">
              <el-input
                v-show="scope.row.edit"
                v-model="scope.row.name"
                placeholder="通道名称"
                :maxlength="255"
                show-word-limit
                clearable
              />
              <span v-show="!scope.row.edit">{{ scope.row.name }}</span>
            </template>
          </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="manufacture" label="厂家" min-width="100">
          </el-table-column>
          <el-table-column label="位置信息" min-width="120">
            <template v-slot:default="scope">
              <el-input
                v-show="scope.row.edit"
                v-model="scope.row.location"
                placeholder="例:117.234,36.378"
                :maxlength="30"
                show-word-limit
                clearable
              />
              <span v-show="!scope.row.edit">{{ scope.row.location }}</span>
            </template>
          </el-table-column>
          <el-table-column prop="ptzType" label="云台类型" min-width="100">
            <template v-slot:default="scope">
              <el-select v-show="scope.row.edit" v-model="scope.row.ptzType"
                         placeholder="云台类型" filterable>
                <el-option
                  v-for="(value, key) in ptzTypes"
                  :key="key"
                  :label="value"
                  :value="key"
                />
              </el-select>
              <div v-show="!scope.row.edit">{{ 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 === true">在线</el-tag>
                <el-tag size="medium" type="info" v-if="scope.row.status === false">离线</el-tag>
              </div>
            </el-image>
          </template>
        </el-table-column>
        <el-table-column prop="subCount" label="子节点数" min-width="120">
        </el-table-column>
        <el-table-column prop="manufacture" label="厂家" min-width="120">
        </el-table-column>
        <el-table-column label="位置信息"  min-width="200">
          <template slot-scope="scope">
            <span v-if="scope.row.longitude*scope.row.latitude > 0">{{ scope.row.longitude }},<br>{{ scope.row.latitude }}</span>
            <span v-if="scope.row.longitude*scope.row.latitude === 0">无</span>
          </template>
        </el-table-column>
        <el-table-column prop="ptztypeText" label="云台类型" min-width="120"/>
        <el-table-column label="开启音频" min-width="120">
          <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="120">
          <template slot-scope="scope">
            <div slot="reference" class="name-wrapper">
              <el-tag size="medium" v-if="scope.row.status === 1">在线</el-tag>
              <el-tag size="medium" type="info" v-if="scope.row.status === 0">离线</el-tag>
            </div>
          </template>
        </el-table-column>
            </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
                v-if="scope.row.edit"
                size="medium"
                type="text"
                icon="el-icon-edit-outline"
                @click="handleSave(scope.row)"
              >
                保存
              </el-button>
              <el-button
                v-else
                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"
                         @click="changeSubchannel(scope.row)">查看
              </el-button>
              <el-divider v-if="scope.row.subCount > 0 || scope.row.parental === 1" direction="vertical"></el-divider>
<!--              <el-button size="medium" v-bind:disabled="device == null || device.online === 0"-->
<!--                         icon="el-icon-video-camera"-->
<!--                         type="text" @click="queryRecords(scope.row)">设备录像-->
<!--              </el-button>-->
<!--              <el-button size="medium" v-bind:disabled="device == null || device.online === 0" icon="el-icon-cloudy"-->
<!--                         type="text" @click="queryCloudRecords(scope.row)">云端录像-->
<!--              </el-button>-->
              <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="float: 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>
        <el-table-column label="操作" min-width="280" 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" icon="el-icon-s-open" type="text" v-if="scope.row.subCount > 0 || scope.row.parental === 1"
                       @click="changeSubchannel(scope.row)">查看
            </el-button>
            <el-divider v-if="scope.row.subCount > 0 || scope.row.parental === 1" direction="vertical"></el-divider>
            <el-button size="medium" v-bind:disabled="device == null || device.online === 0" icon="el-icon-video-camera" type="text" @click="queryRecords(scope.row)">设备录像
            </el-button>
          </template>
        </el-table-column>
      </el-table>
      <el-pagination
        style="float: 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>
  <!--设备列表-->
    <!--设备列表-->
  </div>
</template>
@@ -123,8 +220,6 @@
<script>
import devicePlayer from './dialog/devicePlayer.vue'
import uiHeader from '../layout/UiHeader.vue'
import moment from "moment";
import DviceService from "./service/DeviceService";
import DeviceService from "./service/DeviceService";
import DeviceTree from "./common/DeviceTree";
@@ -148,6 +243,7 @@
      searchSrt: "",
      channelType: "",
      online: "",
      subStream: "",
      winHeight: window.innerHeight - 200,
      currentPage: 1,
      count: 15,
@@ -155,16 +251,23 @@
      beforeUrl: "/deviceList",
      isLoging: false,
      showTree: false,
      loadSnap: {}
      loadSnap: {},
      ptzTypes: {
        0: "未知",
        1: "球机",
        2: "半球",
        3: "固定枪机",
        4: "遥控枪机"
      }
    };
  },
  mounted() {
    if (this.deviceId) {
      this.deviceService.getDevice(this.deviceId, (result)=>{
          this.device = result;
      this.deviceService.getDevice(this.deviceId, (result) => {
        this.device = result;
      }, (error)=>{
      }, (error) => {
        console.log("获取设备信息失败")
        console.error(error)
      })
@@ -216,12 +319,25 @@
          channelType: that.channelType
        }
      }).then(function (res) {
        that.total = res.data.total;
        that.deviceChannelList = res.data.list;
        // 防止出现表格错位
        that.$nextTick(() => {
          that.$refs.channelListTable.doLayout();
        })
        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, "edit", false);
            that.$set(e, "location", "");
            if (e.customLongitude && e.customLatitude) {
              that.$set(e, "location", e.customLongitude + "," + e.customLatitude);
            }else 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);
      });
@@ -236,7 +352,10 @@
      let that = this;
      this.$axios({
        method: 'get',
        url: '/api/play/start/' + deviceId + '/' + channelId
        url: '/api/play/start/' + deviceId + '/' + channelId,
        params: {
          isSubStream: this.isSubStream
        }
      }).then(function (res) {
        console.log(res)
        that.isLoging = false;
@@ -248,6 +367,7 @@
            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
@@ -256,7 +376,7 @@
            that.initData();
          }, 1000)
        }else{
        } else {
          that.$message.error(res.data.msg);
        }
      }).catch(function (e) {
@@ -265,17 +385,33 @@
        // that.$message.error("请求超时");
      });
    },
    moreClick: function (command, itemData) {
      if (command === "records") {
        this.queryRecords(itemData)
      }else if (command === "cloudRecords") {
        this.queryCloudRecords(itemData)
      }
    },
    queryRecords: function (itemData) {
      var format = moment().format("yyyy-MM-DD");
      let deviceId = this.deviceId;
      let channelId = itemData.channelId;
      this.$refs.devicePlayer.openDialog("record", deviceId, channelId, {date: format})
      this.$router.push(`/gbRecordDetail/${deviceId}/${channelId}`)
    },
    queryCloudRecords: function (itemData) {
      let deviceId = this.deviceId;
      let channelId = itemData.channelId;
      this.$router.push(`/cloudRecordDetail/rtp/${deviceId}_${channelId}`)
    },
    stopDevicePush: function (itemData) {
      var that = this;
      this.$axios({
        method: 'get',
        url: '/api/play/stop/' + this.deviceId + "/" + itemData.channelId
        url: '/api/play/stop/' + this.deviceId + "/" + itemData.channelId,
        params: {
          isSubStream: this.isSubStream
        }
      }).then(function (res) {
        that.initData();
      }).catch(function (error) {
@@ -287,8 +423,8 @@
      });
    },
    getSnap: function (row) {
      let url = (process.env.NODE_ENV === 'development'? "debug": "") + '/api/device/query/snap/' + row.deviceId + '/' + row.channelId
      return url
      let baseUrl = window.baseUrl ? window.baseUrl : "";
      return ((process.env.NODE_ENV === 'development') ? process.env.BASE_API : baseUrl) + '/api/device/query/snap/' + row.deviceId + '/' + row.channelId;
    },
    getBigSnap: function (row) {
      return [this.getSnap(row)]
@@ -302,7 +438,7 @@
          return;
        }
        setTimeout(() => {
          let url = (process.env.NODE_ENV === 'development'? "debug": "") + '/api/device/query/snap/' + deviceId + '/' + channelId
          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)
@@ -318,7 +454,7 @@
    changeSubchannel(itemData) {
      this.beforeUrl = this.$router.currentRoute.path;
      var url = `/${this.$router.currentRoute.name}/${this.$router.currentRoute.params.deviceId}/${itemData.channelId}/${this.$router.currentRoute.params.count}/1`
      var url = `/${this.$router.currentRoute.name}/${this.$router.currentRoute.params.deviceId}/${itemData.channelId}`
      this.$router.push(url).then(() => {
        this.searchSrt = "";
        this.channelType = "";
@@ -339,17 +475,30 @@
            online: this.online,
            channelType: this.channelType
          }
        }).then( (res) =>{
          this.total = res.data.total;
          this.deviceChannelList = res.data.list;
          // 防止出现表格错位
          this.$nextTick(() => {
            this.$refs.channelListTable.doLayout();
          })
        }).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, "edit", false);
              this.$set(e, "location", "");
              if (e.customLongitude && e.customLatitude) {
                this.$set(e, "location", e.customLongitude + "," + e.customLatitude);
              }else 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 {
      } else {
        this.$axios({
          method: 'get',
          url: `/api/device/query/tree/channel/${this.deviceId}`,
@@ -358,13 +507,15 @@
            page: this.currentPage,
            count: this.count,
          }
        }).then((res)=> {
          this.total = res.data.total;
          this.deviceChannelList = res.data.list;
          // 防止出现表格错位
          this.$nextTick(() => {
            this.$refs.channelListTable.doLayout();
          })
        }).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);
        });
@@ -385,17 +536,54 @@
        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: {
            deviceId: this.deviceId,
            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: {
          deviceId: this.deviceId,
          channelId: row.channelId,
          streamIdentification: row.streamIdentification
        }
      }).then(function (res) {
        console.log(JSON.stringify(res));
      });
    },
    refresh: function () {
      this.initData();
    },
    switchTree: function (){
    switchTree: function () {
      this.showTree = true;
      this.deviceChannelList = [];
      this.parentChannelId = 0;
      this.currentPage = 1;
    },
    switchList: function (){
    switchList: function () {
      this.showTree = false;
      this.deviceChannelList = [];
      this.parentChannelId = 0;
@@ -406,12 +594,71 @@
      console.log(device)
      if (!!!data.channelId) {
        this.parentChannelId = device.deviceId;
      }else {
      } else {
        this.parentChannelId = data.channelId;
      }
      this.initData();
    }
    },
    // 保存
    handleSave(row) {
      if (row.location) {
        const segements = row.location.split(",");
        if (segements.length !== 2) {
          console.log(1)
          this.$message.warning("位置信息格式有误,例:117.234,36.378");
          return;
        } else {
          row.customLongitude = parseFloat(segements[0]);
          row.customLatitude = parseFloat(segements[1]);
          if (!(row.customLongitude && row.customLatitude)) {
            this.$message.warning("位置信息格式有误,例:117.234,36.378");
            return;
          }
        }
      } else {
        delete row.longitude;
        delete row.latitude;
      }
      Object.keys(row).forEach(key => {
        const value = row[key];
        if (value === null || value === undefined || (typeof value === "string" && value.trim() === "")) {
          delete row[key];
        }
      });
      this.$axios({
        method: 'post',
        url: `/api/device/query/channel/update/${this.deviceId}`,
        params: row
      }).then(response => {
        if (response.data.code === 0) {
          this.$message.success("修改成功!");
          this.initData();
        } else {
          this.$message.error("修改失败!");
        }
      }).catch(_ => {
        this.$message.error("修改失败!");
      })
    },
    // 是否正在编辑
    isEdit() {
      let editing = false;
      this.deviceChannelList.forEach(e => {
        if (e.edit) {
          editing = true;
        }
      });
      return editing;
    },
    // 编辑
    handleEdit(row) {
      if (this.isEdit()) {
        this.$message.warning('请保存当前编辑项!');
      } else {
        row.edit = true;
      }
    }
  }
};
</script>