index.vue 41 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436
  1. <template>
  2. <div id="festivalEdit">
  3. <div class="boxWidth">
  4. <el-form
  5. label-position="right"
  6. label-width="120px"
  7. :model="listData"
  8. :rules="rules"
  9. ref="listData"
  10. >
  11. <el-form-item label="适用业务层级" required>
  12. <el-select
  13. v-model="eduType"
  14. placeholder="请选择教育类型"
  15. @change="changeEduType"
  16. >
  17. <el-option
  18. v-for="(item, index) in eduTypeOptions"
  19. :key="index"
  20. :label="item.educationName"
  21. :value="item.id"
  22. ></el-option>
  23. </el-select>
  24. <el-select
  25. v-model="courType"
  26. placeholder="请选择业务层次"
  27. @change="changecourseType"
  28. >
  29. <el-option
  30. v-for="(item, index) in newCourTypeOptions"
  31. :key="index"
  32. :label="item.projectName + '-' + item.businessName"
  33. :value="item.id"
  34. ></el-option>
  35. </el-select>
  36. <el-popover
  37. ref="popovers"
  38. placement="bottom"
  39. trigger="click"
  40. @show="showHandle"
  41. @hide="hideHandle"
  42. :disabled="courType ? false : true"
  43. >
  44. <el-checkbox
  45. v-model="checkAll"
  46. @change="handleCheckAllChange"
  47. :indeterminate="isIndeterminate"
  48. >全选</el-checkbox
  49. >
  50. <el-checkbox-group
  51. v-model="sujectArray"
  52. class="checkboxSty"
  53. @change="handleCheckedCitiesChange"
  54. >
  55. <el-checkbox
  56. v-for="(item, index) in newSujectOption"
  57. :label="item.newId"
  58. :key="index"
  59. >{{ item.subjectName }}</el-checkbox
  60. >
  61. </el-checkbox-group>
  62. <div style="display: block; text-align: center; margin-top: 10px">
  63. <el-button size="mini" type="primary" @click="submitSujectArray"
  64. >确定</el-button
  65. >
  66. </div>
  67. <el-button
  68. slot="reference"
  69. style="margin-left: 12px"
  70. @click="getMessage"
  71. >请选择科目</el-button
  72. >
  73. </el-popover>
  74. <span style="margin-left: 10px">注:可多选</span>
  75. </el-form-item>
  76. <el-form-item label>
  77. <div :class="changeHeight ? 'ach' : 'clh'">
  78. <div
  79. v-for="(item, index) in newSujectApis"
  80. :key="index"
  81. class="listBoxStys"
  82. >
  83. {{
  84. item.educationName +
  85. " - " +
  86. item.projectName +
  87. " - " +
  88. item.businessName +
  89. " - " +
  90. item.subjectName
  91. }}
  92. <i class="el-icon-error closeIcons" @click="closeType(index)"></i>
  93. </div>
  94. </div>
  95. <el-button
  96. size="mini"
  97. v-if="newSujectApis.length > 1"
  98. @click="changeType"
  99. >{{ changeHeight ? "展开" : "关闭" }}</el-button
  100. >
  101. <el-button
  102. size="mini"
  103. v-if="newSujectApis.length > 0"
  104. @click="sujectApis = []"
  105. >清空</el-button
  106. >
  107. <!-- <span v-if="newSujectApis.length === 0">未选项目类型</span> -->
  108. </el-form-item>
  109. <el-form-item label="标题前缀">
  110. <el-input v-model="listData.prefixName"></el-input>
  111. <div style="color: #999">注:便于检索、归类,以及区分一样的标题</div>
  112. </el-form-item>
  113. <el-form-item label="节标题" prop="name">
  114. <el-input v-model="listData.name"></el-input>
  115. <div style="color: #999">
  116. 注:请尽量规范易懂,方便在课程目录表呈现给学员
  117. </div>
  118. </el-form-item>
  119. <el-form-item label="节类型">
  120. <el-select
  121. clearable
  122. :disabled="typeSection == 1 || typeSection == 3"
  123. v-model="listData.sectionType"
  124. placeholder="请选择节类型"
  125. @change="changeTypeSection"
  126. >
  127. <el-option
  128. v-for="(item, index) in sectionTypeOptions"
  129. :key="index"
  130. :disabled="item.value == 1"
  131. :label="item.label"
  132. :value="item.value"
  133. ></el-option>
  134. </el-select>
  135. </el-form-item>
  136. <el-form-item label="视频类型" prop="viewSign">
  137. <el-radio-group v-model="listData.viewSign" disabled>
  138. <el-radio :label="1">保利威</el-radio>
  139. <el-radio :label="2">腾讯</el-radio>
  140. </el-radio-group>
  141. </el-form-item>
  142. <el-form-item
  143. label="频道号"
  144. v-if="listData.sectionType === 2"
  145. prop="liveUrl"
  146. >
  147. <el-input
  148. style="width: 300px"
  149. v-model="listData.liveUrl"
  150. placeholder="请输入频道号"
  151. ></el-input>
  152. <el-select
  153. v-model="newActiveLiveUrl"
  154. placeholder="快捷选中频道号"
  155. @change="changeLiveUrl"
  156. >
  157. <el-option
  158. v-for="(item, index) in newLiveUrl"
  159. :key="index"
  160. :label="item.streamingName"
  161. :value="item.id"
  162. ></el-option>
  163. </el-select>
  164. <div v-if="listData.liveUrl" style="margin-top: 10px">
  165. <el-button
  166. size="small"
  167. type="warning"
  168. @click="watchZbVideo(listData.liveUrl)"
  169. >直播预览</el-button
  170. >
  171. </div>
  172. </el-form-item>
  173. <el-form-item
  174. label="直播开始时间"
  175. prop="liveStartTime"
  176. v-if="listData.sectionType === 2"
  177. >
  178. <el-date-picker
  179. v-model="listData.liveStartTime"
  180. type="datetime"
  181. placeholder="请选择直播开始时间"
  182. value-format="timestamp"
  183. ></el-date-picker>
  184. </el-form-item>
  185. <el-form-item
  186. label="直播结束时间"
  187. prop="liveEndTime"
  188. v-if="listData.sectionType === 2"
  189. >
  190. <el-date-picker
  191. v-model="listData.liveEndTime"
  192. type="datetime"
  193. placeholder="请选择直播结束时间"
  194. value-format="timestamp"
  195. ></el-date-picker>
  196. </el-form-item>
  197. <el-form-item
  198. label="直播时长"
  199. v-if="
  200. listData.sectionType === 2 &&
  201. listData.liveStartTime &&
  202. listData.liveEndTime
  203. "
  204. >{{
  205. compTimeOUT(listData.liveStartTime, listData.liveEndTime)
  206. }}</el-form-item
  207. >
  208. <el-form-item
  209. label="URL地址"
  210. v-if="listData.sectionType === 1 || listData.sectionType === 3"
  211. prop="recordingUrl"
  212. >
  213. <el-input
  214. style="width: 300px"
  215. v-model="listData.recordingUrl"
  216. placeholder="请输入URL地址"
  217. @change="getApiTime(listData.recordingUrl)"
  218. ></el-input>
  219. <label
  220. v-if="listData.viewSign == 1"
  221. for="mobles"
  222. class="el-button el-button--primary"
  223. style="margin: 0px 6px; padding: 10px 20px"
  224. >上传</label
  225. >
  226. <input
  227. style="display: none"
  228. type="file"
  229. id="mobles"
  230. @change="importMoble"
  231. />
  232. <el-select
  233. v-if="listData.sectionType === 1 && listData.viewSign == 1"
  234. v-model="newActiveRecordingUrl1"
  235. placeholder="快捷选中录播URL地址"
  236. filterable
  237. @change="changeRecordingUrl1"
  238. >
  239. <el-option
  240. v-for="(item, index) in newSteamUrl1"
  241. :key="index"
  242. :label="item.streamingName"
  243. :value="item.id"
  244. ></el-option>
  245. </el-select>
  246. <el-select
  247. v-if="listData.sectionType === 3"
  248. v-model="newActiveRecordingUrl2"
  249. placeholder="快捷选中回放URL地址"
  250. filterable
  251. @change="changeRecordingUrl2"
  252. >
  253. <el-option
  254. v-for="(item, index) in newSteamUrl2"
  255. :key="index"
  256. :label="item.streamingName"
  257. :value="item.id"
  258. ></el-option>
  259. </el-select>
  260. <div v-if="listData.recordingUrl" style="margin-top: 10px">
  261. <el-button
  262. size="small"
  263. type="warning"
  264. @click="watchVideo(listData.recordingUrl)"
  265. >视频预览</el-button
  266. >
  267. </div>
  268. </el-form-item>
  269. <el-form-item
  270. label="节时长"
  271. v-if="listData.sectionType === 1 || listData.sectionType === 3"
  272. prop="durationTime"
  273. >
  274. <el-time-picker
  275. :disabled="disabloutime"
  276. value-format="HH:mm:ss"
  277. range-separator=":"
  278. v-model="listData.durationTime"
  279. placeholder="请填入节时长"
  280. ></el-time-picker>
  281. </el-form-item>
  282. <el-form-item
  283. label="讲师"
  284. :prop="listData.sectionType === 2 ? 'teacherId' : ''"
  285. >
  286. <el-select
  287. v-model="listData.teacherId"
  288. placeholder="请选择讲师"
  289. filterable
  290. >
  291. <el-option
  292. v-for="(item, index) in teacherList"
  293. :key="index"
  294. :label="item.teacherName"
  295. :value="item.teacherId"
  296. ></el-option>
  297. </el-select>
  298. </el-form-item>
  299. <el-form-item label="节封面" prop="coverUrl">
  300. <el-row :gutter="10" style="margin-bottom: 10px">
  301. <el-col :span="12">
  302. <div
  303. style="
  304. width: 100%;
  305. height: 150px;
  306. border: 2px dashed #999;
  307. border-radius: 28px;
  308. line-height: 150px;
  309. text-align: center;
  310. "
  311. v-if="!listData.coverUrl"
  312. >
  313. <label for="uplose">
  314. <i class="el-icon-circle-plus-outline iconStsz"></i>
  315. </label>
  316. <input
  317. ref="file"
  318. type="file"
  319. style="display: none"
  320. id="uplose"
  321. @change="getImgFile"
  322. />
  323. </div>
  324. <el-image
  325. v-else
  326. style="width: 100%"
  327. :src="$methodsTools.splitImgHost(listData.coverUrl)"
  328. :preview-src-list="[
  329. $methodsTools.splitImgHost(listData.coverUrl),
  330. ]"
  331. ></el-image>
  332. </el-col>
  333. <el-col :span="11">
  334. <span style="color: #999; font-size: 14px"
  335. >注:请上传小于300kb,尺寸为750*440的图片,支持gif、jpg、jpeg、png等类型</span
  336. >
  337. </el-col>
  338. </el-row>
  339. <el-button
  340. v-if="listData.coverUrl"
  341. type="danger"
  342. size="mini"
  343. class="margin-top: 20px;"
  344. @click="clearImgs"
  345. >删除</el-button
  346. >
  347. </el-form-item>
  348. <el-form-item label="是否发布" prop="publishStatus">
  349. <el-radio-group v-model="listData.publishStatus">
  350. <el-radio :label="1">是</el-radio>
  351. <el-radio :label="0">否</el-radio>
  352. </el-radio-group>
  353. </el-form-item>
  354. <el-form-item>
  355. <el-button @click="backPage">取消</el-button>
  356. <el-button
  357. type="primary"
  358. @click="submit('listData')"
  359. :disabled="listData.sectionType === 1 ? !noStudent : false"
  360. :loading="disabledBtn"
  361. >确定</el-button
  362. >
  363. </el-form-item>
  364. </el-form>
  365. </div>
  366. <el-dialog
  367. :visible.sync="diavos"
  368. width="840px"
  369. @opened="isOkBf"
  370. @close="closePlayer"
  371. :show-close="false"
  372. :close-on-click-modal="false"
  373. >
  374. <div slot="title" class="hearders">
  375. <div class="leftTitle">视频预览</div>
  376. <div class="rightBoxs">
  377. <img src="@/assets/images/Close@2x.png" alt @click="clears" />
  378. </div>
  379. </div>
  380. <div>
  381. <div id="player" v-show="listData.viewSign == 1"></div>
  382. </div>
  383. <span slot="footer" class="dialog-footer">
  384. <el-button @click="clears">取 消</el-button>
  385. </span>
  386. </el-dialog>
  387. <el-dialog
  388. :visible.sync="diavoszb"
  389. width="840px"
  390. @opened="isOkBfzb"
  391. :show-close="false"
  392. :close-on-click-modal="false"
  393. >
  394. <div slot="title" class="hearders">
  395. <div class="leftTitle">直播预览</div>
  396. <div class="rightBoxs">
  397. <img src="@/assets/images/Close@2x.png" alt @click="clearszb" />
  398. </div>
  399. </div>
  400. <div>
  401. <div id="playerzb"></div>
  402. </div>
  403. <span slot="footer" class="dialog-footer">
  404. <el-button @click="clearszb">取 消</el-button>
  405. </span>
  406. </el-dialog>
  407. </div>
  408. </template>
  409. <script>
  410. import { returnTitle } from "@/utils/polyvError.js";
  411. import { uploadFile } from "@/utils/uopladFile.js";
  412. export default {
  413. name: "FestivalEdit",
  414. data() {
  415. return {
  416. disabledBtn: false,
  417. isIndeterminate: false,
  418. checkAll: false,
  419. vodPlayerJs: "https://player.polyv.net/script/player.js",
  420. vodPlayerJsForTCPlayer:
  421. "https://web.sdk.qcloud.com/player/tcplayer/release/v5.1.0/tcplayer.v5.1.0.min.js",
  422. vid: "",
  423. playerJs:
  424. "https://player.polyv.net/resp/live-h5-player/latest/liveplayer.min.js",
  425. uidzb: "egsxlptzdq",
  426. vidzb: "",
  427. diavos: false,
  428. diavoszb: false,
  429. fileSetting: {
  430. desc: "i am desc", // 描述
  431. cataid: "1639399775001", // 分类ID 可以后端传递 也可以不写 或写死
  432. tag: "i am tag", // 标签
  433. luping: 0, // 是否开启视频课件优化处理,对于上传录屏类视频清晰度有所优化:0为不开启,1为开启
  434. keepsource: 1, // 是否源文件播放(不对视频进行编码):0为编码,1为不编码
  435. },
  436. sectionTypeOptions: [
  437. {
  438. label: "录播",
  439. value: 1,
  440. },
  441. {
  442. label: "直播",
  443. value: 2,
  444. },
  445. {
  446. label: "回放",
  447. value: 3,
  448. },
  449. ],
  450. // 弹窗数据
  451. changeHeight: true,
  452. bfImg: "oss/images/avatar/20211013/1634097664410_1397766697",
  453. listData: {
  454. recordingUrl: "",
  455. liveUrl: "",
  456. coverUrl: "oss/images/avatar/20211013/1634097664410_1397766697",
  457. },
  458. newActiveLiveUrl: "",
  459. newLiveUrl: [], //直播地址
  460. newActiveRecordingUrl1: "",
  461. newSteamUrl1: [], //录播流地址
  462. newActiveRecordingUrl2: "",
  463. newSteamUrl2: [], //回放流地址
  464. eduTypeOptions: [], //教育类型数据
  465. projectTypeOptions: [], //项目类型数据
  466. courTypeOptions: [], //业务层次数据
  467. newCourTypeOptions: [], //当前业务层次数据
  468. sujectOption: [], //科目数据
  469. newSujectOption: [], //当前科目数据数据
  470. eduType: "", //当前选中教育类型
  471. courType: "", //当前选中业务层次
  472. sujectApis: [], //当前存在的科目
  473. newSujectApis: [],
  474. sujectArray: [], //选中的科目
  475. teacherList: [], //教师列表
  476. noStudent: true,
  477. disabloutime: false,
  478. typeSection: "", //备份节类型
  479. //表单验证
  480. rules: {
  481. prefixName: [
  482. { required: true, message: "请输入标题前缀", trigger: "blur" },
  483. ],
  484. name: [{ required: true, message: "请输入节标题", trigger: "blur" }],
  485. // liveDuration: [
  486. // { required: true, message: "节时长不能为空" },
  487. // { type: "number", message: "节时长必须为数字值" },
  488. // ],
  489. viewSign: [
  490. { required: true, message: "请选择视频类型", trigger: "change" },
  491. ],
  492. recordingUrl: [
  493. {
  494. required: true,
  495. message: "请输入URL地址",
  496. trigger: ["blur", "change"],
  497. },
  498. ],
  499. durationTime: [
  500. { required: false, message: "请选择节时长", trigger: "change" },
  501. ],
  502. liveUrl: [
  503. {
  504. required: true,
  505. message: "请输入频道号",
  506. trigger: ["blur", "change"],
  507. },
  508. ],
  509. liveStartTime: [
  510. { required: true, message: "请选择直播开始时间", trigger: "change" },
  511. ],
  512. liveEndTime: [
  513. { required: true, message: "请选择直播结束时间", trigger: "change" },
  514. ],
  515. teacherId: [
  516. { required: true, message: "请选择讲师", trigger: "change" },
  517. ],
  518. publishStatus: [
  519. { required: true, message: "请选择是否发布", trigger: "change" },
  520. ],
  521. coverUrl: [
  522. { required: true, message: "请上传封面", trigger: "change" },
  523. ],
  524. },
  525. pageId: this.$route.query.id,
  526. };
  527. },
  528. watch: {
  529. sujectApis: {
  530. immediate: true,
  531. handler(newName, oldName) {
  532. this.changeTypes();
  533. },
  534. },
  535. },
  536. mounted() {
  537. this.$modal.loading("正在导入数据,请稍后...");
  538. this.$api.gradecheckGoodsChange({ sectionId: this.pageId }).then((res) => {
  539. if (res.data > 0) {
  540. // this.noStudent = false;
  541. }
  542. });
  543. this.getDict();
  544. },
  545. methods: {
  546. /**
  547. * 复选框点击触发
  548. */
  549. handleCheckedCitiesChange() {
  550. let nid = this.newSujectOption.map((item) => {
  551. return item.newId;
  552. });
  553. this.checkAll = this.sujectArray.length === nid.length;
  554. this.isIndeterminate =
  555. this.sujectArray.length > 0 && this.sujectArray.length < nid.length;
  556. },
  557. setFunc(arr) {
  558. var arrays = [];
  559. for (let i = 0; i < arr.length; i++) {
  560. if (!arrays.includes(arr[i])) {
  561. arrays.push(arr[i]);
  562. }
  563. }
  564. return arrays;
  565. },
  566. /**
  567. *
  568. * @param {Arrays} val 复选框数据
  569. * @remards 复选框全选触发
  570. */
  571. handleCheckAllChange(val) {
  572. if (val) {
  573. let nid = this.newSujectOption.map((item) => {
  574. return item.newId;
  575. });
  576. let arrays = this.sujectArray.concat(nid);
  577. this.sujectArray = this.setFunc(arrays);
  578. this.isIndeterminate = false;
  579. } else {
  580. let nid = this.newSujectOption.map((item) => {
  581. return item.newId;
  582. });
  583. let newArr = [];
  584. this.sujectArray.forEach((item) => {
  585. if (!nid.includes(item)) {
  586. newArr.push(item);
  587. }
  588. });
  589. this.sujectArray = newArr;
  590. this.isIndeterminate = false;
  591. }
  592. },
  593. /**
  594. * 自动计算直播时长
  595. */
  596. compTimeOUT(start, end) {
  597. if (start && end) {
  598. if (end < start) {
  599. return "请检查开始与结束的时间范围";
  600. }
  601. const asTimes = end / 1000 - start / 1000;
  602. return this.$methodsTools.secondToDate(asTimes, false);
  603. } else {
  604. return "未检测到直播开始时间结束时间,无法计算!";
  605. }
  606. },
  607. changeTypeSection(val) {
  608. if (val === 3) {
  609. this.listData.durationTime = "";
  610. this.disabloutime = false;
  611. }
  612. this.$nextTick(() => {
  613. this.$refs.listData.clearValidate();
  614. });
  615. },
  616. clearTimes(val) {
  617. if (!val) {
  618. this.listData.durationTime = "";
  619. this.disabloutime = false;
  620. return;
  621. }
  622. },
  623. getApiTime(val) {
  624. console.log("获取时长");
  625. var self = this;
  626. this.clearTimes(val);
  627. const valueUrl = val.replace(/\s/g, "");
  628. if (this.listData.viewSign == 2) {
  629. this.disabledBtn = true;
  630. this.$api
  631. .inquirevodvideodetail(valueUrl)
  632. .then((res) => {
  633. console.log("请求腾讯时长", res.data.duration);
  634. if (res.data.duration) {
  635. self.listData.durationTime = this.$methodsTools.secondToDate(
  636. res.data.duration,
  637. false
  638. );
  639. console.log("转换后的腾讯时间", self.listData.durationTime);
  640. self.disabloutime = true;
  641. } else {
  642. self.disabloutime = false;
  643. }
  644. })
  645. .catch((err) => {
  646. self.disabloutime = false;
  647. })
  648. .finally(() => {
  649. this.disabledBtn = false;
  650. });
  651. } else {
  652. if (valueUrl && valueUrl.length > 30) {
  653. this.disabledBtn = true;
  654. this.$api
  655. .inquirepolyvvideo(valueUrl)
  656. .then((res) => {
  657. if (res.data.duration) {
  658. self.listData.durationTime = res.data.duration;
  659. self.disabloutime = true;
  660. } else {
  661. self.disabloutime = false;
  662. }
  663. })
  664. .catch((err) => {
  665. self.disabloutime = false;
  666. })
  667. .finally(() => {
  668. this.disabledBtn = false;
  669. });
  670. }
  671. }
  672. },
  673. loadPlayerScript(callback) {
  674. if (!window.polyvPlayer) {
  675. const myScript = document.createElement("script");
  676. myScript.setAttribute("src", this.vodPlayerJs);
  677. myScript.onload = callback;
  678. document.body.appendChild(myScript);
  679. } else {
  680. callback();
  681. this.player.on("serverError", (...params) => {
  682. this.$message.error(returnTitle(params[1]));
  683. });
  684. }
  685. },
  686. loadPlayerScript_tencent(callback) {
  687. if (!window.TCPlayer) {
  688. const myScript = document.createElement("script");
  689. myScript.setAttribute("src", this.vodPlayerJsForTCPlayer);
  690. myScript.onload = callback;
  691. document.body.appendChild(myScript);
  692. } else {
  693. callback();
  694. this.player_tencent.on("error", (...params) => {
  695. this.$message.error(returnTitle(params[1]));
  696. });
  697. }
  698. },
  699. loadPlayer() {
  700. var self = this;
  701. const polyvPlayer = window.polyvPlayer;
  702. self.player = polyvPlayer({
  703. wrap: "#player",
  704. width: 800,
  705. height: 533,
  706. vid: self.vid,
  707. teaser_show: 0,
  708. playsafe: function (vid, next) {
  709. self.$api.obtainpolyvvideosign(vid).then((res) => {
  710. next(res.data);
  711. });
  712. },
  713. });
  714. },
  715. loadPlayer_tencent() {
  716. var self = this;
  717. const TCPlayer = window.TCPlayer;
  718. try {
  719. let video = document.querySelector("video");
  720. video.parentNode.removeChild(video);
  721. } catch (error) {
  722. console.log("清除video标签异常");
  723. }
  724. let player_tencent = document.createElement("video");
  725. player_tencent.id = "player-tencent";
  726. document
  727. .getElementById("player")
  728. .insertAdjacentElement("afterend", player_tencent);
  729. self.$api.obtaintcvideosign(self.vid).then((res) => {
  730. self.player_tencent = TCPlayer("player-tencent", {
  731. width: 800,
  732. height: 533,
  733. fileID: self.vid,
  734. appID: res.data.appID,
  735. psign: res.data.psign,
  736. licenseUrl: res.data.licenseUrl,
  737. });
  738. });
  739. },
  740. /**
  741. * @param {String} 关闭视频窗口-销毁实例
  742. */
  743. clears() {
  744. this.diavos = false;
  745. if (this.player) {
  746. this.player.destroy();
  747. }
  748. if (this.player_tencent) {
  749. this.player_tencent.dispose();
  750. }
  751. },
  752. /**
  753. * @param {String} 视频查看
  754. */
  755. watchVideo(url) {
  756. if (!url) {
  757. this.$message.warning("请检查URL地址是否输入完整");
  758. return;
  759. }
  760. this.vid = url;
  761. this.diavos = true;
  762. },
  763. isOkBf() {
  764. if (this.listData.viewSign == 2) {
  765. this.loadPlayerScript_tencent(this.loadPlayer_tencent);
  766. } else {
  767. this.loadPlayerScript(this.loadPlayer);
  768. }
  769. },
  770. closePlayer() {
  771. if (this.player) {
  772. this.player.destroy();
  773. }
  774. if (this.player_tencent) {
  775. this.player_tencent.dispose();
  776. }
  777. },
  778. /**
  779. * @param {String} 直播预览
  780. */
  781. watchZbVideo(url) {
  782. if (!url) {
  783. this.$message.warning("请检查直播流地址是否输入完整");
  784. return;
  785. }
  786. this.vidzb = url;
  787. this.diavoszb = true;
  788. },
  789. loadPlayerScriptzb(callback) {
  790. if (!window.polyvLivePlayer) {
  791. const myScript = document.createElement("script");
  792. myScript.setAttribute("src", this.playerJs);
  793. myScript.onload = callback;
  794. document.body.appendChild(myScript);
  795. } else {
  796. callback();
  797. }
  798. },
  799. loadPlayerzb() {
  800. const polyvLivePlayer = window.polyvLivePlayer;
  801. this.playerzb = polyvLivePlayer({
  802. wrap: "#playerzb",
  803. width: 800,
  804. height: 533,
  805. uid: this.uidzb,
  806. vid: this.vidzb,
  807. });
  808. },
  809. clearszb() {
  810. this.diavoszb = false;
  811. if (this.playerzb) {
  812. this.playerzb.destroy();
  813. }
  814. },
  815. isOkBfzb() {
  816. this.loadPlayerScriptzb(this.loadPlayerzb);
  817. },
  818. /**上传视频 */
  819. importMoble(event) {
  820. var file = event.target.files[0];
  821. if (!event.target.value) {
  822. this.$message.error("请选择您要上传的文件");
  823. return false;
  824. }
  825. /**
  826. * @param: event.target.files -> 传递的文件list
  827. * @param: this.fileSetting -> 常规配置 上面有备注
  828. * @param: 回调
  829. */
  830. uploadFile(file, this.fileSetting, (event) => {
  831. this.listData.recordingUrl = event.vid;
  832. this.listData.durationTime = "";
  833. this.getApiTime(event.vid);
  834. });
  835. },
  836. getMessage() {
  837. if (!this.courType) {
  838. this.$message.warning("请先选择业务层级");
  839. }
  840. },
  841. search() {
  842. this.$api
  843. .obtainCourseSection(this.pageId)
  844. .then((res) => {
  845. this.bfImg = res.data.coverUrl;
  846. if (res.data.sectionType === 2) {
  847. res.data.liveStartTime = this.$methodsTools.time10to13(
  848. res.data.liveStartTime,
  849. 2
  850. );
  851. res.data.liveEndTime = this.$methodsTools.time10to13(
  852. res.data.liveEndTime,
  853. 2
  854. );
  855. }
  856. res.data.durationTime = this.$methodsTools.secondToDate(
  857. res.data.durationTime,
  858. false
  859. );
  860. if (res.data.durationTime) {
  861. this.disabloutime = true;
  862. }
  863. this.typeSection = res.data.sectionType;
  864. this.listData = JSON.parse(JSON.stringify(res.data));
  865. this.$api.obtaincourseSectionbusiness(this.pageId).then((result) => {
  866. var arrays = [];
  867. result.data.map((item) => {
  868. arrays.push(item.businessId + "-" + item.subjectId);
  869. });
  870. this.sujectApis = arrays;
  871. });
  872. })
  873. .finally(() => {
  874. this.$modal.closeLoading();
  875. });
  876. },
  877. clearImgs() {
  878. this.listData.coverUrl = "";
  879. this.$refs.listData.validateField("coverUrl");
  880. },
  881. changeTypes() {
  882. var self = this;
  883. var arrays = [];
  884. this.sujectApis.map((item, index) => {
  885. this.courTypeOptions.map((items) => {
  886. if (items.id === item.split("-").map(Number)[0]) {
  887. var obj = {
  888. educationTypeId: items.educationId,
  889. educationName: items.educationName,
  890. projectId: items.projectId,
  891. projectName: items.projectName,
  892. businessId: items.id,
  893. businessName: items.businessName,
  894. };
  895. self.sujectOption.map((i) => {
  896. if (
  897. i.id === item.split("-").map(Number)[1] &&
  898. i.courseArrays.indexOf(items.projectId) !== -1
  899. ) {
  900. obj.subjectName = i.subjectName;
  901. obj.subjectId = i.id;
  902. }
  903. });
  904. arrays.push(obj);
  905. }
  906. });
  907. });
  908. this.newSujectApis = arrays;
  909. },
  910. changeType() {
  911. this.changeHeight = !this.changeHeight;
  912. },
  913. submitSujectArray() {
  914. var self = this;
  915. this.sujectApis = this.sujectApis.filter((item, index) => {
  916. return item.split("-").map(Number)[0] !== Number(self.courType);
  917. });
  918. for (let i = 0; i < this.sujectArray.length; i++) {
  919. this.sujectApis.push(this.sujectArray[i]);
  920. }
  921. this.$refs.popovers.doClose();
  922. this.$nextTick(() => {
  923. this.changeUrl();
  924. });
  925. },
  926. changeUrl() {
  927. var arr = this.newSujectApis.map((val) => val.businessId);
  928. const unique = [...new Set(arr)];
  929. var busId = "";
  930. if (unique.length) {
  931. busId = unique.toString();
  932. } else {
  933. busId = "";
  934. }
  935. this.$api.inquireCourseStreaming({ status: 1 }).then((res) => {
  936. var arraystt = [];
  937. var newarrays1tt = [];
  938. var newarrays2tt = [];
  939. res.rows.map((item) => {
  940. if (item.streamingType === 1) {
  941. arraystt.push(item);
  942. }
  943. if (item.streamingType === 2) {
  944. newarrays1tt.push(item);
  945. }
  946. if (item.streamingType === 3) {
  947. newarrays2tt.push(item);
  948. }
  949. });
  950. this.$api
  951. .inquireCourseStreaming({ status: 1, businessId: busId })
  952. .then((result) => {
  953. var arrays = [];
  954. var newarrays1 = [];
  955. var newarrays2 = [];
  956. result.rows.map((item) => {
  957. if (item.streamingType === 1) {
  958. arrays.push(item);
  959. }
  960. if (item.streamingType === 2) {
  961. newarrays1.push(item);
  962. }
  963. if (item.streamingType === 3) {
  964. newarrays2.push(item);
  965. }
  966. });
  967. if (arrays.length) {
  968. this.newLiveUrl = arrays;
  969. } else {
  970. this.newLiveUrl = arraystt;
  971. }
  972. if (newarrays1.length) {
  973. this.newSteamUrl1 = newarrays1;
  974. } else {
  975. this.newSteamUrl1 = newarrays1tt;
  976. }
  977. if (newarrays2.length) {
  978. this.newSteamUrl2 = newarrays2;
  979. } else {
  980. this.newSteamUrl2 = newarrays2tt;
  981. }
  982. });
  983. });
  984. },
  985. showHandle() {
  986. var array = [];
  987. for (let i = 0; i < this.sujectApis.length; i++) {
  988. if (
  989. this.sujectApis[i].split("-").map(Number)[0] === Number(this.courType)
  990. ) {
  991. array.push(this.sujectApis[i]);
  992. }
  993. }
  994. this.sujectArray = array;
  995. if (!this.newSujectOption.length) {
  996. this.$message.warning("该业务层次暂无关联科目");
  997. this.$refs.popovers.doClose();
  998. return;
  999. }
  1000. this.newSujectOption.map((item) => {
  1001. item.newId = this.courType + "-" + item.id;
  1002. });
  1003. this.handleCheckedCitiesChange();
  1004. },
  1005. hideHandle() {},
  1006. async getDict() {
  1007. await this.tearchApi();
  1008. await this.eduTys();
  1009. await this.courseTys();
  1010. await this.businTys();
  1011. await this.courseSubject();
  1012. this.Streaming();
  1013. this.search();
  1014. },
  1015. tearchApi() {
  1016. return new Promise((resolve, reject) => {
  1017. this.$api.inquiresystemteacherlist({ status: 1 }).then((res) => {
  1018. this.teacherList = res.rows;
  1019. resolve();
  1020. });
  1021. });
  1022. },
  1023. eduTys() {
  1024. return new Promise((resolve, reject) => {
  1025. this.$api.inquireCourseEducationType({ status: 1 }).then((res) => {
  1026. this.eduTypeOptions = res.rows;
  1027. resolve();
  1028. });
  1029. });
  1030. },
  1031. courseTys() {
  1032. return new Promise((resolve, reject) => {
  1033. this.$api.inquireCourseProjectType({ status: 1 }).then((res) => {
  1034. this.projectTypeOptions = res.rows;
  1035. resolve();
  1036. });
  1037. });
  1038. },
  1039. businTys() {
  1040. return new Promise((resolve, reject) => {
  1041. this.$api.inquirebusinessList({ status: 1 }).then((res) => {
  1042. this.courTypeOptions = res.rows;
  1043. this.newCourTypeOptions = res.rows;
  1044. resolve();
  1045. });
  1046. });
  1047. },
  1048. courseSubject() {
  1049. return new Promise((resolve, reject) => {
  1050. this.$api.inquireCourseSubject({ status: 1 }).then((res) => {
  1051. res.rows.map((item, index) => {
  1052. var array = [];
  1053. item.courseProjectTypes.map((items, indexs) => {
  1054. array.push(items.id);
  1055. });
  1056. item.courseArrays = array;
  1057. });
  1058. this.sujectOption = res.rows;
  1059. resolve();
  1060. });
  1061. });
  1062. },
  1063. Streaming() {
  1064. this.$api.inquireCourseStreaming({ status: 1 }).then((res) => {
  1065. var arrays = [];
  1066. var newarrays1 = [];
  1067. var newarrays2 = [];
  1068. res.rows.map((item) => {
  1069. if (item.streamingType === 1) {
  1070. arrays.push(item);
  1071. }
  1072. if (item.streamingType === 2) {
  1073. newarrays1.push(item);
  1074. }
  1075. if (item.streamingType === 3) {
  1076. newarrays2.push(item);
  1077. }
  1078. });
  1079. this.newLiveUrl = arrays;
  1080. this.newSteamUrl1 = newarrays1;
  1081. this.newSteamUrl2 = newarrays2;
  1082. });
  1083. },
  1084. changeEduType() {
  1085. if (!(this.courType === undefined || this.courType === "")) {
  1086. this.courType = "";
  1087. }
  1088. var arrays = [];
  1089. this.courTypeOptions.map((item) => {
  1090. if (item.educationId === this.eduType) {
  1091. arrays.push(item);
  1092. }
  1093. });
  1094. this.newCourTypeOptions = arrays;
  1095. },
  1096. changecourseType() {
  1097. this.newCourTypeOptions.map((item, index) => {
  1098. if (item.id === this.courType) {
  1099. this.eduType = item.educationId;
  1100. var array = [];
  1101. this.sujectOption.map((items, indexs) => {
  1102. if (items.courseArrays.indexOf(item.projectId) !== -1) {
  1103. array.push(items);
  1104. }
  1105. });
  1106. this.newSujectOption = array;
  1107. }
  1108. });
  1109. var arrays = [];
  1110. this.courTypeOptions.map((item) => {
  1111. if (item.educationId === this.eduType) {
  1112. arrays.push(item);
  1113. }
  1114. });
  1115. this.newCourTypeOptions = arrays;
  1116. this.$refs.popovers.doClose();
  1117. },
  1118. submit(formName) {
  1119. this.$refs[formName].validate((valid) => {
  1120. if (valid) {
  1121. if (!this.newSujectApis.length) {
  1122. this.$message.error("请选择适用业务层级");
  1123. return;
  1124. }
  1125. // if (
  1126. // this.listData.coverUrl === "" ||
  1127. // this.listData.coverUrl === null ||
  1128. // this.listData.coverUrl === undefined
  1129. // ) {
  1130. // this.$message.error("请上传节封面");
  1131. // return false;
  1132. // }
  1133. this.rulesTableSumbit();
  1134. } else {
  1135. return false;
  1136. }
  1137. });
  1138. },
  1139. async rulesTableSumbit() {
  1140. this.disabledBtn = true;
  1141. var dataInfos = {
  1142. status: 1,
  1143. sectionId: this.pageId,
  1144. businessList: this.newSujectApis,
  1145. viewSign: this.listData.viewSign,
  1146. coverUrl: this.listData.coverUrl,
  1147. name: this.listData.name,
  1148. prefixName: this.listData.prefixName,
  1149. publishStatus: this.listData.publishStatus,
  1150. teacherId: this.listData.teacherId,
  1151. };
  1152. if (this.listData.sectionType === 2) {
  1153. dataInfos.sectionType = 2;
  1154. dataInfos.liveUrl = this.listData.liveUrl;
  1155. dataInfos.liveStartTime = this.$methodsTools.time10to13(
  1156. this.listData.liveStartTime,
  1157. 1
  1158. );
  1159. dataInfos.liveEndTime = this.$methodsTools.time10to13(
  1160. this.listData.liveEndTime,
  1161. 1
  1162. );
  1163. if (
  1164. dataInfos.liveStartTime &&
  1165. dataInfos.liveEndTime &&
  1166. dataInfos.liveEndTime < dataInfos.liveStartTime
  1167. ) {
  1168. this.$message.warning("请检查直播开始与结束时间范围");
  1169. this.disabledBtn = false;
  1170. return;
  1171. }
  1172. dataInfos.durationTime =
  1173. dataInfos.liveEndTime - dataInfos.liveStartTime;
  1174. }
  1175. if (this.listData.sectionType === 1) {
  1176. dataInfos.sectionType = 1;
  1177. dataInfos.recordingUrl = this.listData.recordingUrl;
  1178. dataInfos.durationTime = this.$methodsTools.secondFormDate(
  1179. this.listData.durationTime
  1180. );
  1181. }
  1182. if (this.listData.sectionType === 3) {
  1183. dataInfos.sectionType = 3;
  1184. dataInfos.recordingUrl = this.listData.recordingUrl;
  1185. dataInfos.durationTime = this.$methodsTools.secondFormDate(
  1186. this.listData.durationTime
  1187. );
  1188. }
  1189. this.$api
  1190. .editCourseSection(dataInfos)
  1191. .then((res) => {
  1192. this.$message.success("修改成功");
  1193. setTimeout(() => {
  1194. this.$store
  1195. .dispatch("tagsView/exitView", this.$route)
  1196. .then((res) => {
  1197. if (this.$store.getters.festivalPage) {
  1198. this.$router.push({
  1199. path: this.$store.getters.festivalPage.name,
  1200. });
  1201. } else {
  1202. this.$router.push({
  1203. path: "festival",
  1204. });
  1205. }
  1206. });
  1207. }, 500);
  1208. })
  1209. .catch(() => {
  1210. this.disabledBtn = false;
  1211. });
  1212. },
  1213. backPage() {
  1214. this.$store.dispatch("tagsView/delView", this.$route).then((res) => {
  1215. if (this.$store.getters.festivalPage) {
  1216. this.$router.push({
  1217. path: this.$store.getters.festivalPage.name,
  1218. });
  1219. } else {
  1220. this.$router.push({
  1221. path: "festival",
  1222. });
  1223. }
  1224. });
  1225. },
  1226. closeType(index) {
  1227. this.sujectApis.splice(index, 1);
  1228. this.$nextTick(() => {
  1229. this.changeUrl();
  1230. });
  1231. },
  1232. changeLiveUrl() {
  1233. this.newLiveUrl.map((item) => {
  1234. if (item.id === this.newActiveLiveUrl) {
  1235. this.listData.liveUrl = item.liveUrl;
  1236. }
  1237. });
  1238. this.newActiveLiveUrl = "";
  1239. },
  1240. changeRecordingUrl1() {
  1241. this.newSteamUrl1.map((item) => {
  1242. if (item.id === this.newActiveRecordingUrl1) {
  1243. this.listData.recordingUrl = item.recordingVideoId;
  1244. this.getApiTime(item.recordingVideoId);
  1245. }
  1246. });
  1247. this.newActiveRecordingUrl1 = "";
  1248. },
  1249. changeRecordingUrl2() {
  1250. this.newSteamUrl2.map((item) => {
  1251. if (item.id === this.newActiveRecordingUrl2) {
  1252. this.listData.recordingUrl = item.playbackUrl;
  1253. this.getApiTime(item.recordingVideoId);
  1254. }
  1255. });
  1256. this.newActiveRecordingUrl2 = "";
  1257. },
  1258. getImgFile() {
  1259. var self = this;
  1260. var file = self.$refs.file.files[0];
  1261. if (file === undefined) {
  1262. self.$set(self.listData, "coverUrl", "");
  1263. return;
  1264. }
  1265. if (file.size > 0.3 * 1024 * 1024) {
  1266. self.$message.error("图片不得大于300kb");
  1267. return;
  1268. }
  1269. var type = self.$refs.file.value.toLowerCase().split(".").splice(-1);
  1270. if (
  1271. type[0] != "jpg" &&
  1272. type[0] != "png" &&
  1273. type[0] != "jpeg" &&
  1274. type[0] != "gif"
  1275. ) {
  1276. self.$message.error("上传格式需为:.jpg/.png/.jpeg/gif");
  1277. self.$refs.file.value = "";
  1278. return;
  1279. }
  1280. this.$upload.upload(file, 0).then((res) => {
  1281. self.listData.coverUrl = res;
  1282. self.$refs.listData.validateField("coverUrl");
  1283. });
  1284. },
  1285. },
  1286. destroyed() {
  1287. this.closePlayer();
  1288. },
  1289. };
  1290. </script>
  1291. <style lang="less" scoped>
  1292. /deep/.el-button {
  1293. border-radius: 8px;
  1294. }
  1295. /deep/.el-dialog {
  1296. border-radius: 8px;
  1297. .el-dialog__header {
  1298. padding: 0;
  1299. .hearders {
  1300. height: 40px;
  1301. display: flex;
  1302. align-items: center;
  1303. justify-content: space-between;
  1304. padding: 0px 18px 0px 20px;
  1305. border-bottom: 1px solid #e2e2e2;
  1306. .leftTitle {
  1307. font-size: 14px;
  1308. font-weight: bold;
  1309. color: #2f4378;
  1310. }
  1311. .rightBoxs {
  1312. display: flex;
  1313. align-items: center;
  1314. img {
  1315. width: 14px;
  1316. height: 14px;
  1317. margin-left: 13px;
  1318. cursor: pointer;
  1319. }
  1320. }
  1321. }
  1322. }
  1323. .el-dialog__footer {
  1324. padding: 0;
  1325. .dialog-footer {
  1326. padding: 0px 40px;
  1327. height: 70px;
  1328. border-top: 1px solid #e2e2e2;
  1329. display: flex;
  1330. align-items: center;
  1331. justify-content: flex-end;
  1332. }
  1333. }
  1334. }
  1335. .imgBox {
  1336. width: 100%;
  1337. // height: 210px;
  1338. border: 1px solid #e2e2e2;
  1339. border-radius: 8px;
  1340. padding: 8px 8px 3px;
  1341. display: flex;
  1342. flex-direction: column;
  1343. align-items: center;
  1344. .imgLabel {
  1345. flex: 1;
  1346. width: 100%;
  1347. border: 1px dotted #e2e2e2;
  1348. color: #999;
  1349. font-size: 14px;
  1350. cursor: pointer;
  1351. border-radius: 8px;
  1352. .msPhoto {
  1353. display: flex;
  1354. justify-content: center;
  1355. align-items: center;
  1356. max-width: 100%;
  1357. max-height: 270px;
  1358. img {
  1359. max-width: 100%;
  1360. max-height: 270px;
  1361. }
  1362. }
  1363. .imgbbx {
  1364. display: flex;
  1365. flex-direction: column;
  1366. align-items: center;
  1367. justify-content: center;
  1368. width: 100%;
  1369. height: 100%;
  1370. i {
  1371. font-weight: bold;
  1372. margin: 14px 0;
  1373. font-size: 24px;
  1374. }
  1375. }
  1376. }
  1377. p {
  1378. margin: 5px 0px;
  1379. }
  1380. }
  1381. .boxWidth {
  1382. width: 770px;
  1383. }
  1384. .numInputs {
  1385. width: 150px;
  1386. }
  1387. .checkboxSty {
  1388. max-height: 210px;
  1389. overflow: auto;
  1390. display: flex;
  1391. flex-direction: column;
  1392. }
  1393. .listBoxStys {
  1394. flex-shrink: 0;
  1395. padding: 0px 10px;
  1396. border-radius: 8px;
  1397. border: 1px solid #eee;
  1398. margin-right: 10px;
  1399. margin-bottom: 6px;
  1400. }
  1401. .closeIcons {
  1402. color: red;
  1403. cursor: pointer;
  1404. margin-left: 6px;
  1405. }
  1406. .ach {
  1407. display: flex;
  1408. align-items: center;
  1409. overflow: hidden;
  1410. }
  1411. .clh {
  1412. display: flex;
  1413. align-items: center;
  1414. flex-wrap: wrap;
  1415. }
  1416. .imgBoxins {
  1417. width: 375px;
  1418. height: 220px;
  1419. text-align: center;
  1420. img {
  1421. height: 100%;
  1422. }
  1423. }
  1424. .iconStsz {
  1425. font-size: 40px;
  1426. color: #67c23a;
  1427. cursor: pointer;
  1428. }
  1429. </style>