userCourseCatalogDetails.vue 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548
  1. <template>
  2. <div class="app-container">
  3. <div style="padding-bottom: 20px">
  4. <span v-if="courseName!=null">{{ courseName }}</span>
  5. </div>
  6. <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
  7. <el-form-item label="小节名称" prop="title">
  8. <el-input
  9. v-model="queryParams.title"
  10. placeholder="请输入小节名称"
  11. clearable
  12. size="small"
  13. @keyup.enter.native="handleQuery"
  14. />
  15. </el-form-item>
  16. <el-form-item>
  17. <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
  18. <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
  19. </el-form-item>
  20. </el-form>
  21. <el-table border v-loading="loading" :data="userCourseVideoList" @selection-change="handleSelectionChange">
  22. <el-table-column type="selection" width="55" align="center" />
  23. <el-table-column label="视频ID" align="center" prop="videoId" />
  24. <el-table-column label="小节名称" align="center" show-overflow-tooltip prop="title" />
  25. <el-table-column label="视频文件名称" align="center" show-overflow-tooltip prop="fileName" >
  26. </el-table-column>
  27. <el-table-column label="视频时长" align="center" prop="duration">
  28. <template slot-scope="{ row }">
  29. {{ formatDuration(row.duration) }}
  30. </template>
  31. </el-table-column>
  32. <el-table-column label="排序" align="center" prop="courseSort" />
  33. <el-table-column label="上传时间" align="center" prop="createTime" />
  34. <el-table-column label="默认红包" align="center" prop="redPacketMoney" />
  35. <el-table-column label="公司红包" align="center" prop="companyRedPacketMoney" />
  36. <el-table-column label="是否上架" align="center" prop="isOnPut">
  37. <template slot-scope="{ row }">
  38. <el-tag v-if="row.isOnPut == 0">是</el-tag>
  39. <el-tag type="danger" v-if="row.isOnPut == 1">否</el-tag>
  40. </template>
  41. </el-table-column>
  42. <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
  43. <template slot-scope="scope">
  44. <el-button
  45. size="mini"
  46. type="text"
  47. @click="handleDetails(scope.row)"
  48. >查看</el-button>
  49. <el-button
  50. size="mini"
  51. type="text"
  52. v-hasPermi="['course:userCourseVideo:edit']"
  53. @click="updateMoney(scope.row)"
  54. >设置红包金额</el-button>
  55. <el-button size="mini"
  56. type="text" @click="openTagDialog(scope.row)">
  57. 绑定看课标签
  58. </el-button>
  59. <el-button size="mini"
  60. type="text" v-if="projectFrom === 'hzyy'" @click="openDialog(scope.row)">
  61. 复制看课链接
  62. </el-button>
  63. </template>
  64. </el-table-column>
  65. </el-table>
  66. <pagination
  67. v-show="total>0"
  68. :total="total"
  69. :page.sync="queryParams.pageNum"
  70. :limit.sync="queryParams.pageSize"
  71. @pagination="getList"
  72. />
  73. <el-drawer
  74. :with-header="false"
  75. size="75%"
  76. :visible.sync="open" append-to-body>
  77. <userCourseVideoDetails ref="userCourseVideoDetails" />
  78. </el-drawer>
  79. <el-dialog title="设置红包金额" :visible.sync="moneyOpen" width="600px" append-to-body>
  80. <el-form ref="form" label-width="100px">
  81. <el-form-item label="红包金额" prop="corpId">
  82. <el-input-number v-model="redPacketMoneyForm.redPacketMoney" :min="0" :max="200" :step="0.1" :precision="2"></el-input-number>
  83. </el-form-item>
  84. </el-form>
  85. <div slot="footer" class="dialog-footer">
  86. <el-button type="primary" @click="submitForm">确 定</el-button>
  87. <el-button @click="cancel">取 消</el-button>
  88. </div>
  89. </el-dialog>
  90. <el-dialog
  91. title="生成链接"
  92. :visible.sync="dialogVisible"
  93. width="400px"
  94. @close="resetRoomForm"
  95. append-to-body>
  96. <el-form :model="roomLinkForm" label-width="120px">
  97. <!-- 新增下拉框 -->
  98. <el-form-item label="销售企微选择">
  99. <el-select
  100. v-model="roomLinkForm.qwUserId"
  101. placeholder="请选择销售企微"
  102. style="width: 100%">
  103. <el-option
  104. v-for="item in qwUserList"
  105. :key="item.id"
  106. :label="formatOptionLabel(item)"
  107. :value="item.id">
  108. </el-option>
  109. </el-select>
  110. </el-form-item>
  111. </el-form>
  112. <div slot="footer" class="dialog-footer">
  113. <el-button @click="dialogVisible = false">取消</el-button>
  114. <el-button type="primary" @click="confirm">确认</el-button>
  115. </div>
  116. </el-dialog>
  117. <AutoTagDialog
  118. :visible.sync="tagDialogVisible"
  119. :title="tagDialogTitle"
  120. :videoId="currentVideoId"
  121. append-to-body
  122. />
  123. </div>
  124. </template>
  125. <script>
  126. import {getVideoListByCourseId, updatePacketMoney, updateUserCourseVideoUpdate} from "@/api/course/userCourseVideo";
  127. import userCourseVideoDetails from '../../components/course/userCourseVideoDetails.vue';
  128. import {createLinkUrl, createRoomLinkUrl, queryQwIds} from "@/api/course/sopCourseLink";
  129. import AutoTagDialog from "@/views/components/tag/AutoTagDialog.vue";
  130. import {addTag, updateTag} from "@/api/tag/api";
  131. export default {
  132. name: "userCourseCatalog",
  133. components: {
  134. userCourseVideoDetails,
  135. AutoTagDialog
  136. },
  137. props: {
  138. video: {
  139. type: Object,
  140. required: true,
  141. },
  142. },
  143. data() {
  144. return {
  145. currentRow: null,
  146. // 假设这里有当前课程小节的数据传入,里面含id等
  147. tagGroups: [],
  148. tagsInGroup: [],
  149. tagDialogVisible: false,
  150. tagDialogTitle: "",
  151. tagDialogFormData: null,
  152. currentVideoId:null,
  153. projectFrom:process.env.VUE_APP_PROJECT_FROM,
  154. tagDialog: {
  155. videoId: null,
  156. groupId: null,
  157. watchTagId: null,
  158. finishTagId: null,
  159. tgId: null,
  160. watchingTgId: null,
  161. watchedTgId: null
  162. },
  163. linkForm:{
  164. days:null,
  165. courseId:null,
  166. videoId:null
  167. },
  168. roomLinkForm:{
  169. courseId:null,
  170. videoId:null,
  171. qwUserId:null,
  172. qwUserName:null,
  173. corpId:null,
  174. title:null,
  175. },
  176. dialogVisible: false, // 控制弹框显示
  177. //短链
  178. sortLink:'',
  179. //课题
  180. questionBank:{
  181. title:'',
  182. open:false,
  183. },
  184. moneyOpen:false,
  185. videoUrl: "",
  186. uploadTypeOptions: [
  187. { dictLabel: "线路一", dictValue: 2 },
  188. { dictLabel: "线路二", dictValue: 1 },
  189. { dictLabel: "线路三", dictValue: 3 },
  190. ],
  191. uploadLoading:false,
  192. courseId:null,
  193. videoName:'',
  194. title: "",
  195. redPacketMoneyForm:{
  196. redPacketMoney:null,
  197. voidId:null
  198. },
  199. // 是否显示弹出层
  200. open: false,
  201. uploadUrl:process.env.VUE_APP_BASE_API+"/common/uploadHuaWeiObs",
  202. baseUrl: process.env.VUE_APP_BASE_API,
  203. typeOptions:[],
  204. files:[],
  205. fileList: [],
  206. // 上传成功后的地址
  207. videoURL: '',
  208. // 进度条百分比
  209. progress: 0,
  210. // 上传视频获取成功后拿到的fileID【备用】
  211. fileId: '',
  212. courseName:null,
  213. userCourseVideoList:[],
  214. fetchingQwIds: false,
  215. qwUserList:[],
  216. total: 0,
  217. queryParams: {
  218. pageNum: 1,
  219. pageSize: 10,
  220. courseId:null,
  221. title:null
  222. },
  223. // 显示搜索条件
  224. showSearch: true,
  225. // 遮罩层
  226. loading: true,
  227. // 导出遮罩层
  228. exportLoading: false,
  229. // 选中数组
  230. ids: [],
  231. // 非单个禁用
  232. single: true,
  233. // 非多个禁用
  234. multiple: true,
  235. // 表单参数
  236. form: {},
  237. // 表单校验
  238. rules: {
  239. title: [
  240. { required: true, message: "小节名称不能为空", trigger: "change" }
  241. ],
  242. courseSort: [
  243. { required: true, message: "排序不能为空", trigger: "change" }
  244. ],
  245. }
  246. }
  247. },
  248. created() {
  249. this.getDicts("sys_course_temp_type").then(response => {
  250. this.typeOptions = response.data;
  251. });
  252. },
  253. methods: {
  254. formatOptionLabel(item) {
  255. return item.corpName ? `${item.qwUserName} (${item.corpName})` : item.qwUserName;
  256. },
  257. closeTagDialog(){
  258. this.tagDialogVisible = false;
  259. },
  260. openTagDialog(row) {
  261. this.currentRow = row;
  262. this.tagDialogVisible = true;
  263. this.currentVideoId = row.videoId;
  264. },
  265. updateTagsInGroup(groupId) {
  266. let tagGroup = this.tagGroups.find(e=> e.groupId === groupId);
  267. this.tagsInGroup = tagGroup.tag || [];
  268. // 切换组时清空标签选择
  269. this.tagDialog.watchTagId = null;
  270. this.tagDialog.finishTagId = null;
  271. this.tagDialog.tgId = tagGroup.id;
  272. },
  273. resetDialog() {
  274. this.tagDialog = {
  275. videoId: null,
  276. groupId: null,
  277. watchTagId: null,
  278. finishTagId: null,
  279. };
  280. this.tagGroups = [];
  281. this.tagsInGroup = [];
  282. },
  283. // 打开弹框
  284. openDialog(row) {
  285. if (!this.fetchingQwIds) {
  286. this.fetchingQwIds = true;
  287. queryQwIds().then(response => {
  288. if (response.code === 200){
  289. this.qwUserList = response.list;
  290. }
  291. }).finally(() => {
  292. this.fetchingQwIds = false;
  293. // 在请求完成后再显示弹框
  294. this.showDialog(row);
  295. });
  296. } else {
  297. // 如果已经在请求中,直接显示弹框
  298. this.showDialog(row);
  299. }
  300. },
  301. // 新增方法:显示弹框
  302. showDialog(row) {
  303. this.roomLinkForm.courseId = row.courseId;
  304. this.roomLinkForm.videoId = row.videoId;
  305. this.roomLinkForm.title = row.title;
  306. this.dialogVisible = true;
  307. },
  308. // 确认按钮操作
  309. confirm() {
  310. if (!this.roomLinkForm.qwUserId) {
  311. this.$message.error("请选择销售企微!");
  312. return;
  313. }
  314. // 获取选中的用户信息
  315. const selectedUser = this.qwUserList.find(user => user.id === this.roomLinkForm.qwUserId);
  316. if (selectedUser) {
  317. // 将用户信息填充到表单中
  318. this.roomLinkForm.qwUserName = selectedUser.qwUserName;
  319. this.roomLinkForm.corpId = selectedUser.corpId;
  320. console.log(this.roomLinkForm);
  321. // 调用创建链接的API
  322. createRoomLinkUrl(this.roomLinkForm).then(response => {
  323. if (response.code === 200){
  324. this.msgSuccess("创建成功");
  325. this.copyLink(response.link);
  326. console.log(response.link);
  327. }
  328. });
  329. }
  330. // 关闭弹框
  331. this.dialogVisible = false;
  332. },
  333. // 重置表单
  334. resetForm() {
  335. this.linkForm={
  336. days:null,
  337. courseId:null,
  338. videoId:null
  339. }
  340. },
  341. resetRoomForm() {
  342. this.roomLinkForm={
  343. courseId:null,
  344. videoId:null,
  345. qwUserId:null,
  346. }
  347. },
  348. handleCreateLink(){
  349. createLinkUrl(this.linkForm).then(response => {
  350. if (response.code === 200){
  351. this.copyLink(response.url);
  352. }
  353. });
  354. },
  355. copyLink(url) {
  356. const link = url;
  357. // navigator clipboard 需要https等安全上下文
  358. if (navigator.clipboard && window.isSecureContext) {
  359. // navigator clipboard 向剪贴板写文本
  360. navigator.clipboard.writeText(link).then(() => {
  361. this.$message.success('链接已复制到剪贴板');
  362. });
  363. } else {
  364. // document.execCommand('copy') 向剪贴板写文本
  365. let input = document.createElement('input')
  366. input.style.position = 'fixed'
  367. input.style.top = '-10000px'
  368. input.style.zIndex = '-999'
  369. document.body.appendChild(input)
  370. input.value = link
  371. input.focus()
  372. input.select()
  373. try {
  374. let result = document.execCommand('copy')
  375. document.body.removeChild(input)
  376. if (!result || result === 'unsuccessful') {
  377. this.$message.error('复制失败');
  378. console.log('复制失败')
  379. } else {
  380. this.$message.success('链接已复制到剪贴板');
  381. console.log('复制成功')
  382. }
  383. } catch (e) {
  384. document.body.removeChild(input)
  385. alert('当前浏览器不支持复制功能,请检查更新或更换其他浏览器操作')
  386. }
  387. }
  388. },
  389. formatDuration(seconds) {
  390. if (seconds === null || seconds === undefined) {
  391. return '未上传视频'; // 或者您可以根据具体需求返回其他默认值
  392. }
  393. const hours = Math.floor(seconds / 3600);
  394. const minutes = Math.floor((seconds % 3600) / 60);
  395. const remainingSeconds = seconds % 60;
  396. const formattedHours = hours > 0 ? hours.toString() + ':' : '';
  397. const formattedMinutes = minutes.toString().padStart(2, '0');
  398. const formattedSeconds = remainingSeconds.toString().padStart(2, '0');
  399. return `${formattedHours}${formattedMinutes}:${formattedSeconds}`;
  400. },
  401. getDetails(courseId,courseName) {
  402. this.courseName = courseName
  403. this.courseId = courseId;
  404. this.queryParams.courseId = courseId;
  405. this.getList();
  406. },
  407. getList() {
  408. this.loading = true;
  409. getVideoListByCourseId(this.queryParams).then(response => {
  410. this.userCourseVideoList = response.rows;
  411. this.total = response.total;
  412. this.loading = false;
  413. });
  414. },
  415. // 取消按钮
  416. cancel() {
  417. this.open = false;
  418. this.reset();
  419. },
  420. updateMoney(row){
  421. this.redPacketMoneyForm.redPacketMoney=row.companyRedPacketMoney;
  422. this.redPacketMoneyForm.videoId=row.videoId;
  423. this.moneyOpen=true;
  424. },
  425. submitForm(){
  426. updatePacketMoney(this.redPacketMoneyForm).then(response => {
  427. this.msgSuccess("修改成功");
  428. this.moneyOpen=false;
  429. this.getList()
  430. });
  431. },
  432. updateMoneycancel(){
  433. this.moneyOpen=false;
  434. },
  435. // 表单重置
  436. reset() {
  437. this.form = {
  438. videoId: null,
  439. title: null,
  440. description: null,
  441. url: null,
  442. thumbnail: null,
  443. duration: null,
  444. createTime: null,
  445. uploadType:null,
  446. lineOne:null,
  447. lineTwo:null,
  448. lineThree:null,
  449. fileName:null,
  450. userId: null,
  451. cateId: null,
  452. courseId: null,
  453. likes: null,
  454. views: null,
  455. comments: null,
  456. status: 0,
  457. courseSort: 1,
  458. isHot: null,
  459. isShow: null,
  460. isAudit: null,
  461. auditBy: null,
  462. auditTime: null,
  463. updateTime: null,
  464. source: null,
  465. isDel: null,
  466. shares: null,
  467. tags: null,
  468. productId: null,
  469. productJson: null,
  470. questionBankId:null,
  471. questionBankList:[],
  472. };
  473. this.videoURL = '';
  474. this.progress=0;
  475. this.resetForm("form");
  476. },
  477. /** 搜索按钮操作 */
  478. handleQuery() {
  479. this.queryParams.pageNum = 1;
  480. this.getList();
  481. },
  482. /** 重置按钮操作 */
  483. resetQuery() {
  484. this.resetForm("queryForm");
  485. this.queryParams.title = null;
  486. this.handleQuery();
  487. },
  488. // 多选框选中数据
  489. handleSelectionChange(selection) {
  490. this.ids = selection.map(item => item.courseId)
  491. this.single = selection.length!==1
  492. this.multiple = !selection.length
  493. },
  494. /** 修改按钮操作 */
  495. handleDetails(row) {
  496. this.open=true;
  497. setTimeout(() => {
  498. this.$refs.userCourseVideoDetails.getDetails(row.videoId);
  499. }, 500);
  500. }
  501. }
  502. }
  503. </script>
  504. <style>
  505. .avatar-uploader .el-upload {
  506. border: 1px dashed #d9d9d9;
  507. border-radius: 6px;
  508. cursor: pointer;
  509. position: relative;
  510. overflow: hidden;
  511. }
  512. .avatar-uploader .el-upload:hover {
  513. border-color: #409EFF;
  514. }
  515. .avatar-uploader-icon {
  516. font-size: 28px;
  517. color: #8c939d;
  518. width: 150px;
  519. height: 150px;
  520. line-height: 150px;
  521. text-align: center;
  522. }
  523. </style>