index.vue 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963
  1. <template>
  2. <div class="app-container">
  3. <el-row :gutter="10" class="mb8">
  4. <el-col :span="1.5">
  5. <el-button
  6. type="primary"
  7. plain
  8. icon="el-icon-plus"
  9. size="mini"
  10. @click="handleAdd"
  11. v-hasPermi="['live:live:add']"
  12. >新增</el-button>
  13. </el-col>
  14. <!-- <el-col :span="1.5">-->
  15. <!-- <el-button-->
  16. <!-- type="success"-->
  17. <!-- plain-->
  18. <!-- icon="el-icon-edit"-->
  19. <!-- size="mini"-->
  20. <!-- :disabled="single"-->
  21. <!-- @click="handleUpdate"-->
  22. <!-- v-hasPermi="['live:live:edit']"-->
  23. <!-- >修改</el-button>-->
  24. <!-- </el-col>-->
  25. <!-- <el-col :span="1.5">-->
  26. <!-- <el-button-->
  27. <!-- type="warning"-->
  28. <!-- plain-->
  29. <!-- icon="el-icon-download"-->
  30. <!-- size="mini"-->
  31. <!-- :loading="exportLoading"-->
  32. <!-- @click="handleExport"-->
  33. <!-- v-hasPermi="['live:live:export']"-->
  34. <!-- >导出</el-button>-->
  35. <!-- </el-col>-->
  36. <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
  37. </el-row>
  38. <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
  39. <el-form-item label="直播名称" prop="liveName">
  40. <el-input
  41. v-model="queryParams.liveName"
  42. placeholder="请输入直播名称"
  43. clearable
  44. size="small"
  45. />
  46. </el-form-item>
  47. <el-form-item label="直播状态" prop="liveStatus">
  48. <el-select
  49. v-model="queryParams.status"
  50. placeholder="请选择直播状态"
  51. clearable
  52. size="small"
  53. >
  54. <el-option label="待直播" value="1"></el-option>
  55. <el-option label="直播中" value="2"></el-option>
  56. <el-option label="已结束" value="3"></el-option>
  57. <el-option label="直播回放中" value="4"></el-option>
  58. </el-select>
  59. </el-form-item>
  60. <el-form-item label="公司名称" prop="companyName">
  61. <el-input
  62. v-model="queryParams.companyName"
  63. placeholder="请输入公司名称"
  64. clearable
  65. size="small"
  66. />
  67. </el-form-item>
  68. <el-form-item label="直播类型" prop="liveType">
  69. <el-select
  70. v-model="queryParams.liveType"
  71. placeholder="请选择直播类型"
  72. clearable
  73. size="small"
  74. >
  75. <el-option label="直播" value="1"></el-option>
  76. <el-option label="录播" value="2"></el-option>
  77. <el-option label="直播回放" value="3"></el-option>
  78. </el-select>
  79. </el-form-item>
  80. <el-form-item label="开始时间" prop="startTimeS">
  81. <el-date-picker
  82. v-model="queryParams.startTimeS"
  83. type="datetime"
  84. placeholder="选择起始时间"
  85. value-format="yyyy-MM-dd HH:mm:ss"
  86. size="small"
  87. ></el-date-picker>
  88. </el-form-item>
  89. <!-- 开始时间-范围结束 -->
  90. <el-form-item label="结束时间" prop="endTimeE">
  91. <el-date-picker
  92. v-model="queryParams.endTimeE"
  93. type="datetime"
  94. placeholder="选择结束时间"
  95. value-format="yyyy-MM-dd HH:mm:ss"
  96. size="small"
  97. :disabled="!queryParams.startTimeS"
  98. ></el-date-picker>
  99. </el-form-item>
  100. <el-form-item label="上下架" prop="isShow">
  101. <el-select
  102. v-model="queryParams.isShow"
  103. placeholder="请选择上下架状态"
  104. clearable
  105. size="small"
  106. >
  107. <el-option label="上架" value="1"></el-option>
  108. <el-option label="下架" value="2"></el-option>
  109. </el-select>
  110. </el-form-item>
  111. <!-- 审核状态 -->
  112. <el-form-item label="审核状态" prop="isAudit">
  113. <el-select
  114. v-model="queryParams.isAudit"
  115. placeholder="请选择审核状态"
  116. clearable
  117. size="small"
  118. >
  119. <el-option label="审核未通过" value="0"></el-option>
  120. <el-option label="审核通过" value="1"></el-option>
  121. </el-select>
  122. </el-form-item>
  123. <el-form-item>
  124. <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
  125. <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
  126. <el-button icon="el-icon-download" size="mini" v-hasPermi="['live:live:export']" @click="handleExport">导出</el-button>
  127. </el-form-item>
  128. </el-form>
  129. <div class="selection-toolbar">
  130. <el-checkbox :indeterminate="isIndeterminate" v-model="allChecked" @change="toggleSelectAll">
  131. {{ multipleSelection.length > 0 ? `已选 ${multipleSelection.length} 条` : '选中本页' }}
  132. </el-checkbox>
  133. <el-button plain size="mini" @click="handleShelf">上架</el-button>
  134. <el-button plain size="mini" @click="handleUnshelf">下架</el-button>
  135. <el-button plain size="mini" @click="handleDeleteSelected">删除</el-button>
  136. <!-- <el-dropdown>-->
  137. <!-- <el-button plain size="mini">-->
  138. <!-- 更多操作<i class="el-icon-arrow-down el-icon&#45;&#45;right"></i>-->
  139. <!-- </el-button>-->
  140. <!-- <el-dropdown-menu slot="dropdown">-->
  141. <!-- <el-dropdown-item>操作一</el-dropdown-item>-->
  142. <!-- <el-dropdown-item>操作二</el-dropdown-item>-->
  143. <!-- </el-dropdown-menu>-->
  144. <!-- </el-dropdown>-->
  145. </div>
  146. <el-table ref="liveTable" v-loading="loading" :data="liveList" @selection-change="handleSelectionChange">
  147. <el-table-column type="selection" width="55"></el-table-column>
  148. <el-table-column label="直播封面" align="center" prop="liveImgUrl" width="180">
  149. <template slot-scope="scope">
  150. <el-image style="width: 90px;height: 90px;" :src="scope.row.liveImgUrl" mode="aspectFill" :preview-src-list="[scope.row.liveImgUrl]" />
  151. </template>
  152. </el-table-column>
  153. <el-table-column label="直播名称" align="center" prop="liveName" />
  154. <el-table-column label="显示类型" align="center" prop="showType">
  155. <template slot-scope="scope">
  156. <el-tag v-if="scope.row.showType == 1">横屏</el-tag>
  157. <el-tag v-if="scope.row.showType == 2">竖屏</el-tag>
  158. </template>
  159. </el-table-column>
  160. <el-table-column label="直播状态" align="center" prop="status">
  161. <template slot-scope="scope">
  162. <el-tag v-if="scope.row.status == 1">待直播</el-tag>
  163. <el-tag v-if="scope.row.status == 2">直播中</el-tag>
  164. <el-tag v-if="scope.row.status == 3">已结束</el-tag>
  165. <el-tag v-if="scope.row.status == 4">直播回放中</el-tag>
  166. </template>
  167. </el-table-column>
  168. <el-table-column label="公司名称" align="center" prop="companyName" >
  169. <template slot-scope="scope">
  170. <el-tag v-if="scope.row.companyName">{{ scope.row.companyName }}</el-tag>
  171. <el-tag v-else>总台</el-tag>
  172. </template>
  173. </el-table-column>
  174. <el-table-column label="直播类型" align="center" prop="liveType">
  175. <template slot-scope="scope">
  176. <el-tag type="danger" v-if="scope.row.liveType == 1">直播</el-tag>
  177. <el-tag type="success" v-if="scope.row.liveType == 2">录播</el-tag>
  178. <el-tag v-if="scope.row.liveType == 3">直播回放</el-tag>
  179. </template>
  180. </el-table-column>
  181. <el-table-column label="开始时间" align="center" prop="startTime" width="180" />
  182. <el-table-column label="结束时间" align="center" prop="finishTime" width="180" />
  183. <el-table-column label="上下架" align="center" prop="isShow">
  184. <template slot-scope="scope">
  185. <el-tag v-if="scope.row.isShow == 1">上架</el-tag>
  186. <el-tag type="danger" v-if="scope.row.isShow == 2">下架</el-tag>
  187. </template>
  188. </el-table-column>
  189. <el-table-column label="审核状态" align="center" prop="isAudit">
  190. <template slot-scope="scope">
  191. <el-tag type="danger" v-if="scope.row.isAudit == 0">审核未通过</el-tag>
  192. <el-tag v-if="scope.row.isAudit == 1">审核通过</el-tag>
  193. </template>
  194. </el-table-column>
  195. <el-table-column label="操作" align="center" width="200" class-name="small-padding fixed-width">
  196. <template slot-scope="scope">
  197. <el-button
  198. size="mini"
  199. type="text"
  200. icon="el-icon-edit"
  201. @click="handleUpdate(scope.row)"
  202. v-hasPermi="['live:live:edit']"
  203. >修改</el-button>
  204. <el-button
  205. size="mini"
  206. type="text"
  207. icon="el-icon-setting"
  208. @click="handleConfig(scope.row)"
  209. v-hasPermi="['live:config:list']"
  210. >配置</el-button>
  211. <el-button
  212. size="mini"
  213. type="text"
  214. icon="el-icon-service"
  215. @click="handleManage(scope.row)"
  216. v-hasPermi="['live:console:list']"
  217. >进入直播间</el-button>
  218. <el-dropdown trigger="hover">
  219. <el-button size="mini" type="text" icon="el-icon-more">
  220. 更多
  221. </el-button>
  222. <el-dropdown-menu slot="dropdown">
  223. <el-dropdown-item
  224. v-if="scope.row.status == 2 && scope.row.liveType == 1"
  225. @click.native="showLivingUrl(scope.row)"
  226. >
  227. <i class="el-icon-switch-button"></i> 推流码
  228. </el-dropdown-item>
  229. <!-- 去直播按钮 -->
  230. <el-dropdown-item
  231. v-if="scope.row.status != 2"
  232. @click.native="handleStart(scope.row)"
  233. >
  234. <i class="el-icon-monitor"></i> 去直播
  235. </el-dropdown-item>
  236. <el-dropdown-item
  237. v-if="scope.row.status == 2"
  238. @click.native="handleEnded(scope.row)"
  239. >
  240. <i class="el-icon-service"></i> 结束
  241. </el-dropdown-item>
  242. <el-dropdown-item
  243. @click.native="handleCopy(scope.row)"
  244. >
  245. <i class="el-icon-service"></i> 复制直播间
  246. </el-dropdown-item>
  247. <el-dropdown-item
  248. @click.native="handleAudit(scope.row)"
  249. >
  250. <i class="el-icon-service"></i> 审核
  251. </el-dropdown-item>
  252. </el-dropdown-menu>
  253. </el-dropdown>
  254. <!-- <el-button-->
  255. <!-- v-if="scope.row.status == 2"-->
  256. <!-- size="mini"-->
  257. <!-- type="text"-->
  258. <!-- icon="el-icon-switch-button"-->
  259. <!-- @click="handleEnded(scope.row)"-->
  260. <!-- v-hasPermi="['live:config:edit']"-->
  261. <!-- >结束</el-button>-->
  262. </template>
  263. </el-table-column>
  264. </el-table>
  265. <el-dialog
  266. title="直播二维码"
  267. :visible.sync="qrcodeDialogVisible"
  268. width="500px"
  269. :close-on-click-modal="true"
  270. :show-close="true"
  271. top="10vh"
  272. >
  273. <div class="qrcode-img-container">
  274. <img
  275. :src="currentQrcodeUrl"
  276. alt="直播二维码"
  277. class="qrcode-img"
  278. @error="handleImgError"
  279. >
  280. </div>
  281. </el-dialog>
  282. <pagination
  283. v-show="total>0"
  284. :total="total"
  285. :page.sync="queryParams.pageNum"
  286. :limit.sync="queryParams.pageSize"
  287. @pagination="getList"
  288. />
  289. <!-- 添加或修改直播对话框 -->
  290. <el-dialog :title="title" :visible.sync="open" width="900px" append-to-body>
  291. <el-form ref="form" :model="form" :rules="rules" label-width="80px">
  292. <el-form-item label="直播名称" prop="liveName">
  293. <el-input v-model="form.liveName" placeholder="请输入直播名称" />
  294. </el-form-item>
  295. <el-form-item label="显示类型" prop="showType">
  296. <el-radio-group v-model="form.showType">
  297. <el-radio :label="1">横屏</el-radio>
  298. <el-radio :label="2">竖屏</el-radio>
  299. </el-radio-group>
  300. </el-form-item>
  301. <el-form-item label="直播类型" prop="liveType">
  302. <el-radio-group v-model="form.liveType">
  303. <el-radio :label="1">直播</el-radio>
  304. <el-radio :label="2">录播</el-radio>
  305. </el-radio-group>
  306. </el-form-item>
  307. <!-- <el-form-item label="直播达人" prop="talentId">-->
  308. <!-- <el-select filterable v-model="form.talentId" placeholder="请选择达人">-->
  309. <!-- <el-option-->
  310. <!-- v-for="item in talentList"-->
  311. <!-- :key="item.talentId"-->
  312. <!-- :label="item.nickName"-->
  313. <!-- :value="item.talentId">-->
  314. <!-- </el-option>-->
  315. <!-- </el-select>-->
  316. <!-- </el-form-item>-->
  317. <el-form-item label="直播描述" prop="liveDesc">
  318. <Editor ref="myeditor" :height="300" @on-text-change="updateText"/>
  319. <!-- <Editor v-model="form.liveDesc" :height="300" placeholder="直播描述" />-->
  320. </el-form-item>
  321. <!-- <el-form-item label="录播视屏" prop="videoUrl" v-if="form.liveType == 2">-->
  322. <!-- <file-upload v-model="form.videoUrl" :limit="1" :file-size="3" :file-type="['mp4']" />-->
  323. <!-- <el-button @click="getVideoDuration" v-loading="timeLoading">读取视屏时长</el-button>-->
  324. <!-- <p style="margin: 0;padding: 0;" v-loading="timeLoading">视屏时长:<span style="color: #ff4949;">{{form.durationTime}}</span></p>-->
  325. <!-- </el-form-item>-->
  326. <el-form-item label="录播视屏" prop="videoUrl" v-if="form.liveType == 2">
  327. <video-upload :fileKey.sync="form.fileKey" :fileSize.sync="form.fileSize"
  328. :videoUrl.sync="form.videoUrl" :fileName.sync="form.fileName" :line_1.sync="form.lineOne"
  329. :uploadType.sync="form.uploadType" :isTranscode.sync="form.isTranscode"
  330. ref="videoUpload"
  331. :transcodeFileKey.sync="form.transcodeFileKey" @video-duration="handleVideoDuration"
  332. @change="handleVideoChange"></video-upload>
  333. </el-form-item>
  334. <!-- <video-upload-->
  335. <!-- v-if="form.liveType == 2"-->
  336. <!-- :type = "1"-->
  337. <!-- :isPrivate = "isPrivate"-->
  338. <!-- :fileKey.sync = "form.fileKey"-->
  339. <!-- :fileSize.sync = "form.fileSize"-->
  340. <!-- :videoUrl.sync="videoUrl"-->
  341. <!-- :fileName.sync="form.fileName"-->
  342. <!-- :line_2.sync="form.lineTwo"-->
  343. <!-- :line_1.sync="form.lineOne"-->
  344. <!-- :thumbnail.sync="form.thumbnail"-->
  345. <!-- :uploadType.sync="form.uploadType"-->
  346. <!-- :isTranscode.sync="form.isTranscode"-->
  347. <!-- :transcodeFileKey.sync="form.transcodeFileKey"-->
  348. <!-- @video-duration="handleVideoDuration"-->
  349. <!-- @change="handleVideoChange"-->
  350. <!-- ref="videoUpload"-->
  351. <!-- append-to-body-->
  352. <!-- />-->
  353. <el-form-item label="开始时间" prop="startTime">
  354. <el-date-picker size="small"
  355. v-model="form.startTime"
  356. @change="timeChange"
  357. type="datetime"
  358. format="yyyy-MM-dd HH:mm:ss"
  359. value-format="yyyy-MM-dd HH:mm:ss"
  360. :picker-options="{
  361. timePickerOptions: {
  362. selectableRange: '00:00:00 - 23:59:59',
  363. format: 'HH:mm:ss'
  364. }
  365. }"
  366. placeholder="选择开始时间">
  367. </el-date-picker>
  368. </el-form-item>
  369. <el-form-item label="结束时间" prop="finishTime" v-loading="timeLoading">
  370. <el-date-picker size="small"
  371. v-model="form.finishTime"
  372. type="datetime"
  373. format="yyyy-MM-dd HH:mm:ss"
  374. value-format="yyyy-MM-dd HH:mm:ss"
  375. :picker-options="{
  376. timePickerOptions: {
  377. selectableRange: '00:00:00 - 23:59:59',
  378. format: 'HH:mm:ss'
  379. }
  380. }"
  381. placeholder="视屏播放结束">
  382. </el-date-picker>
  383. </el-form-item>
  384. <el-form-item label="直播封面" prop="liveImgUrl">
  385. <image-upload v-model="form.liveImgUrl" :limit="1" />
  386. </el-form-item>
  387. <el-form-item label="上下架" prop="isShow">
  388. <el-radio-group v-model="form.isShow">
  389. <el-radio :label="1">上架</el-radio>
  390. <el-radio :label="2">下架</el-radio>
  391. </el-radio-group>
  392. </el-form-item>
  393. </el-form>
  394. <div slot="footer" class="dialog-footer">
  395. <el-button type="primary" @click="submitForm">确 定</el-button>
  396. <el-button @click="cancel">取 消</el-button>
  397. </div>
  398. </el-dialog>
  399. <el-dialog
  400. title="提示"
  401. :visible.sync="rtmpUrlVisible"
  402. width="30%"
  403. >
  404. <div>服务器地址:{{serverName}}</div>
  405. <div>推流码:{{livingCode}}</div>
  406. <span slot="footer" class="dialog-footer">
  407. <el-button type="primary" @click="rtmpUrlVisible = false">确 定</el-button>
  408. </span>
  409. </el-dialog>
  410. </div>
  411. </template>
  412. <script>
  413. import {
  414. listLive,
  415. getLive,
  416. delLive,
  417. addLive,
  418. updateLive,
  419. exportLive,
  420. selectCompanyTalent,
  421. updateLiveIsAudit,
  422. handleShelfOrUn,
  423. handleDeleteSelected,
  424. finishLive, startLive,
  425. copyLive,
  426. } from "@/api/live/live";
  427. import Editor from '@/components/Editor/wang';
  428. import user from '@/store/modules/user';
  429. import VideoUpload from "@/components/VideoUpload/index.vue";
  430. export default {
  431. name: "Live",
  432. components: { Editor,VideoUpload },
  433. data() {
  434. return {
  435. baseUrl: process.env.VUE_APP_BASE_API,
  436. uploadUrl:process.env.VUE_APP_BASE_API+"/common/uploadOSS",
  437. isPrivate:null,
  438. // 遮罩层
  439. loading: true,
  440. // 导出遮罩层
  441. exportLoading: false,
  442. // 选中数组
  443. ids: [],
  444. // 非单个禁用
  445. single: true,
  446. // 非多个禁用
  447. multiple: true,
  448. timeLoading: false,
  449. // 显示搜索条件
  450. showSearch: true,
  451. // 总条数
  452. total: 0,
  453. // 直播表格数据
  454. liveList: [],
  455. // 达人列表
  456. talentList: [{talentId:111,nickName:"测试达人"},{talentId:222,nickName:"测试达人2"}],
  457. // 弹出层标题
  458. title: "",
  459. // 是否显示弹出层
  460. open: false,
  461. // 查询参数
  462. queryParams: {
  463. pageNum: 1,
  464. pageSize: 10,
  465. liveName: null,
  466. liveDesc: null,
  467. showType: null,
  468. status: null,
  469. anchorId: null,
  470. liveType: null,
  471. startTime: null,
  472. startTimeS: null,
  473. endTimeE: null,
  474. finishTime: null,
  475. liveImgUrl: null,
  476. liveConfig: null,
  477. isShow: null,
  478. isDel: null,
  479. qwQrCode: null,
  480. rtmpUrl: null,
  481. },
  482. // 表单参数
  483. form: {
  484. uploadType: 1,
  485. isTranscode:0,
  486. transcodeFileKey:null,
  487. videoUrl: null,
  488. fileKey: null,
  489. fileName: null,
  490. fileSize: null,
  491. lineOne: null,
  492. },
  493. // 表单校验
  494. rules: {
  495. liveName: [
  496. { required: true, message: "不能为空", trigger: "burl" }
  497. ],
  498. showType: [
  499. { required: true, message: "不能为空", trigger: "burl" }
  500. ],
  501. liveType: [
  502. { required: true, message: "不能为空", trigger: "burl" }
  503. ],
  504. startTime: [
  505. { required: true, message: "不能为空", trigger: "burl" }
  506. ],
  507. liveImgUrl: [
  508. { required: true, message: "不能为空", trigger: "burl" }
  509. ],
  510. isShow: [
  511. { required: true, message: "不能为空", trigger: "change" }
  512. ],
  513. talentId: [
  514. { required: true, message: "不能为空", trigger: "change" }
  515. ]
  516. },
  517. multipleSelection: [],
  518. allChecked: false,
  519. isIndeterminate: false,
  520. rtmpUrlVisible:false,
  521. serverName: '',
  522. livingCode:'',
  523. videoUrl: "",
  524. qrcodeDialogVisible: false, // 弹窗显示状态:默认隐藏
  525. currentQrcodeUrl: "", // 当前要展示的二维码地址
  526. defaultImg: "https://via.placeholder.com/400x400?text=二维码加载失败" // 占位图
  527. };
  528. },
  529. created() {
  530. this.getList();
  531. },
  532. watch: {
  533. 'form.startTime': {
  534. handler(newVal) {
  535. // 1. 若 startTime 为空,直接返回(避免无效处理)
  536. if (!newVal) return;
  537. // 2. 将字符串时间转为 Date 对象(处理 "yyyy-MM-dd HH:mm:ss" 格式)
  538. const timeObj = new Date(newVal);
  539. // 兼容时间解析失败的情况(如格式错误)
  540. if (isNaN(timeObj.getTime())) return;
  541. // 3. 强制将秒数设为 1 并补零(即 01 秒)
  542. timeObj.setSeconds(1); // 固定秒数为 1
  543. const formattedSeconds = this.pad(timeObj.getSeconds()); // 补零为 "01"
  544. // 4. 重新拼接完整的时间字符串(保持原格式:yyyy-MM-dd HH:mm:ss)
  545. const year = timeObj.getFullYear();
  546. const month = this.pad(timeObj.getMonth() + 1); // 月份从 0 开始,需 +1
  547. const day = this.pad(timeObj.getDate());
  548. const hours = this.pad(timeObj.getHours());
  549. const minutes = this.pad(timeObj.getMinutes());
  550. // 5. 更新 form.startTime,完成格式强制修正
  551. this.form.startTime = `${year}-${month}-${day} ${hours}:${minutes}:${formattedSeconds}`;
  552. },
  553. immediate: true, // 初始化时立即执行一次(确保初始值也符合格式)
  554. deep: false // startTime 是字符串,无需深度监听
  555. }
  556. },
  557. methods: {
  558. beforeAvatarUpload(file) {
  559. const isLt1M = file.size / 1024 / 1024 < 1;
  560. if (!isLt1M) {
  561. this.$message.error('上传图片大小不能超过 1MB!');
  562. }
  563. return isLt1M;
  564. },
  565. handleAvatarSuccess(res, file) {
  566. if(res.code==200){
  567. this.form.thumbnail=res.url;
  568. this.$forceUpdate()
  569. }
  570. else{
  571. this.msgError(res.msg);
  572. }
  573. },
  574. handleVideoDuration(duration) {
  575. this.form.duration = duration;
  576. },
  577. handleVideoChange(videoUrl,lineOne){
  578. this.videoUrl = videoUrl;
  579. this.form.videoUrl = videoUrl;
  580. },
  581. showLivingUrl(row){
  582. this.serverName=''
  583. this.livingCode=''
  584. this.rtmpUrlVisible = true
  585. this.serverName = row.rtmpUrl.slice(0,row.rtmpUrl.lastIndexOf('/') + 1)
  586. this.livingCode = row.rtmpUrl.slice(row.rtmpUrl.lastIndexOf('/') + 1)
  587. },
  588. handleShelf(){
  589. if (this.multipleSelection.length > 0) {
  590. var liveList = []
  591. this.multipleSelection.forEach(item => {
  592. liveList.push(item.liveId);
  593. })
  594. handleShelfOrUn({"liveIds":liveList,"isShow":1}).then(res=>{
  595. if (res.code == 200) {
  596. this.getList();
  597. this.$refs.liveTable.clearSelection();
  598. } else {
  599. this.$message.error(res.msg);
  600. }
  601. })
  602. } else {
  603. this.$message.info("请选择上架直播!")
  604. }
  605. },
  606. handleUnshelf(){
  607. if (this.multipleSelection.length > 0) {
  608. var liveList = []
  609. this.multipleSelection.forEach(item => {
  610. console.log(typeof(item.liveId))
  611. console.log(item.liveId)
  612. liveList.push(item.liveId);
  613. })
  614. handleShelfOrUn({"liveIds":liveList,"isShow":2}).then(res=>{
  615. if (res.code == 200) {
  616. this.getList();
  617. this.$refs.liveTable.clearSelection();
  618. } else {
  619. this.$message.error(res.msg);
  620. }
  621. })
  622. } else {
  623. this.$message.info("请选择下架直播!")
  624. }
  625. },
  626. handleDeleteSelected(){
  627. if (this.multipleSelection.length > 0) {
  628. var liveList = []
  629. this.multipleSelection.forEach(item => {
  630. liveList.push(item.liveId);
  631. })
  632. handleDeleteSelected({"liveIds":liveList}).then(res=>{
  633. if (res.code == 200) {
  634. this.getList();
  635. this.$refs.liveTable.clearSelection();
  636. } else {
  637. this.$message.error(res.msg);
  638. }
  639. })
  640. } else {
  641. this.$message.info("请选择被删除的直播!")
  642. }
  643. },
  644. // 全选或取消全选
  645. toggleSelectAll(val) {
  646. this.checked = val; // 更新 checkbox 的状态
  647. if (val) {
  648. // 如果 checkbox 被选中,则全选
  649. this.toggleSelection(this.liveList);
  650. } else {
  651. // 如果 checkbox 被取消选中,则取消全选
  652. this.toggleSelection();
  653. }
  654. },
  655. toggleSelection(rows) {
  656. if (rows && !this.isIndeterminate) {
  657. rows.forEach(row => {
  658. this.$refs.liveTable.toggleRowSelection(row);
  659. });
  660. } else {
  661. this.$refs.liveTable.clearSelection();
  662. }
  663. },
  664. // 多选框选中数据
  665. handleSelectionChange(val) {
  666. this.multipleSelection = val;
  667. // 根据选择项的数量更新 checkbox 的状态
  668. this.allChecked = val.length === this.liveList.length;
  669. this.isIndeterminate = val.length > 0 && val.length < this.liveList.length;
  670. },
  671. /** 查询直播列表 */
  672. getList() {
  673. this.loading = true;
  674. listLive(this.queryParams).then(response => {
  675. this.liveList = response.rows;
  676. this.total = response.total;
  677. this.loading = false;
  678. });
  679. },
  680. urlChange(url) {
  681. this.form.videoUrl = url;
  682. this.getVideoDuration();
  683. },
  684. getVideoDuration() {
  685. this.timeLoading = true;
  686. const videoElement = document.createElement('video');
  687. videoElement.src = this.form.videoUrl;
  688. videoElement.onloadedmetadata = () => {
  689. this.form.duration = Math.floor(videoElement.duration); // 秒
  690. this.form.durationTime = this.secondsToTime(this.form.duration);
  691. this.timeChange();
  692. this.timeLoading = false;
  693. };
  694. },
  695. changeDuration(e){
  696. this.form.duration = e.duration;
  697. this.form.durationTime = e.time;
  698. this.$forceUpdate();
  699. this.timeChange();
  700. },
  701. timeChange(){
  702. if(!this.form.startTime) return;
  703. if(!this.form.duration) return;
  704. const startDateTime = new Date(this.form.startTime);
  705. // 将视频时长(秒)加到开始时间
  706. const endDateTime = new Date(startDateTime.getTime() + this.form.duration * 1000); // 毫秒为单位
  707. // 格式化为年月日 时分秒
  708. this.form.finishTime = this.formatDate(endDateTime);
  709. this.$forceUpdate();
  710. },
  711. // 将秒数转换为时分秒
  712. secondsToTime(seconds) {
  713. const hours = Math.floor(seconds / 3600);
  714. const minutes = Math.floor((seconds % 3600) / 60);
  715. const secs = Math.floor(seconds % 60);
  716. return `${this.pad(hours)}:${this.pad(minutes)}:${this.pad(secs)}`;
  717. },
  718. // 补零处理
  719. pad(number) {
  720. return number < 10 ? `0${number}` : number;
  721. },
  722. // 格式化日期为 年月日 时分秒
  723. formatDate(date) {
  724. const year = date.getFullYear();
  725. const month = (date.getMonth() + 1).toString().padStart(2, '0');
  726. const day = date.getDate().toString().padStart(2, '0');
  727. const hours = date.getHours().toString().padStart(2, '0');
  728. const minutes = date.getMinutes().toString().padStart(2, '0');
  729. const seconds = date.getSeconds().toString().padStart(2, '0');
  730. return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
  731. },
  732. // 取消按钮
  733. cancel() {
  734. this.open = false;
  735. this.reset();
  736. },
  737. // 表单重置
  738. reset() {
  739. this.form = {
  740. showType: 1,
  741. liveType: 2,
  742. isShow: 1,
  743. uploadType:1,
  744. isTranscode:0,
  745. transcodeFileKey:null
  746. };
  747. this.videoUrl = "";
  748. this.resetForm("form");
  749. },
  750. /** 搜索按钮操作 */
  751. handleQuery() {
  752. this.queryParams.pageNum = 1;
  753. this.getList();
  754. },
  755. /** 重置按钮操作 */
  756. resetQuery() {
  757. this.resetForm("queryForm");
  758. this.queryParams.status = null;
  759. this.handleQuery();
  760. },
  761. /** 新增按钮操作 */
  762. handleAdd() {
  763. this.reset();
  764. this.open = true;
  765. setTimeout(() => {
  766. this.$refs.myeditor.setText("");
  767. this.$refs.videoUpload.reset();
  768. }, 100);
  769. this.title = "添加直播间";
  770. },
  771. updateText(text){
  772. this.form.liveDesc=text
  773. },
  774. /** 修改按钮操作 */
  775. handleUpdate(row) {
  776. this.reset();
  777. const liveId = row.liveId || this.ids
  778. getLive(liveId).then(response => {
  779. this.form = response.data;
  780. this.videoUrl = this.form.videoUrl;
  781. if(this.form.duration){
  782. this.form.durationTime = this.secondsToTime(this.form.duration)
  783. }
  784. setTimeout(() => {
  785. if(this.form.liveDesc==null){
  786. this.$refs.myeditor.setText("");
  787. }else{
  788. this.$refs.myeditor.setText(this.form.liveDesc);
  789. }
  790. this.form.videoUrl = row.videoUrl
  791. }, 1);
  792. this.open = true;
  793. this.title = "修改直播间";
  794. });
  795. },
  796. /** 提交按钮 */
  797. submitForm() {
  798. if(this.form.liveId != null) { this.videoUrl = this.form.videoUrl; }
  799. if(this.form.liveType==2 && this.videoUrl.length == 0) {return this.$message.error("请上传视频");}
  800. this.$refs["form"].validate(valid => {
  801. if (valid) {
  802. this.form.videoUrl = this.videoUrl;
  803. if (this.form.liveId != null) {
  804. updateLive(this.form).then(response => {
  805. this.msgSuccess("修改成功");
  806. this.open = false;
  807. this.getList();
  808. });
  809. } else {
  810. addLive(this.form).then(response => {
  811. this.msgSuccess("新增成功");
  812. this.open = false;
  813. this.getList();
  814. });
  815. }
  816. }
  817. });
  818. },
  819. /** 删除按钮操作 */
  820. handleDelete(row) {
  821. const liveIds = row.liveId || this.ids;
  822. this.$confirm('是否确认删除直播编号为"' + liveIds + '"的数据项?', "警告", {
  823. confirmButtonText: "确定",
  824. cancelButtonText: "取消",
  825. type: "warning"
  826. }).then(function() {
  827. return delLive(liveIds);
  828. }).then(() => {
  829. this.getList();
  830. this.msgSuccess("删除成功");
  831. }).catch(() => {});
  832. },
  833. handleConfig(row) {
  834. console.info(row)
  835. this.$router.push('/live/liveConfig/' + row.liveId)
  836. },
  837. handleManage(row) {
  838. this.$router.push('/live/liveConsole/' + row.liveId)
  839. },
  840. // 查看二维码图片
  841. handleCheckCode(row) {
  842. // 先校验图片地址是否存在
  843. if (!row.liveCodeUrl) {
  844. this.$message.warning("二维码图片地址不存在,请稍后重试");
  845. return;
  846. }
  847. // 赋值当前图片地址 + 打开弹窗
  848. this.currentQrcodeUrl = row.liveCodeUrl;
  849. this.qrcodeDialogVisible = true;
  850. },
  851. // 2. 图片加载失败:替换为占位图
  852. handleImgError(e) {
  853. e.target.src = this.defaultImg;
  854. },
  855. handleAudit( row){
  856. this.$alert('是否审核通过?', '审核', {
  857. confirmButtonText: '同意',
  858. cancelButtonText: '不同意',
  859. showCancelButton: true, // 显示取消按钮(即“不同意”)
  860. distinguishCancelAndClose: true,
  861. type: 'warning',
  862. callback: action => {
  863. let isAudit;
  864. if(action === 'cancel') isAudit = 0;
  865. if(action === 'confirm') isAudit = 1;
  866. if("confirm" === action || "cancel" === action){
  867. updateLiveIsAudit( {"liveId" : row.liveId, "isAudit" : isAudit}).then(res => {
  868. console.log(res)
  869. console.log(res>0)
  870. if (res > 0) {
  871. this.getList()
  872. this.$message.success("审核成功")
  873. }else {
  874. this.$message.error(res.msg)
  875. }
  876. })
  877. }
  878. }
  879. });
  880. },
  881. handleStart(row){
  882. if(row.isShow == 2){
  883. this.$message.error("直播间已下架")
  884. return
  885. }
  886. this.$confirm('是否确认开启直播间?', "警告", {
  887. confirmButtonText: "确定",
  888. cancelButtonText: "取消",
  889. type: "warning"
  890. }).then(() => {
  891. startLive({"liveId":row.liveId}).then(response=>{this.getList()})
  892. }).catch(() => {});
  893. },
  894. handleEnded(row){
  895. this.$confirm('是否确认关闭直播间?', "警告", {
  896. confirmButtonText: "确定",
  897. cancelButtonText: "取消",
  898. type: "warning"
  899. }).then(() => {
  900. finishLive({"liveId":row.liveId}).then(response=>{this.getList()})
  901. }).catch(() => {});
  902. },
  903. handleCopy(row){
  904. this.$confirm('是否确认复制直播间?', "警告", {
  905. confirmButtonText: "确定",
  906. cancelButtonText: "取消",
  907. type: "warning"
  908. }).then(() => {
  909. this.loading = true;
  910. copyLive({"liveId":row.liveId}).then(response=>{this.getList();this.loading = false;})
  911. }).catch(() => {});
  912. },
  913. /** 导出按钮操作 */
  914. handleExport() {
  915. const queryParams = this.queryParams;
  916. this.$confirm('是否确认导出所有直播数据项?', "警告", {
  917. confirmButtonText: "确定",
  918. cancelButtonText: "取消",
  919. type: "warning"
  920. }).then(() => {
  921. this.exportLoading = true;
  922. return exportLive(queryParams);
  923. }).then(response => {
  924. this.download(response.msg);
  925. this.exportLoading = false;
  926. }).catch(() => {});
  927. }
  928. }
  929. };
  930. </script>
  931. <style scoped>
  932. .selection-toolbar {
  933. display: flex;
  934. align-items: center;
  935. margin-bottom: 10px;
  936. padding-left: 10px;
  937. }
  938. .selection-toolbar .el-checkbox {
  939. margin-right: 10px;
  940. }
  941. /* 调整 checkbox 内部输入框的对齐 */
  942. .selection-toolbar .el-checkbox .el-checkbox__inner {
  943. top: 8px; /* 根据实际需求调整 */
  944. }
  945. </style>