createLiveClass.vue 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565
  1. <template>
  2. <div class="m-container">
  3. <h2>
  4. <el-page-header @back="onCancel" :content="name"></el-page-header>
  5. </h2>
  6. <div class="m-core">
  7. <el-form ref="liveForm" :model="form" label-position="top">
  8. <el-alert title="课程规划" :closable="false" type="info" style="margin: 0 0 20px" />
  9. <el-row :gutter="20">
  10. <el-col :span="10">
  11. <el-form-item label="直播课标题" prop="roomTitle" :rules="[{ required: true, message: '请输入直播课标题' }]">
  12. <el-input v-model="form.roomTitle" placeholder="请输入直播课标题" maxlength="25"></el-input>
  13. </el-form-item>
  14. </el-col>
  15. <el-col :span="10">
  16. <el-form-item label="直播课内容" prop="liveRemark" :rules="[{ required: true, message: '请输入直播课内容' }]">
  17. <el-input type="textarea" v-model="form.liveRemark" placeholder="请输入直播课内容" maxlength="200"
  18. show-word-limit></el-input>
  19. </el-form-item>
  20. </el-col>
  21. <el-col :span="10">
  22. <el-form-item label="分部" prop="organIds" :rules="[{ required: true, message: '请选择分部' }]">
  23. <select-all v-model.trim="form.organIds" filterable placeholder="请选择分部" multiple clearable>
  24. <el-option v-for="(item, index) in selects.branchs" :key="index" :label="item.name"
  25. :value="item.id"></el-option>
  26. </select-all>
  27. </el-form-item>
  28. </el-col>
  29. <el-col :span="10">
  30. <el-form-item label="声部" prop="subjectIdList" :rules="[{ required: true, message: '请选择声部' }]">
  31. <el-select v-model.trim="form.subjectIdList" filterable clearable @change="onChangeSubject"
  32. placeholder="请选择声部" style="width: 100% !important">
  33. <el-option v-for="(item, index) in subjectList" :key="index" :value="item.id" :label="item.name" />
  34. </el-select>
  35. </el-form-item>
  36. </el-col>
  37. <el-col :span="10">
  38. <el-form-item label="指导老师" prop="teacher" :rules="[{ required: true, message: '请选择指导老师' }]">
  39. <el-select v-model.trim="form.teacher" filterable clearable placeholder="请选择指导老师"
  40. style="width: 100% !important" :disabled="!form.subjectIdList">
  41. <el-option v-for="(item, index) in teacherList" :key="index" :label="item.realName" :value="item.id" />
  42. </el-select>
  43. </el-form-item>
  44. </el-col>
  45. <el-col :span="10">
  46. <el-form-item label="乐团主管" prop="educationalTeacherId" :rules="[{ required: true, message: '请选择乐团主管' }]">
  47. <el-select v-model.trim="form.educationalTeacherId" filterable clearable style="width: 100% !important"
  48. :rules="[{ required: true, message: '请选择乐团主管' }]">
  49. <el-option v-for="(item, key) in educationList" :key="key" :label="item.userName" :value="item.userId" />
  50. </el-select>
  51. </el-form-item>
  52. </el-col>
  53. <el-col :span="10">
  54. <el-form-item label="课程购买时间" prop="signUpTimeList" :rules="[{ required: true, message: '请选择课程购买时间' }]">
  55. <el-date-picker style="width: 100%" v-model="form.signUpTimeList" :picker-options="pickerOptions"
  56. type="datetimerange" :default-time="['00:00:00', '23:59:59']" range-separator="-"
  57. start-placeholder="购买开始日期" end-placeholder="购买结束日期">
  58. </el-date-picker>
  59. </el-form-item>
  60. </el-col>
  61. <el-col :span="10">
  62. <el-form-item label="课时数" prop="onlineClassesNum" :rules="[{ required: true, message: '请输入课时数' }]">
  63. <el-input v-model="form.onlineClassesNum" placeholder="请输入课时数" maxlength="2" @input="val => {
  64. form.val = val.replace(/^[+]{0,1}(\d+)$/g, '');
  65. }
  66. " @change="() => {
  67. form.timeTable = []; // 课表重置
  68. }
  69. "></el-input>
  70. </el-form-item>
  71. </el-col>
  72. <el-col :span="10">
  73. <el-form-item label="课程时长" prop="singleClassMinuteId" :rules="[{ required: true, message: '请选择课程时长' }]">
  74. <el-select v-model.trim="form.singleClassMinuteId" filterable clearable style="width: 100% !important"
  75. placeholder="请选择课程时长" @change="onSingleClassChange">
  76. <el-option v-for="(item, key) in liveGroupList" :key="key" :label="item.singleClassMinutes"
  77. :value="item.id" />
  78. </el-select>
  79. </el-form-item>
  80. </el-col>
  81. <el-col :span="10">
  82. <el-form-item label="现价" prop="onlineClassesUnitPrice" :rules="[{ required: true, message: '请输入现价' }]">
  83. <el-input v-model="form.onlineClassesUnitPrice" placeholder="请输入现价"
  84. @keyup.native="keyupEvent($event)"></el-input>
  85. </el-form-item>
  86. </el-col>
  87. <el-col :span="10">
  88. <el-form-item label="原价" prop="offlineClassesUnitPrice" :rules="[{ required: true, message: '请输入原价' }]">
  89. <el-input v-model="form.offlineClassesUnitPrice" placeholder="请输入原价"
  90. @keyup.native="keyupEvent($event)"></el-input>
  91. </el-form-item>
  92. </el-col>
  93. </el-row>
  94. <el-button type="danger" @click="onTimeTable">点击排课</el-button>
  95. <el-table style="width: 100%; margin-top: 20px;" :header-cell-style="{ background: '#EDEEF0', color: '#444' }"
  96. :data="form.timeTable">
  97. <el-table-column align="center" label="课时">
  98. <template slot-scope="scope">
  99. 第{{ scope.$index + 1 }}课
  100. </template>
  101. </el-table-column>
  102. <el-table-column align="center" label="内容" width="150px" prop="teachingContent" key="teachingContent">
  103. <template slot-scope="scope">
  104. <!-- v-model="form.eclass[scope.$index].courseCurrentPrice" -->
  105. <el-form-item :prop="'timeTable.' + scope.$index + '.teachingContent'"
  106. :rules="[{ required: true, message: '请输入内容' }]" style="margin-bottom: 0;">
  107. <el-input v-model="scope.row.teachingContent" placeholder="请输入内容">
  108. </el-input>
  109. </el-form-item>
  110. </template>
  111. </el-table-column>
  112. <el-table-column align="center" label="技能/知识点掌握" width="220px" prop="teachingPoint" key="teachingPoint">
  113. <template slot-scope="scope">
  114. <el-form-item :prop="'timeTable.' + scope.$index + '.teachingPoint'"
  115. :rules="[{ required: true, message: '请输入技能/知识点掌握' }]" style="margin-bottom: 0;">
  116. <!-- v-model="scope.row.teachingPoint" -->
  117. <el-input v-model="form.timeTable[scope.$index].teachingPoint" placeholder="请输入技能/知识点掌握">
  118. </el-input>
  119. </el-form-item>
  120. </template>
  121. </el-table-column>
  122. <el-table-column align="center" prop="singleClassMinutes" label="时长"></el-table-column>
  123. <el-table-column align="center" label="课程日期">
  124. <template slot-scope="scope">
  125. <div>{{ scope.row.classDate | formatTimer }}</div>
  126. </template>
  127. </el-table-column>
  128. <el-table-column align="center" prop="startClassTimeStr" label="开始时间"></el-table-column>
  129. <el-table-column align="center" prop="endClassTimeStr" label="结束时间"></el-table-column>
  130. <el-table-column align="center" label="课程类型">
  131. <template slot-scope="scope">
  132. <div>{{ scope.row.teachMode | teachMode }}</div>
  133. </template>
  134. </el-table-column>
  135. </el-table>
  136. <el-alert title="直播课信息" :closable="false" type="info" style="margin: 20px 0" />
  137. <el-row :gutter="20">
  138. <el-col :span="6">
  139. <el-form-item label="直播设备" prop="os" :rules="[{ required: true, message: '请选择推广类型' }]">
  140. <el-radio-group v-model="form.os">
  141. <!-- 根据不同的模式,显示不同的直播设备 -->
  142. <el-radio v-if="serviceProvider === 'rongCloud'" label="pc">web</el-radio>
  143. <el-radio v-if="serviceProvider === 'tencentCloud'" label="client">客户端</el-radio>
  144. <el-radio label="mobile">手机</el-radio>
  145. </el-radio-group>
  146. </el-form-item>
  147. </el-col>
  148. <el-col :span="6">
  149. <el-form-item label="直播场景" prop="useScene" :rules="[{ required: true, message: '请选择直播场景' }]">
  150. <el-radio-group v-model="form.useScene">
  151. <el-radio label="NORMAL">普通模式</el-radio>
  152. <el-radio label="MUSIC">音乐模式</el-radio>
  153. </el-radio-group>
  154. </el-form-item>
  155. </el-col>
  156. <el-col :span="6">
  157. <el-form-item prop="roomConfig.whether_video" label="保存直播回放"
  158. :rules="[{ required: true, message: '是否保存直播回放' }]">
  159. <el-radio-group v-model="form.roomConfig.whether_video">
  160. <el-radio :label="0">是</el-radio>
  161. <el-radio :label="1">否</el-radio>
  162. </el-radio-group>
  163. </el-form-item>
  164. </el-col>
  165. <el-col :span="6">
  166. <el-form-item prop="roomConfig.whether_view_shop_cart" label="是否展示购物车"
  167. :rules="[{ required: true, message: '是否展示购物车' }]">
  168. <el-radio-group v-model="form.roomConfig.whether_view_shop_cart">
  169. <el-radio :label="0">是</el-radio>
  170. <el-radio :label="1">否</el-radio>
  171. </el-radio-group>
  172. </el-form-item>
  173. </el-col>
  174. <el-col :span="24">
  175. <el-form-item label="预热模板(模板使用于分享宣传图片)" prop="preTemplate" :rules="[{ required: true, message: '请选择预热模板' }]">
  176. <el-radio-group v-model="form.preTemplate">
  177. <div class="chioseWrap">
  178. <div class="chioseItem" @click="setPreTemplate(1)">
  179. <img src="./images/img1.png" alt="" />
  180. <i class="dotWrap" :class="form.preTemplate == 1 ? 'checked' : ''"></i>
  181. </div>
  182. <div class="chioseItem" @click="setPreTemplate(2)">
  183. <img src="./images/img2.png" alt="" />
  184. <i class="dotWrap" :class="form.preTemplate == 2 ? 'checked' : ''"></i>
  185. </div>
  186. <div class="chioseItem" @click="setPreTemplate(3)">
  187. <img src="./images/img3.png" alt="" />
  188. <i class="dotWrap" :class="form.preTemplate == 3 ? 'checked' : ''"></i>
  189. </div>
  190. </div>
  191. </el-radio-group>
  192. </el-form-item>
  193. </el-col>
  194. </el-row>
  195. <el-row>
  196. <el-col :span="24">
  197. <el-button type="primary" @click="onReset">重置</el-button>
  198. <el-button type="primary" @click="onSubmit">确定</el-button>
  199. </el-col>
  200. </el-row>
  201. </el-form>
  202. </div>
  203. <el-dialog title="排课" ref="maskForm" width="500px" :visible.sync="dialogFormVisible">
  204. <addLiveCourse :singleClassMinutes="form.singleClassMinutes" :signUpTimeList="form.signUpTimeList"
  205. :onlineCourseNum="form.onlineClassesNum" @close="dialogFormVisible = false" @confirm="onConfirm" />
  206. </el-dialog>
  207. </div>
  208. </template>
  209. <script>
  210. import dayjs from "dayjs";
  211. import deepClone from "@/helpers/deep-clone";
  212. import preview from "./modals/preview.vue";
  213. import addLiveCourse from "./modals/addLiveCourse.vue";
  214. import { sysTenantConfigAll } from "./api";
  215. import {
  216. getSubject,
  217. findTeacherByOrganId,
  218. getOrganRole
  219. } from "@/api/buildTeam";
  220. import { vipGroupCategory, createVip } from "@/api/vipSeting";
  221. export default {
  222. components: { preview, addLiveCourse },
  223. data() {
  224. return {
  225. name: "新建直播课",
  226. dialogFormVisible: false,
  227. form: {
  228. roomTitle: "", //
  229. liveRemark: "", // 内容
  230. organIds: [],
  231. subjectIdList: null, // 声部
  232. teacher: "", // 指导老师列表
  233. educationalTeacherId: null, // 乐团主管
  234. preTemplate: 1, // 模板
  235. signUpStart: null, // 开始时间
  236. signUpEnd: null, // 结束时间
  237. signUpTimeList: [], // 课程购买时间
  238. onlineClassesNum: null,
  239. singleClassMinuteId: null, //时长编号
  240. singleClassMinutes: null, // 时长
  241. onlineClassesUnitPrice: null, // 售价
  242. offlineClassesUnitPrice: null, // 原价
  243. os: "client", // 直播设备
  244. useScene: "NORMAL", // 直播场景
  245. popularizeType: "ALL", // 观看权限信息
  246. viewMode: "LOGIN",
  247. roomConfig: {
  248. whether_like: 0,
  249. whether_chat: 0,
  250. whether_video: 0,
  251. whether_mic: 0,
  252. whether_view_shop_cart: 1
  253. },
  254. timeTable: [], // 排课
  255. clientType: "TEACHER" // 主讲人身份 默认[老师]
  256. },
  257. serviceProvider: "tencentCloud", // 直播模式
  258. subjectList: [], // 声部列表
  259. teacherList: [], // 指导老师
  260. educationList: [], // 乐团主管
  261. liveGroupList: [], // 课时列表
  262. pickerOptions: {
  263. firstDayOfWeek: 1,
  264. disabledDate(time) {
  265. return time.getTime() + 86400000 <= new Date().getTime();
  266. }
  267. }
  268. };
  269. },
  270. async mounted() {
  271. this.$store.dispatch("setBranchs");
  272. await this.__init();
  273. },
  274. methods: {
  275. async onChangeSubject(val) {
  276. try {
  277. this.form.teacher = ""; // 重置指导老师
  278. // 根据科目id获取相应的老师
  279. await findTeacherByOrganId({
  280. subjectIds: val <= 0 ? null : val
  281. }).then(res => {
  282. if (res.code == 200) {
  283. this.teacherList = res.data;
  284. }
  285. });
  286. } catch { }
  287. },
  288. onCancel() {
  289. this.$store.dispatch("delVisitedViews", this.$route);
  290. this.$router.push("/liveClassManager?tabrouter=2");
  291. },
  292. setPreTemplate(index) {
  293. this.form.preTemplate = index;
  294. },
  295. async onSubmit() {
  296. this.$refs.liveForm.validate(async flag => {
  297. if (!flag) {
  298. this.onScrollError();
  299. return false;
  300. }
  301. try {
  302. const form = this.form;
  303. if (form.timeTable.length <= 0) {
  304. this.$message.error("请点击排课");
  305. return;
  306. }
  307. const timeTable = [];
  308. form.timeTable.forEach(item => {
  309. timeTable.push({
  310. classDate: item.classDate,
  311. actualTeacherId: form.teacher,
  312. startClassTimeStr: item.startClassTimeStr,
  313. endClassTimeStr: item.endClassTimeStr,
  314. teachMode: item.teachMode,
  315. teachingContent: item.teachingContent,
  316. teachingPoint: item.teachingPoint
  317. });
  318. });
  319. let obj = {
  320. courseSchedules: timeTable,
  321. vipGroupApplyBaseInfo: {
  322. groupType: "LIVE",
  323. vipGroupStudentCoursePrices: [],
  324. // coursesExpireDate: this.leftForm.courseEnd,
  325. // teacherSchoolId: this.leftForm.section,
  326. studentIdList: "",
  327. offlineClassesNum: 0,
  328. onlineClassesNum: form.onlineClassesNum || 0,
  329. offlineClassesUnitPrice: form.offlineClassesUnitPrice || 0,
  330. onlineClassesUnitPrice: form.onlineClassesUnitPrice || 0,
  331. registrationStartTime: dayjs(form.signUpTimeList[0]).format(
  332. "YYYY-MM-DD"
  333. ),
  334. paymentExpireDate: dayjs(form.signUpTimeList[0]).format(
  335. "YYYY-MM-DD"
  336. ),
  337. singleClassMinutes: form.singleClassMinutes,
  338. userId: form.teacher,
  339. // vipGroupActivityId: form.singleClassMinuteId,
  340. vipGroupCategoryId: form.singleClassMinuteId,
  341. onlineTeacherSalary: 0,
  342. offlineTeacherSalary: 0,
  343. giveTeachMode: "ONLINE",
  344. subjectIdList: form.subjectIdList,
  345. educationalTeacherId: form.educationalTeacherId,
  346. organId: -1,
  347. organIdList: form.organIds.join(",")
  348. },
  349. liveBroadcastRoom: {
  350. speakerId: form.teacher,
  351. clientType: "TEACHER",
  352. roomTitle: form.roomTitle,
  353. liveRemark: form.liveRemark,
  354. preTemplate: form.preTemplate,
  355. useScene: form.useScene,
  356. os: form.os,
  357. serviceProvider: form.serviceProvider,
  358. viewMode: form.viewMode,
  359. popularizeType: form.popularizeType,
  360. roomConfig: {
  361. ...form.roomConfig,
  362. subjectId: form.subjectIdList,
  363. groupType: "LIVE"
  364. }
  365. }
  366. };
  367. console.log(obj, "obj");
  368. createVip(obj).then(res => {
  369. if (res.code == 200) {
  370. this.$message.success("恭喜您创建成功");
  371. this.$store.dispatch("delVisitedViews", this.$route);
  372. this.$router.push({
  373. path: "/liveClassManager",
  374. query: {
  375. tabrouter: 2
  376. }
  377. });
  378. }
  379. });
  380. } catch (e) {
  381. console.log(e);
  382. }
  383. });
  384. },
  385. onReset() {
  386. // 重置
  387. this.form.timeTable = [];
  388. this.$refs.liveForm.resetFields();
  389. this.$nextTick(() => {
  390. let isError = document.getElementsByClassName("el-alert");
  391. isError[0].scrollIntoView({
  392. block: "center",
  393. behavior: "smooth"
  394. });
  395. });
  396. },
  397. // 点击排课
  398. async onTimeTable() {
  399. let count = 0;
  400. this.$refs.liveForm.validateField(
  401. ["signUpTimeList", "onlineClassesNum", "singleClassMinuteId"],
  402. valid => {
  403. count += 1;
  404. if (valid) {
  405. this.onScrollError();
  406. return;
  407. }
  408. if (count >= 3) {
  409. this.dialogFormVisible = true;
  410. }
  411. }
  412. );
  413. },
  414. onScrollError() {
  415. this.$nextTick(() => {
  416. let isError = document.getElementsByClassName("is-error");
  417. isError[0].scrollIntoView({
  418. block: "center",
  419. behavior: "smooth"
  420. });
  421. });
  422. },
  423. onSingleClassChange(val) {
  424. // 设置 - 课程时长切换时
  425. let onlinePrice = null;
  426. let offLinePrice = null;
  427. let minus = null;
  428. this.liveGroupList.forEach(item => {
  429. if (item.id === val) {
  430. onlinePrice = item.onlineClassesUnitPrice;
  431. offLinePrice = item.offlineClassesUnitPrice;
  432. minus = item.singleClassMinutes;
  433. }
  434. });
  435. this.form.onlineClassesUnitPrice = onlinePrice;
  436. this.form.offlineClassesUnitPrice = offLinePrice;
  437. this.form.singleClassMinutes = minus;
  438. this.form.timeTable = []; // 课表重置
  439. },
  440. onConfirm(val) {
  441. let tempVal = deepClone(val || []);
  442. tempVal.forEach(item => {
  443. item.teachingContent = "";
  444. item.teachingPoint = "";
  445. item.singleClassMinutes = this.form.singleClassMinutes;
  446. });
  447. this.form.timeTable = tempVal;
  448. console.log(this.form.timeTable, "time table");
  449. this.$forceUpdate();
  450. },
  451. async __init() {
  452. try {
  453. const findName = await sysTenantConfigAll({
  454. group: "LIVE_CLIENT"
  455. });
  456. if (findName.data && findName.data.length > 0) {
  457. findName.data.forEach(item => {
  458. if (item.paramName == "live_client") {
  459. this.serviceProvider = item.paranValue;
  460. this.form.os =
  461. this.serviceProvider == "tencentCloud" ? "client" : "pc";
  462. }
  463. });
  464. }
  465. // 获取 指导老师列表
  466. await getSubject({
  467. tenantId: 1
  468. }).then(res => {
  469. if (res.code == 200) {
  470. this.subjectList = [
  471. {
  472. id: -1,
  473. name: "乐理"
  474. },
  475. ...res.data
  476. ];
  477. }
  478. });
  479. // 获取乐团主管
  480. await getOrganRole().then(ruselt => {
  481. this.educationList = ruselt?.data?.EDUCATION;
  482. });
  483. // 获取课时数
  484. // 获取默认左边参数
  485. await vipGroupCategory({
  486. groupType: "live"
  487. }).then(res => {
  488. if (res.code == 200) {
  489. this.liveGroupList = res.data;
  490. }
  491. });
  492. } catch (e) {
  493. //
  494. console.log(e, "e info");
  495. }
  496. }
  497. }
  498. };
  499. </script>
  500. <style lang="scss" scoped>
  501. .chioseWrap {
  502. display: flex;
  503. flex-direction: row;
  504. justify-content: flex-start;
  505. .chioseItem {
  506. border-radius: 4px;
  507. overflow: hidden;
  508. position: relative;
  509. margin-right: 10px;
  510. width: 188px;
  511. height: 188px;
  512. cursor: pointer;
  513. .dotWrap {
  514. width: 21px;
  515. height: 21px;
  516. background: url("../../assets/images/icon_checkbox_default.png") no-repeat center;
  517. background-size: contain;
  518. display: block;
  519. position: absolute;
  520. top: 10px;
  521. right: 12px;
  522. overflow: hidden;
  523. &.checked {
  524. background: url("../../assets/images/icon_checkbox.png") no-repeat center;
  525. background-size: contain;
  526. }
  527. }
  528. }
  529. }
  530. ::v-deep .el-select>.el-input {
  531. height: 36px !important;
  532. }
  533. ::v-deep .select-all {
  534. .select {
  535. .el-input__inner {
  536. height: 36px !important;
  537. min-height: 36px !important;
  538. }
  539. }
  540. .btn {
  541. height: 36px !important;
  542. min-height: 36px !important;
  543. }
  544. }
  545. </style>