channelList.vue 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695
  1. <template>
  2. <div id="channelList" style="width: 100%">
  3. <div class="page-header">
  4. <div class="page-title">
  5. <el-button icon="el-icon-back" size="mini" style="font-size: 20px; color: #000;" type="text" @click="showDevice" ></el-button>
  6. <el-divider direction="vertical"></el-divider>
  7. 通道列表
  8. </div>
  9. <div class="page-header-btn">
  10. <div v-if="!showTree" style="display: inline;">
  11. 搜索:
  12. <el-input @input="search" style="margin-right: 1rem; width: auto;" size="mini" placeholder="关键字"
  13. prefix-icon="el-icon-search" v-model="searchSrt" clearable></el-input>
  14. 通道类型:
  15. <el-select size="mini" @change="search" style="width: 8rem; margin-right: 1rem;" v-model="channelType" placeholder="请选择"
  16. default-first-option>
  17. <el-option label="全部" value=""></el-option>
  18. <el-option label="设备" value="false"></el-option>
  19. <el-option label="子目录" value="true"></el-option>
  20. </el-select>
  21. 在线状态:
  22. <el-select size="mini" style="width: 8rem; margin-right: 1rem;" @change="search" v-model="online" placeholder="请选择"
  23. default-first-option>
  24. <el-option label="全部" value=""></el-option>
  25. <el-option label="在线" value="true"></el-option>
  26. <el-option label="离线" value="false"></el-option>
  27. </el-select>
  28. 码流类型重置:
  29. <el-select size="mini" style="width: 16rem; margin-right: 1rem;" @change="subStreamChange" v-model="subStream"
  30. placeholder="请选择码流类型" default-first-option >
  31. <el-option label="stream:0(主码流)" value="stream:0"></el-option>
  32. <el-option label="stream:1(子码流)" value="stream:1"></el-option>
  33. <el-option label="streamnumber:0(主码流-2022)" value="streamnumber:0"></el-option>
  34. <el-option label="streamnumber:1(子码流-2022)" value="streamnumber:1"></el-option>
  35. <el-option label="streamprofile:0(主码流-大华)" value="streamprofile:0"></el-option>
  36. <el-option label="streamprofile:1(子码流-大华)" value="streamprofile:1"></el-option>
  37. <el-option label="streamMode:main(主码流-水星+TP-LINK)" value="streamMode:main"></el-option>
  38. <el-option label="streamMode:sub(子码流-水星+TP-LINK)" value="streamMode:sub"></el-option>
  39. </el-select>
  40. </div>
  41. <el-button icon="el-icon-refresh-right" circle size="mini" @click="refresh()"></el-button>
  42. <el-button v-if="showTree" icon="iconfont icon-list" circle size="mini" @click="switchList()"></el-button>
  43. <el-button v-if="!showTree" icon="iconfont icon-tree" circle size="mini" @click="switchTree()"></el-button>
  44. </div>
  45. </div>
  46. <devicePlayer ref="devicePlayer"></devicePlayer>
  47. <el-container v-loading="isLoging" style="height: 82vh;">
  48. <el-aside width="auto" style="height: 82vh; background-color: #ffffff; overflow: auto" v-if="showTree">
  49. <DeviceTree ref="deviceTree" :device="device" :onlyCatalog="true" :clickEvent="treeNodeClickEvent"></DeviceTree>
  50. </el-aside>
  51. <el-main style="padding: 5px;">
  52. <el-table ref="channelListTable" :data="deviceChannelList" :height="winHeight" style="width: 100%"
  53. header-row-class-name="table-header">
  54. <el-table-column prop="channelId" label="通道编号" min-width="180">
  55. </el-table-column>
  56. <el-table-column prop="deviceId" label="设备编号" min-width="180">
  57. </el-table-column>
  58. <el-table-column prop="name" label="通道名称" min-width="180">
  59. <template v-slot:default="scope">
  60. <el-input
  61. v-show="scope.row.edit"
  62. v-model="scope.row.name"
  63. placeholder="通道名称"
  64. :maxlength="255"
  65. show-word-limit
  66. clearable
  67. />
  68. <span v-show="!scope.row.edit">{{ scope.row.name }}</span>
  69. </template>
  70. </el-table-column>
  71. <el-table-column label="快照" min-width="100">
  72. <template v-slot:default="scope">
  73. <el-image
  74. :src="getSnap(scope.row)"
  75. :preview-src-list="getBigSnap(scope.row)"
  76. @error="getSnapErrorEvent(scope.row.deviceId, scope.row.channelId)"
  77. :fit="'contain'"
  78. style="width: 60px">
  79. <div slot="error" class="image-slot">
  80. <i class="el-icon-picture-outline"></i>
  81. </div>
  82. </el-image>
  83. </template>
  84. </el-table-column>
  85. <el-table-column prop="subCount" label="子节点数" min-width="100">
  86. </el-table-column>
  87. <el-table-column prop="manufacture" label="厂家" min-width="100">
  88. </el-table-column>
  89. <el-table-column label="位置信息" min-width="120">
  90. <template v-slot:default="scope">
  91. <el-input
  92. v-show="scope.row.edit"
  93. v-model="scope.row.location"
  94. placeholder="例:117.234,36.378"
  95. :maxlength="30"
  96. show-word-limit
  97. clearable
  98. />
  99. <span v-show="!scope.row.edit">{{ scope.row.location }}</span>
  100. </template>
  101. </el-table-column>
  102. <el-table-column prop="ptztype" label="云台类型" min-width="100">
  103. <template v-slot:default="scope">
  104. <el-select v-show="scope.row.edit" v-model="scope.row.ptztype"
  105. placeholder="云台类型" filterable>
  106. <el-option
  107. v-for="(value, key) in ptzTypes"
  108. :key="key"
  109. :label="value"
  110. :value="key"
  111. />
  112. </el-select>
  113. <div v-show="!scope.row.edit">{{ scope.row.ptztypeText }}</div>
  114. </template>
  115. </el-table-column>
  116. <el-table-column label="开启音频" min-width="100">
  117. <template slot-scope="scope">
  118. <el-switch @change="updateChannel(scope.row)" v-model="scope.row.hasAudio" active-color="#409EFF">
  119. </el-switch>
  120. </template>
  121. </el-table-column>
  122. <el-table-column label="码流类型" min-width="180">
  123. <template slot-scope="scope">
  124. <el-select size="mini" style="margin-right: 1rem;" @change="channelSubStreamChange(scope.row)" v-model="scope.row.streamIdentification"
  125. placeholder="请选择码流类型" default-first-option >
  126. <el-option label="stream:0(主码流)" value="stream:0"></el-option>
  127. <el-option label="stream:1(子码流)" value="stream:1"></el-option>
  128. <el-option label="streamnumber:0(主码流-2022)" value="streamnumber:0"></el-option>
  129. <el-option label="streamnumber:1(子码流-2022)" value="streamnumber:1"></el-option>
  130. <el-option label="streamprofile:0(主码流-大华)" value="streamprofile:0"></el-option>
  131. <el-option label="streamprofile:1(子码流-大华)" value="streamprofile:1"></el-option>
  132. <el-option label="streamMode:main(主码流-水星+TP-LINK)" value="streamMode:main"></el-option>
  133. <el-option label="streamMode:sub(子码流-水星+TP-LINK)" value="streamMode:sub"></el-option>
  134. </el-select>
  135. </template>
  136. </el-table-column>
  137. <el-table-column label="状态" min-width="100">
  138. <template slot-scope="scope">
  139. <div slot="reference" class="name-wrapper">
  140. <el-tag size="medium" v-if="scope.row.status === true">在线</el-tag>
  141. <el-tag size="medium" type="info" v-if="scope.row.status === false">离线</el-tag>
  142. </div>
  143. </template>
  144. </el-table-column>
  145. <el-table-column label="操作" min-width="340" fixed="right">
  146. <template slot-scope="scope">
  147. <el-button size="medium" v-bind:disabled="device == null || device.online === 0" icon="el-icon-video-play"
  148. type="text" @click="sendDevicePush(scope.row)">播放
  149. </el-button>
  150. <el-button size="medium" v-bind:disabled="device == null || device.online === 0"
  151. icon="el-icon-switch-button"
  152. type="text" style="color: #f56c6c" v-if="!!scope.row.streamId"
  153. @click="stopDevicePush(scope.row)">停止
  154. </el-button>
  155. <el-divider direction="vertical"></el-divider>
  156. <el-button
  157. v-if="scope.row.edit"
  158. size="medium"
  159. type="text"
  160. icon="el-icon-edit-outline"
  161. @click="handleSave(scope.row)"
  162. >
  163. 保存
  164. </el-button>
  165. <el-button
  166. v-else
  167. size="medium"
  168. type="text"
  169. icon="el-icon-edit"
  170. @click="handleEdit(scope.row)"
  171. >
  172. 编辑
  173. </el-button>
  174. <el-divider direction="vertical"></el-divider>
  175. <el-button size="medium" icon="el-icon-s-open" type="text"
  176. v-if="scope.row.subCount > 0 || scope.row.parental === 1"
  177. @click="changeSubchannel(scope.row)">查看
  178. </el-button>
  179. <el-divider v-if="scope.row.subCount > 0 || scope.row.parental === 1" direction="vertical"></el-divider>
  180. <el-button size="medium" v-bind:disabled="device == null || device.online === 0"
  181. icon="el-icon-video-camera"
  182. type="text" @click="queryRecords(scope.row)">设备录像
  183. </el-button>
  184. <el-button size="medium" v-bind:disabled="device == null || device.online === 0" icon="el-icon-cloudy"
  185. type="text" @click="queryCloudRecords(scope.row)">云端录像
  186. </el-button>
  187. </template>
  188. </el-table-column>
  189. </el-table>
  190. <el-pagination
  191. style="float: right"
  192. @size-change="handleSizeChange"
  193. @current-change="currentChange"
  194. :current-page="currentPage"
  195. :page-size="count"
  196. :page-sizes="[15, 25, 35, 50]"
  197. layout="total, sizes, prev, pager, next"
  198. :total="total">
  199. </el-pagination>
  200. </el-main>
  201. </el-container>
  202. <!--设备列表-->
  203. </div>
  204. </template>
  205. <script>
  206. import devicePlayer from './dialog/devicePlayer.vue'
  207. import uiHeader from '../layout/UiHeader.vue'
  208. import DeviceService from "./service/DeviceService";
  209. import DeviceTree from "./common/DeviceTree";
  210. export default {
  211. name: 'channelList',
  212. components: {
  213. devicePlayer,
  214. uiHeader,
  215. DeviceTree
  216. },
  217. data() {
  218. return {
  219. deviceService: new DeviceService(),
  220. device: null,
  221. deviceId: this.$route.params.deviceId,
  222. parentChannelId: this.$route.params.parentChannelId,
  223. deviceChannelList: [],
  224. videoComponentList: [],
  225. currentPlayerInfo: {}, //当前播放对象
  226. updateLooper: 0, //数据刷新轮训标志
  227. searchSrt: "",
  228. channelType: "",
  229. online: "",
  230. subStream: "",
  231. winHeight: window.innerHeight - 200,
  232. currentPage: 1,
  233. count: 15,
  234. total: 0,
  235. beforeUrl: "/deviceList",
  236. isLoging: false,
  237. showTree: false,
  238. loadSnap: {},
  239. ptzTypes: {
  240. 0: "未知",
  241. 1: "球机",
  242. 2: "半球",
  243. 3: "固定枪机",
  244. 4: "遥控枪机"
  245. }
  246. };
  247. },
  248. mounted() {
  249. if (this.deviceId) {
  250. this.deviceService.getDevice(this.deviceId, (result) => {
  251. this.device = result;
  252. }, (error) => {
  253. console.log("获取设备信息失败")
  254. console.error(error)
  255. })
  256. }
  257. this.initData();
  258. },
  259. destroyed() {
  260. this.$destroy('videojs');
  261. clearTimeout(this.updateLooper);
  262. },
  263. methods: {
  264. initData: function () {
  265. if (typeof (this.parentChannelId) == "undefined" || this.parentChannelId == 0) {
  266. this.getDeviceChannelList();
  267. } else {
  268. this.showSubchannels();
  269. }
  270. },
  271. initParam: function () {
  272. this.deviceId = this.$route.params.deviceId;
  273. this.parentChannelId = this.$route.params.parentChannelId;
  274. this.currentPage = 1;
  275. this.count = 15;
  276. if (this.parentChannelId == "" || this.parentChannelId == 0) {
  277. this.beforeUrl = "/deviceList"
  278. }
  279. },
  280. currentChange: function (val) {
  281. this.currentPage = val;
  282. this.initData();
  283. },
  284. handleSizeChange: function (val) {
  285. this.count = val;
  286. this.getDeviceChannelList();
  287. },
  288. getDeviceChannelList: function () {
  289. let that = this;
  290. if (typeof (this.$route.params.deviceId) == "undefined") return;
  291. this.$axios({
  292. method: 'get',
  293. url: `/api/device/query/devices/${this.$route.params.deviceId}/channels`,
  294. params: {
  295. page: that.currentPage,
  296. count: that.count,
  297. query: that.searchSrt,
  298. online: that.online,
  299. channelType: that.channelType
  300. }
  301. }).then(function (res) {
  302. if (res.data.code === 0) {
  303. that.total = res.data.data.total;
  304. that.deviceChannelList = res.data.data.list;
  305. that.deviceChannelList.forEach(e => {
  306. e.ptztype = e.ptztype + "";
  307. that.$set(e, "edit", false);
  308. that.$set(e, "location", "");
  309. if (e.longitude && e.latitude) {
  310. that.$set(e, "location", e.longitude + "," + e.latitude);
  311. }
  312. });
  313. // 防止出现表格错位
  314. that.$nextTick(() => {
  315. that.$refs.channelListTable.doLayout();
  316. })
  317. }
  318. }).catch(function (error) {
  319. console.log(error);
  320. });
  321. },
  322. //通知设备上传媒体流
  323. sendDevicePush: function (itemData) {
  324. let deviceId = this.deviceId;
  325. this.isLoging = true;
  326. let channelId = itemData.channelId;
  327. console.log("通知设备推流1:" + deviceId + " : " + channelId);
  328. let that = this;
  329. this.$axios({
  330. method: 'get',
  331. url: '/api/play/start/' + deviceId + '/' + channelId,
  332. params: {
  333. isSubStream: this.isSubStream
  334. }
  335. }).then(function (res) {
  336. console.log(res)
  337. that.isLoging = false;
  338. if (res.data.code === 0) {
  339. setTimeout(() => {
  340. let snapId = deviceId + "_" + channelId;
  341. that.loadSnap[deviceId + channelId] = 0;
  342. that.getSnapErrorEvent(snapId)
  343. }, 5000)
  344. itemData.streamId = res.data.data.stream;
  345. that.$refs.devicePlayer.openDialog("media", deviceId, channelId, {
  346. streamInfo: res.data.data,
  347. hasAudio: itemData.hasAudio
  348. });
  349. setTimeout(() => {
  350. that.initData();
  351. }, 1000)
  352. } else {
  353. that.$message.error(res.data.msg);
  354. }
  355. }).catch(function (e) {
  356. console.error(e)
  357. that.isLoging = false;
  358. // that.$message.error("请求超时");
  359. });
  360. },
  361. queryRecords: function (itemData) {
  362. let deviceId = this.deviceId;
  363. let channelId = itemData.channelId;
  364. this.$router.push(`/gbRecordDetail/${deviceId}/${channelId}`)
  365. },
  366. queryCloudRecords: function (itemData) {
  367. let deviceId = this.deviceId;
  368. let channelId = itemData.channelId;
  369. this.$router.push(`/cloudRecordDetail/rtp/${deviceId}_${channelId}`)
  370. },
  371. stopDevicePush: function (itemData) {
  372. var that = this;
  373. this.$axios({
  374. method: 'get',
  375. url: '/api/play/stop/' + this.deviceId + "/" + itemData.channelId,
  376. params: {
  377. isSubStream: this.isSubStream
  378. }
  379. }).then(function (res) {
  380. that.initData();
  381. }).catch(function (error) {
  382. if (error.response.status === 402) { // 已经停止过
  383. that.initData();
  384. } else {
  385. console.log(error)
  386. }
  387. });
  388. },
  389. getSnap: function (row) {
  390. let baseUrl = window.baseUrl ? window.baseUrl : "";
  391. return ((process.env.NODE_ENV === 'development') ? process.env.BASE_API : baseUrl) + '/api/device/query/snap/' + row.deviceId + '/' + row.channelId;
  392. },
  393. getBigSnap: function (row) {
  394. return [this.getSnap(row)]
  395. },
  396. getSnapErrorEvent: function (deviceId, channelId) {
  397. if (typeof (this.loadSnap[deviceId + channelId]) != "undefined") {
  398. console.log("下载截图" + this.loadSnap[deviceId + channelId])
  399. if (this.loadSnap[deviceId + channelId] > 5) {
  400. delete this.loadSnap[deviceId + channelId];
  401. return;
  402. }
  403. setTimeout(() => {
  404. let url = (process.env.NODE_ENV === 'development' ? "debug" : "") + '/api/device/query/snap/' + deviceId + '/' + channelId
  405. this.loadSnap[deviceId + channelId]++
  406. document.getElementById(deviceId + channelId).setAttribute("src", url + '?' + new Date().getTime())
  407. }, 1000)
  408. }
  409. },
  410. showDevice: function () {
  411. this.$router.push(this.beforeUrl).then(() => {
  412. this.initParam();
  413. this.initData();
  414. })
  415. },
  416. changeSubchannel(itemData) {
  417. this.beforeUrl = this.$router.currentRoute.path;
  418. var url = `/${this.$router.currentRoute.name}/${this.$router.currentRoute.params.deviceId}/${itemData.channelId}`
  419. this.$router.push(url).then(() => {
  420. this.searchSrt = "";
  421. this.channelType = "";
  422. this.online = "";
  423. this.initParam();
  424. this.initData();
  425. })
  426. },
  427. showSubchannels: function (channelId) {
  428. if (!this.showTree) {
  429. this.$axios({
  430. method: 'get',
  431. url: `/api/device/query/sub_channels/${this.deviceId}/${this.parentChannelId}/channels`,
  432. params: {
  433. page: this.currentPage,
  434. count: this.count,
  435. query: this.searchSrt,
  436. online: this.online,
  437. channelType: this.channelType
  438. }
  439. }).then((res) => {
  440. if (res.data.code === 0) {
  441. this.total = res.data.data.total;
  442. this.deviceChannelList = res.data.data.list;
  443. this.deviceChannelList.forEach(e => {
  444. e.ptztype = e.ptztype + "";
  445. this.$set(e, "edit", false);
  446. this.$set(e, "location", "");
  447. if (e.longitude && e.latitude) {
  448. this.$set(e, "location", e.longitude + "," + e.latitude);
  449. }
  450. });
  451. // 防止出现表格错位
  452. this.$nextTick(() => {
  453. this.$refs.channelListTable.doLayout();
  454. })
  455. }
  456. }).catch(function (error) {
  457. console.log(error);
  458. });
  459. } else {
  460. this.$axios({
  461. method: 'get',
  462. url: `/api/device/query/tree/channel/${this.deviceId}`,
  463. params: {
  464. parentId: this.parentChannelId,
  465. page: this.currentPage,
  466. count: this.count,
  467. }
  468. }).then((res) => {
  469. if (res.data.code === 0) {
  470. this.total = res.data.total;
  471. this.deviceChannelList = res.data.list;
  472. // 防止出现表格错位
  473. this.$nextTick(() => {
  474. this.$refs.channelListTable.doLayout();
  475. })
  476. }
  477. }).catch(function (error) {
  478. console.log(error);
  479. });
  480. }
  481. },
  482. search: function () {
  483. this.currentPage = 1;
  484. this.total = 0;
  485. this.initData();
  486. },
  487. updateChannel: function (row) {
  488. this.$axios({
  489. method: 'post',
  490. url: `/api/device/query/channel/update/${this.deviceId}`,
  491. params: row
  492. }).then(function (res) {
  493. console.log(JSON.stringify(res));
  494. });
  495. },
  496. subStreamChange: function () {
  497. this.$confirm('确定重置所有通道的码流类型?', '提示', {
  498. confirmButtonText: '确定',
  499. cancelButtonText: '取消',
  500. type: 'warning'
  501. }).then(() => {
  502. this.$axios({
  503. method: 'post',
  504. url: `/api/device/query/channel/stream/identification/update/`,
  505. params: {
  506. deviceId: this.deviceId,
  507. streamIdentification: this.subStream
  508. }
  509. }).then((res)=> {
  510. console.log(JSON.stringify(res));
  511. this.initData()
  512. }).finally(()=>{
  513. this.subStream = ""
  514. })
  515. }).catch(() => {
  516. this.subStream = ""
  517. });
  518. },
  519. channelSubStreamChange: function (row) {
  520. this.$axios({
  521. method: 'post',
  522. url: `/api/device/query/channel/stream/identification/update/`,
  523. params: {
  524. deviceId: this.deviceId,
  525. channelId: row.channelId,
  526. streamIdentification: row.streamIdentification
  527. }
  528. }).then(function (res) {
  529. console.log(JSON.stringify(res));
  530. });
  531. },
  532. refresh: function () {
  533. this.initData();
  534. },
  535. switchTree: function () {
  536. this.showTree = true;
  537. this.deviceChannelList = [];
  538. this.parentChannelId = 0;
  539. this.currentPage = 1;
  540. },
  541. switchList: function () {
  542. this.showTree = false;
  543. this.deviceChannelList = [];
  544. this.parentChannelId = 0;
  545. this.currentPage = 1;
  546. this.initData();
  547. },
  548. treeNodeClickEvent: function (device, data, isCatalog) {
  549. console.log(device)
  550. if (!!!data.channelId) {
  551. this.parentChannelId = device.deviceId;
  552. } else {
  553. this.parentChannelId = data.channelId;
  554. }
  555. this.initData();
  556. },
  557. // 保存
  558. handleSave(row) {
  559. if (row.location) {
  560. const segements = row.location.split(",");
  561. if (segements.length !== 2) {
  562. this.$message.warning("位置信息格式有误,例:117.234,36.378");
  563. return;
  564. } else {
  565. row.longitude = parseFloat(segements[0]);
  566. row.latitude = parseFloat(segements[1]);
  567. if (!(row.longitude && row.latitude)) {
  568. this.$message.warning("位置信息格式有误,例:117.234,36.378");
  569. return;
  570. }
  571. }
  572. } else {
  573. delete row.longitude;
  574. delete row.latitude;
  575. }
  576. Object.keys(row).forEach(key => {
  577. const value = row[key];
  578. if (value === null || value === undefined || (typeof value === "string" && value.trim() === "")) {
  579. delete row[key];
  580. }
  581. });
  582. this.$axios({
  583. method: 'post',
  584. url: `/api/device/query/channel/update/${this.deviceId}`,
  585. params: row
  586. }).then(response => {
  587. if (response.data.code === 0) {
  588. this.$message.success("修改成功!");
  589. this.initData();
  590. } else {
  591. this.$message.error("修改失败!");
  592. }
  593. }).catch(_ => {
  594. this.$message.error("修改失败!");
  595. })
  596. },
  597. // 是否正在编辑
  598. isEdit() {
  599. let editing = false;
  600. this.deviceChannelList.forEach(e => {
  601. if (e.edit) {
  602. editing = true;
  603. }
  604. });
  605. return editing;
  606. },
  607. // 编辑
  608. handleEdit(row) {
  609. if (this.isEdit()) {
  610. this.$message.warning('请保存当前编辑项!');
  611. } else {
  612. row.edit = true;
  613. }
  614. }
  615. }
  616. };
  617. </script>
  618. <style>
  619. .videoList {
  620. display: flex;
  621. flex-wrap: wrap;
  622. align-content: flex-start;
  623. }
  624. .video-item {
  625. position: relative;
  626. width: 15rem;
  627. height: 10rem;
  628. margin-right: 1rem;
  629. background-color: #000000;
  630. }
  631. .video-item-img {
  632. position: absolute;
  633. top: 0;
  634. bottom: 0;
  635. left: 0;
  636. right: 0;
  637. margin: auto;
  638. width: 100%;
  639. height: 100%;
  640. }
  641. .video-item-img:after {
  642. content: "";
  643. display: inline-block;
  644. position: absolute;
  645. z-index: 2;
  646. top: 0;
  647. bottom: 0;
  648. left: 0;
  649. right: 0;
  650. margin: auto;
  651. width: 3rem;
  652. height: 3rem;
  653. background-image: url("../assets/loading.png");
  654. background-size: cover;
  655. background-color: #000000;
  656. }
  657. .video-item-title {
  658. position: absolute;
  659. bottom: 0;
  660. color: #000000;
  661. background-color: #ffffff;
  662. line-height: 1.5rem;
  663. padding: 0.3rem;
  664. width: 14.4rem;
  665. }
  666. </style>