program.vue 39 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048
  1. <template>
  2. <div class="program">
  3. <van-cell-group class="van-cell-group--inset">
  4. <van-cell :title="vipGroup.name" class="titleContent" title-class="titleStyle" label-class="labelStyle">
  5. <template #label>
  6. <p>{{ vipGroup.description }}</p>
  7. <!-- <p>排课时间范围:{{ vipGroup.coursesStartTime }} 至 {{ vipGroup.coursesEndTime }}</p> -->
  8. </template>
  9. </van-cell>
  10. </van-cell-group>
  11. <h2 class="van-block__title">{{ typeStatus ? '付费' : '赠送' }}课程排课</h2>
  12. <van-cell-group>
  13. <van-field
  14. :value="typeStatus ? courseType[vipGroup.courseType] : courseType[vipGroup.giveCourseType]"
  15. label="课程类型"
  16. :readonly="true"
  17. input-align="right"
  18. size="large"
  19. placeholder="请选择"
  20. />
  21. <van-field
  22. :value="typeStatus ? vipGroup.vipGroupCategoryNames : vipGroup.giveCategoryName"
  23. label="课程形式"
  24. v-if="courseTypeIsVip"
  25. :readonly="true"
  26. input-align="right"
  27. size="large"
  28. placeholder="请选择"
  29. />
  30. <van-field
  31. v-model="formName.subjectListName"
  32. @click="onGetSheetList('subjectList')"
  33. label="排课声部"
  34. :readonly="true"
  35. input-align="right"
  36. is-link
  37. size="large"
  38. placeholder="请选择"
  39. />
  40. <van-field
  41. v-model="formName.educationalTeacherName"
  42. @click="onGetSheetList('teacherList')"
  43. label="乐团主管"
  44. :readonly="true"
  45. input-align="right"
  46. is-link
  47. size="large"
  48. placeholder="请选择"
  49. />
  50. </van-cell-group>
  51. <template v-if="studentList.length > 0">
  52. <h2 class="van-block__title">上课学员</h2>
  53. <van-cell-group>
  54. <van-cell title-style="flex: 1 auto; color: #1A1A1A;" size="large" v-for="(item, index) in studentList" :key="index">
  55. <template #title>
  56. {{ item.username }} - {{ item.phone }}
  57. </template>
  58. <template #default>
  59. <span @click="onDelete('student', item)"><van-icon name="delete-o" size=".14rem" /> <span style="font-size: .12rem;">删除</span></span>
  60. </template>
  61. </van-cell>
  62. </van-cell-group>
  63. </template>
  64. <div class="addButton" @click="studentStatus = true">
  65. <van-icon name="plus" /> 添加学员
  66. </div>
  67. <h2 class="van-block__title">课时组成</h2>
  68. <van-cell-group>
  69. <van-field
  70. v-if="statusList.hasOnline && teachMode == -1"
  71. v-model="form.onlineClassesNums"
  72. @keyup="onClassKeyUp"
  73. label="线上课次数"
  74. input-align="right"
  75. size="large"
  76. placeholder="请输入次数"
  77. type="number"
  78. />
  79. <van-field
  80. v-if="statusList.hasOffline && teachMode == -1"
  81. v-model="form.offlineClassesNums"
  82. @keyup="onClassKeyUp('offLine')"
  83. label="线下课次数"
  84. input-align="right"
  85. size="large"
  86. placeholder="请输入次数"
  87. type="number"
  88. />
  89. <van-field
  90. v-if="tempOfflineNum > 0"
  91. v-model="formName.teacherSchoolName"
  92. @click="onGetSheetList('teacherSchool')"
  93. label="线下课地址"
  94. :readonly="true"
  95. input-align="right"
  96. is-link
  97. size="large"
  98. placeholder="请选择"
  99. />
  100. <!-- 不影响判断逻辑 -->
  101. <van-field
  102. v-if="isMusicTheory"
  103. :value="'1-' + (form.studentNum || 0) + '人'"
  104. label="班级人数"
  105. :readonly="true"
  106. input-align="right"
  107. size="large"
  108. />
  109. <van-field
  110. v-else
  111. :value="(form.studentNum || 0) + '人'"
  112. label="班级人数"
  113. :readonly="true"
  114. input-align="right"
  115. size="large"
  116. />
  117. <van-field
  118. :value="(form.singleClassMinutes || 0) + '分钟'"
  119. label="单课时时长"
  120. :readonly="true"
  121. input-align="right"
  122. size="large"
  123. />
  124. </van-cell-group>
  125. <h2 class="van-block__title">课时安排</h2>
  126. <van-cell-group>
  127. <van-cell
  128. title="最早排课时间"
  129. :readonly="true"
  130. v-if="vipGroup.coursesStartTime"
  131. input-align="right"
  132. size="large"
  133. value-class="showText"
  134. :value="vipGroup.coursesStartTime"
  135. >
  136. </van-cell>
  137. <van-cell
  138. title="最晚排课时间"
  139. :readonly="true"
  140. v-if="vipGroup.coursesEndTime"
  141. input-align="right"
  142. size="large"
  143. value-class="showText"
  144. :value="vipGroup.coursesEndTime"
  145. >
  146. </van-cell>
  147. <!-- 为了处理,付费网管课程 -->
  148. <!-- {{ !courseTypeIsVip && isLimitNum && typeStatus ? false : true }} -->
  149. <van-field
  150. v-model="form.totalClassTime"
  151. label="课时总数"
  152. :readonly="!courseTypeIsVip && !isLimitNum && typeStatus ? false : true"
  153. input-align="right"
  154. size="large"
  155. placeholder="请输入排课课时数"
  156. >
  157. <template #extra v-if="form.totalClassTime">
  158. <span style="color: #808080; font-size: 16px;">课时</span>
  159. </template>
  160. </van-field>
  161. <van-field
  162. v-model="form.courseStart"
  163. label="排课开始时间"
  164. :readonly="true"
  165. input-align="right"
  166. is-link
  167. size="large"
  168. placeholder="请选择"
  169. @click="dataForm.status = true"
  170. />
  171. </van-cell-group>
  172. <van-cell-group :border="false" style="margin-top: .1rem">
  173. <van-cell
  174. title-class="title-time"
  175. v-for="(item, index) in scheduleList"
  176. :key="index"
  177. >
  178. <template slot="title">
  179. <span class="online">{{ item.type }}</span>
  180. <span class="week">{{ item.weekStr }}</span>
  181. <span class="timer">{{ item.startTime + "~" + item.endTime }}</span>
  182. </template>
  183. <template slot="default">
  184. <span @click="onDelete('class', item)"><van-icon name="delete-o" size=".14rem" /> <span style="font-size: .12rem;">删除</span></span>
  185. </template>
  186. </van-cell>
  187. </van-cell-group>
  188. <div class="addButton" @click="teachingStatus = true">
  189. <van-icon name="plus" /> 添加课时安排
  190. </div>
  191. <van-cell-group>
  192. <van-field
  193. label="排课列表"
  194. v-if="scheduleList.length > 0"
  195. disabled
  196. input-align="right"
  197. @click="onShowTimeTable"
  198. is-link
  199. size="large"
  200. />
  201. </van-cell-group>
  202. <div class="button-group">
  203. <van-button type="primary" @click="onSubmit" round size="large">确认</van-button>
  204. </div>
  205. <!-- 选择上课学员 -->
  206. <van-popup
  207. v-model="studentStatus"
  208. :lock-scroll="true"
  209. position="bottom"
  210. :style="{ height: '5.16rem','overflow': 'hidden' }"
  211. class="studentChose"
  212. >
  213. <student-list
  214. @close="studentStatus = false"
  215. :studentList="studentList"
  216. :activityId="activityId"
  217. :studentNum="form.studentNum"
  218. :courseTypeIsVip="courseTypeIsVip"
  219. :typeStatus="typeStatus"
  220. :isMusicTheory="isMusicTheory"
  221. @submit="onSelectStudent" />
  222. </van-popup>
  223. <!-- 排课开始时间 -->
  224. <van-popup v-model="dataForm.status" position="bottom">
  225. <van-datetime-picker
  226. v-model="dataForm.currentDate"
  227. type="date"
  228. :min-date="dataForm.minDate"
  229. :max-date="dataForm.maxDate"
  230. :formatter="formatter"
  231. @cancel="dataForm.status = false"
  232. @confirm="onCurrentConfirm"
  233. />
  234. </van-popup>
  235. <!-- 课时安排 -->
  236. <van-popup v-model="teachingStatus" position="bottom">
  237. <course-modal :scheduleList="scheduleList" :singleClassMinutes="form.singleClassMinutes" @close="teachingStatus = false" />
  238. </van-popup>
  239. <van-popup v-model="sheetForm.sheetStatus" position="bottom">
  240. <van-picker
  241. :loading="sheetForm.loading"
  242. :default-index="sheetForm.index"
  243. :columns="sheetForm.columns"
  244. show-toolbar
  245. @cancel="sheetForm.sheetStatus = false"
  246. @confirm="onSheetConfirm"
  247. />
  248. </van-popup>
  249. <!-- 课表展示 -->
  250. <van-popup v-model="statusList.classTime" position="bottom" >
  251. <van-row>
  252. <van-col span="12">上课类型</van-col>
  253. <van-col span="12">上课时间</van-col>
  254. </van-row>
  255. <div class="tableContainer">
  256. <van-row v-for="(item, index) in timeTable" :key="index">
  257. <van-col span="12">
  258. {{ item.teachMode == "ONLINE" ? "线上" : "线下" }}
  259. </van-col>
  260. <van-col span="12">
  261. {{ item.classDate }} {{ item.startClassTimeStr }}
  262. </van-col>
  263. </van-row>
  264. </div>
  265. </van-popup>
  266. </div>
  267. </template>
  268. <script>
  269. import dayjs from 'dayjs'
  270. import studentList from './modal/studentList'
  271. import courseModal from './modal/course'
  272. import { courseType } from '../../constant'
  273. import { getActivityWaitCourseStudentNum, createVipGroup, createPracticeGroup } from './api'
  274. import { findSubSubjects, findEducationUsers, findVipSchoolByTeacher2 } from "@/api/teacher";
  275. export default {
  276. components: { studentList, courseModal },
  277. data() {
  278. const query = this.$route.query
  279. return {
  280. courseType,
  281. type: query.type,
  282. activityId: query.activityId,
  283. vipDetail: {},
  284. vipGroup: {},
  285. studentStatus: false,
  286. studentList: [],
  287. checkboxSelectIds: [],
  288. teachMode: null, // -1:所有;0:线上;1:线下"
  289. sheetForm: {
  290. // 上拉弹窗
  291. currentType: null, // 当前选择的类型
  292. sheetStatus: false,
  293. loading: true, // 加载数据
  294. index: 0, // 选中的索引值
  295. columns: [],
  296. },
  297. timeTable: [], // 生成的课表
  298. loadData: {
  299. // 下拉加载数据
  300. subjectList: [], // 声部列表
  301. teacherList: [],
  302. teacherSchool: [], // 线下课地址
  303. },
  304. tempOfflineNum: 0, // 临时存放线下课次数
  305. form: {
  306. vipGroupCategoryId: null,
  307. subjectIdList: null,
  308. educationalTeacherId: null,
  309. singleClassMinutes: null,
  310. onlineClassesNums: null,
  311. offlineClassesNums: null,
  312. totalClassTime: null, // 总课时数
  313. studentNum: null, // 每班人数
  314. courseStart: null, // 排课开始时间
  315. teacherSchoolId: null,
  316. vipGroupActivityId: query.activityId,
  317. },
  318. formName: {
  319. vipGroupCategoryId: null,
  320. subjectListName: null,
  321. subjectListIndex: 0, // 声部名称
  322. educationalTeacherName: null, // 乐团主管
  323. educationalTeacherIndex: 0,
  324. teacherSchoolName: null,
  325. teacherSchoolIndex: 0, // 线下课地址
  326. },
  327. statusList: {
  328. hasOnline: false, // 是否显示线上
  329. hasOffline: false, // 是否显示线下
  330. classTime: false, // 查看排课列表
  331. },
  332. scheduleList: [],
  333. // 排课弹窗
  334. teachingStatus: false, // 课时安排状态
  335. dataForm: {
  336. // 时间下拉框
  337. status: false,
  338. minDate: new Date(),
  339. maxDate: new Date(2035, 10, 1),
  340. currentDate: new Date(),
  341. },
  342. isMusicTheory: false,
  343. }
  344. },
  345. computed: {
  346. typeStatus() { // 是否是付费课程
  347. return this.type == 'pay' ? true : false
  348. },
  349. courseTypeIsVip() { // 目前只有两种课程,VIP 网管课,则可以这样判断
  350. const type = this.typeStatus ? this.vipGroup.courseType : this.vipGroup.giveCourseType
  351. return type == 'VIP' ? true : false
  352. },
  353. isLimitNum() { // 是否限制排课
  354. return this.vipGroup.minCourseNum > 0 ? true : false
  355. }
  356. },
  357. mounted() {
  358. this.__init()
  359. },
  360. methods: {
  361. async __init() {
  362. try {
  363. let res = await getActivityWaitCourseStudentNum({ activityId: this.activityId })
  364. this.vipDetail = res.data
  365. let vipGroup = res.data.vipGroupActivity
  366. vipGroup.coursesStartTime = vipGroup.coursesStartTime ? dayjs(vipGroup.coursesStartTime).format('YYYY-MM-DD') : null
  367. vipGroup.coursesEndTime = vipGroup.coursesEndTime ? dayjs(vipGroup.coursesEndTime).format('YYYY-MM-DD') : null
  368. this.vipGroup = vipGroup
  369. let form = this.form
  370. // 课程形式
  371. form.vipGroupCategoryId = this.typeStatus ? vipGroup.vipGroupCategoryIdList : vipGroup.giveCategoryId
  372. // 单课时长
  373. form.singleClassMinutes = this.typeStatus ? vipGroup.singleCourseTime : vipGroup.giveSingleCourseTime
  374. const name = this.typeStatus ? vipGroup.vipGroupCategoryNames : vipGroup.giveCategoryName
  375. this.isMusicTheory = name === '乐理课'
  376. if(this.courseTypeIsVip) {
  377. // 每班人数
  378. form.studentNum = this.typeStatus ? vipGroup.vipGroupCategoryNum : vipGroup.giveCategoryNum
  379. this.statusList.hasOnline = this.typeStatus ? this.formatStatus('online', vipGroup.teachMode) : this.formatStatus('online', vipGroup.giveTeachMode)
  380. this.statusList.hasOffline = this.typeStatus ? this.formatStatus('offline', vipGroup.teachMode) : this.formatStatus('offline', vipGroup.giveTeachMode)
  381. } else {
  382. form.totalClassTime = null
  383. form.studentNum = 1
  384. this.statusList.hasOnLine = false
  385. this.statusList.hasOffLine = false
  386. }
  387. if(this.isLimitNum || !this.typeStatus) { // 是否限制了排课
  388. // 排课次数,活动排课没有范围一说,最大次数和最小次数必须一致
  389. form.totalClassTime = this.typeStatus ? vipGroup.minCourseNum : vipGroup.giveCourseNum
  390. }
  391. // 如果
  392. if(this.teachMode == 0) {
  393. form.onlineClassesNums = form.totalClassTime || 0
  394. form.offlineClassesNums = 0
  395. } else if(this.teachMode == 1) {
  396. form.onlineClassesNums = 0
  397. form.offlineClassesNums = form.totalClassTime || 0
  398. this.tempOfflineNum = form.totalClassTime || 0
  399. }
  400. } catch {
  401. //
  402. }
  403. },
  404. onGetSheetList(name) {
  405. // 获取科目列表
  406. let sheetForm = this.sheetForm;
  407. sheetForm.columns = [];
  408. sheetForm.sheetStatus = true;
  409. sheetForm.loading = true;
  410. sheetForm.currentType = name;
  411. sheetForm.index = 0;
  412. let arr = this.loadData[name];
  413. if (arr.length > 0) {
  414. sheetForm.columns = arr;
  415. sheetForm.index = this.formName[name + "Index"];
  416. sheetForm.loading = false;
  417. } else {
  418. this.onLoadingData(name);
  419. }
  420. },
  421. onLoadingData() {
  422. // 加载数据
  423. let sheetForm = this.sheetForm;
  424. if (sheetForm.currentType == "subjectList") {
  425. // 声部列表
  426. findSubSubjects().then((res) => {
  427. let result = res.data;
  428. if (result.code == 200 && result.data.length > 0) {
  429. let tempArr = [];
  430. result.data.forEach((item) => {
  431. item.value = item.id;
  432. item.text = item.name;
  433. tempArr.push(item);
  434. });
  435. this.loadData.subjectList = tempArr;
  436. sheetForm.columns = tempArr;
  437. sheetForm.loading = false;
  438. } else {
  439. this.$toast("暂无科目列表");
  440. sheetForm.loading = false;
  441. }
  442. });
  443. } else if (sheetForm.currentType == "teacherSchool") {
  444. // 教师教学点
  445. findVipSchoolByTeacher2().then((res) => {
  446. let result = res.data;
  447. if (result.code == 200 && result.data.length > 0) {
  448. let tempArr = [];
  449. result.data.forEach((item) => {
  450. item.value = item.id;
  451. item.text = item.name;
  452. tempArr.push(item);
  453. });
  454. this.loadData.teacherSchool = tempArr;
  455. sheetForm.columns = tempArr;
  456. sheetForm.loading = false;
  457. } else {
  458. this.$toast("暂无教学点");
  459. sheetForm.loading = false;
  460. }
  461. });
  462. } else if (sheetForm.currentType == "teacherList") {
  463. // 乐团主管
  464. findEducationUsers().then((res) => {
  465. let result = res.data;
  466. const EDUCATION = result.data.EDUCATION || [];
  467. if (result.code == 200 && EDUCATION.length > 0) {
  468. let tempArr = [];
  469. EDUCATION.forEach((item) => {
  470. item.value = item.userId;
  471. item.text = item.userName;
  472. tempArr.push(item);
  473. });
  474. this.loadData.teacherList = tempArr;
  475. sheetForm.columns = tempArr;
  476. sheetForm.loading = false;
  477. } else {
  478. this.$toast("暂无乐团主管");
  479. sheetForm.loading = false;
  480. }
  481. });
  482. }
  483. },
  484. onSheetConfirm(value, index) {
  485. // 上拉弹窗
  486. let sheetForm = this.sheetForm,
  487. form = this.form,
  488. formName = this.formName
  489. if (sheetForm.currentType == "subjectList") {
  490. // 科目名称赋值
  491. form.subjectIdList = value.value;
  492. formName.subjectListName = value.text;
  493. formName.subjectListIndex = index;
  494. } else if (sheetForm.currentType == "teacherSchool") {
  495. // 线下课地址
  496. form.teacherSchoolId = value.value;
  497. formName.teacherSchoolName = value.text;
  498. formName.teacherSchoolIndex = index;
  499. } else if (sheetForm.currentType == "teacherList") {
  500. // 乐团主管
  501. form.educationalTeacherId = value.value;
  502. formName.educationalTeacherName = value.text;
  503. formName.educationalTeacherIndex = index;
  504. }
  505. sheetForm.sheetStatus = false;
  506. },
  507. async onSubmit() {
  508. // 次数限制是否可以继续创建
  509. let form = this.form;
  510. let statusList = this.statusList;
  511. if (!form.subjectIdList) {
  512. this.$toast("请选择排课声部");
  513. return false;
  514. }
  515. if (!form.educationalTeacherId) {
  516. this.$toast("请选择乐团主管");
  517. return;
  518. }
  519. if (this.checkboxSelectIds.length <= 0) {
  520. this.$toast("请选择上课学员");
  521. return;
  522. }
  523. if (this.isMusicTheory) {
  524. if (!(this.checkboxSelectIds.length >= 1 && this.checkboxSelectIds.length <= this.form.studentNum)) {
  525. this.$toast(`请选择1-${this.form.studentNum}名学生,当前选择${this.checkboxSelectIds.length}名`);
  526. return;
  527. }
  528. } else {
  529. if (this.checkboxSelectIds.length != this.form.studentNum) {
  530. this.$toast(`请选择学生${this.form.studentNum}名,当前选择${this.checkboxSelectIds.length}名`);
  531. return;
  532. }
  533. }
  534. let onlineNums = form.onlineClassesNums || 0
  535. if(form.onlineClassesNums && form.onlineClassesNums.length > 0) {
  536. onlineNums = Number(form.onlineClassesNums)
  537. }
  538. let offlineNums = form.offlineClassesNums || 0
  539. if(form.offlineClassesNums && form.offlineClassesNums.length > 0) {
  540. offlineNums = Number(form.offlineClassesNums)
  541. }
  542. let onlineClassesStatus = onlineNums === "" ? true : false;
  543. let offlineClassesStatus = offlineNums === "" ? true : false;
  544. if (statusList.hasOnline && onlineClassesStatus) {
  545. this.$toast("请输入线上课次数");
  546. return false;
  547. }
  548. if (statusList.hasOffline) {
  549. if (offlineClassesStatus) {
  550. this.$toast("请输入线下课次数");
  551. return false;
  552. }
  553. // 判断是否有线下
  554. if (form.offlineClassesNums > 0 && !form.teacherSchoolId) {
  555. this.$toast("请选择线下课地址");
  556. return false;
  557. }
  558. }
  559. if (
  560. statusList.hasOffline &&
  561. statusList.hasOnline &&
  562. parseFloat(form.onlineClassesNums || 0) + parseFloat(form.offlineClassesNums || 0) != this.form.totalClassTime
  563. ) {
  564. this.$toast('线上课次数+线下课次数不等于总课次数')
  565. return
  566. }
  567. if (!this.checkCourseList()) {
  568. return;
  569. }
  570. if (this.scheduleList.length <= 0) {
  571. this.$toast("课时安排不能为空");
  572. return false;
  573. }
  574. // 排课
  575. const checkmMsg = this.checkTimeTable()
  576. if (checkmMsg) {
  577. this.$toast(checkmMsg)
  578. return
  579. }
  580. this.setTimeTable();
  581. form.studentIdList = this.checkboxSelectIds.join(",");
  582. form.firstStudentId = this.studentList.length > 0 ? this.studentList[0].userId : null;
  583. form.onlineClassesNum = Number(form.onlineClassesNums || 0);
  584. form.offlineClassesNum = Number(form.offlineClassesNums || 0);
  585. let params = {
  586. courseSchedules: this.timeTable
  587. }
  588. // return
  589. if(this.courseTypeIsVip) {
  590. params.vipGroupApplyBaseInfo = form
  591. params.giveFlag = !this.typeStatus
  592. await this.onPayVip(params)
  593. } else {
  594. params.practiceGroupApplyBaseInfoDto = form
  595. params.practiceGroupApplyBaseInfoDto.studentId = form.studentIdList
  596. params.practiceGroupApplyBaseInfoDto.allCourseNum = form.totalClassTime
  597. params.practiceGroupApplyBaseInfoDto.subjectId = form.subjectIdList
  598. params.giveFlag = !this.typeStatus
  599. await this.onPayPractice(params)
  600. }
  601. },
  602. async onPayVip(params) {
  603. try {
  604. await createVipGroup(params)
  605. this.$toast("排课成功");
  606. setTimeout(() => {
  607. this.$router.back()
  608. }, 1000);
  609. } catch {
  610. //
  611. }
  612. },
  613. async onPayPractice(params) {
  614. try {
  615. await createPracticeGroup(params)
  616. this.$toast("排课成功");
  617. setTimeout(() => {
  618. this.$router.back()
  619. }, 1000);
  620. } catch {
  621. //
  622. }
  623. },
  624. onSelectStudent(items) {
  625. // 选中的数据
  626. const tempItems = items || []
  627. this.studentList = tempItems
  628. // if(tempItems.length <= 0) { // 判断是否有选择学员
  629. this.checkboxSelectIds = []
  630. // }
  631. tempItems.forEach(item => {
  632. this.checkboxSelectIds.push(item.userId)
  633. })
  634. this.studentStatus = false
  635. },
  636. onDelete(type, item) {
  637. if(type == 'student') {
  638. // 删除上课学员
  639. this.$dialog.confirm({
  640. title: '提示',
  641. message: '是否删除该学员?',
  642. confirmButtonText: '确定',
  643. confirmButtonColor: '#269a93',
  644. cancelButtonText: '取消'
  645. }).then(() => {
  646. let index = this.studentList.indexOf(item);
  647. if (index !== -1) {
  648. this.studentList.splice(index, 1);
  649. this.checkboxSelectIds.splice(index, 1);
  650. }
  651. })
  652. } else if(type == 'class') {
  653. // 删除上课学员
  654. this.$dialog.confirm({
  655. title: '提示',
  656. message: '是否删除该课时安排?',
  657. confirmButtonText: '确定',
  658. confirmButtonColor: '#269a93',
  659. cancelButtonText: '取消'
  660. }).then(() => {
  661. let index = this.scheduleList.indexOf(item);
  662. if (index !== -1) {
  663. this.scheduleList.splice(index, 1);
  664. }
  665. })
  666. }
  667. },
  668. onCurrentConfirm(value) {
  669. // 排课开始时间
  670. this.form.courseStart = dayjs(value).format('YYYY-MM-DD')
  671. this.dataForm.status = false;
  672. },
  673. onClassKeyUp(type) {
  674. // 线上课&线下课修改时
  675. if(this.teachMode != -1) return
  676. let form = this.form
  677. let onlineNum = form.onlineClassesNums
  678. let offLineNum = form.offlineClassesNums
  679. // 重置次数,不能
  680. if(parseInt(onlineNum || 0) + parseInt(offLineNum || 0) >= form.totalClassTime) {
  681. if(type == 'offLine') {
  682. let diffNum = form.totalClassTime - parseInt(onlineNum || 0)
  683. offLineNum = diffNum < 0 ? 0 : diffNum
  684. } else {
  685. let diffNum = form.totalClassTime - parseInt(offLineNum || 0)
  686. onlineNum = diffNum < 0 ? 0 : diffNum
  687. }
  688. }
  689. this.form.onlineClassesNums = onlineNum
  690. this.form.offlineClassesNums = offLineNum
  691. this.tempOfflineNum = offLineNum || 0
  692. },
  693. onShowTimeTable() {
  694. // 显示排课列表
  695. if (!this.checkCourseList()) {
  696. return;
  697. }
  698. const checkmMsg = this.checkTimeTable()
  699. if (checkmMsg) {
  700. this.$toast(checkmMsg)
  701. return
  702. }
  703. this.statusList.classTime = true;
  704. this.setTimeTable();
  705. },
  706. checkTimeTable() {
  707. let form = this.form,
  708. scheduleList = this.scheduleList;
  709. let online = parseInt(
  710. form.onlineClassesNums ? form.onlineClassesNums : 0
  711. );
  712. let offline = parseInt(
  713. form.offlineClassesNums ? form.offlineClassesNums : 0
  714. );
  715. // 网管课默认只有线上课次
  716. if(!this.courseTypeIsVip) {
  717. online = parseInt(form.totalClassTime || 0)
  718. }
  719. // let totalCount = Number(online) + Number(offline)
  720. let hasOnlineSchedule = false,
  721. hasOfflineSchedule = false
  722. for (let i = 0; i < scheduleList.length; i++) {
  723. const item = scheduleList[i];
  724. if (item.type == '线上') {
  725. hasOnlineSchedule = true;
  726. }
  727. if (item.type == '线下') {
  728. hasOfflineSchedule = true;
  729. }
  730. if (hasOnlineSchedule && hasOfflineSchedule) {
  731. break;
  732. }
  733. }
  734. if (online > 0 && !hasOnlineSchedule) {
  735. return '请添加线上课时安排';
  736. }
  737. if (offline > 0 && !hasOfflineSchedule) {
  738. return '请添加线下课时安排';
  739. }
  740. console.log({...form}, online, offline, hasOnlineSchedule, hasOfflineSchedule)
  741. },
  742. setTimeTable() {
  743. if (!this.checkCourseList(false)) {
  744. return;
  745. }
  746. // 重置排课列表
  747. this.timeTable = [];
  748. let form = this.form,
  749. scheduleList = this.scheduleList;
  750. // 拿到线上课数与线下课数 以及
  751. let online = parseInt(
  752. form.onlineClassesNums ? form.onlineClassesNums : 0
  753. );
  754. let offline = parseInt(
  755. form.offlineClassesNums ? form.offlineClassesNums : 0
  756. );
  757. // 网管课默认只有线上课次
  758. if(!this.courseTypeIsVip) {
  759. online = parseInt(form.totalClassTime || 0)
  760. }
  761. // 判断是否有课程安排
  762. if (scheduleList.length <= 0) {
  763. return;
  764. }
  765. let totalCount = Number(online) + Number(offline);
  766. let tempCourseStart = form.courseStart.replace(/-/gi, "/");
  767. let dateOperation = new Date(tempCourseStart);
  768. let forMark = 0;
  769. while (totalCount && totalCount > 0) {
  770. for (let i = 0; i < scheduleList.length; i++) {
  771. if (online == 0 && offline == 0) break;
  772. let num = scheduleList[i].weekIndex - dateOperation.getDay();
  773. // 如果是同一天一个周期会出现排课都排到一天
  774. if (forMark > 0 && num == 0 && i == 0) {
  775. num = num + 7;
  776. }
  777. if (num < 0) {
  778. // 如果为负数则为下周
  779. num = num + 7;
  780. }
  781. let dataStr = this.getThinkDate(dateOperation, num);
  782. // 判断是否大于当前时间
  783. let nowGetTime = new Date().getTime();
  784. let courseTime = new Date(dataStr.replace(/-/gi, "/") +" " + scheduleList[i].startTime + ":00").getTime();
  785. if (nowGetTime < courseTime) {
  786. let tempArr = {
  787. classDate: dataStr,
  788. startClassTimeStr: scheduleList[i].startTime,
  789. endClassTimeStr: scheduleList[i].endTime,
  790. };
  791. if (scheduleList[i].type == "线上" && online > 0) {
  792. tempArr.teachMode = "ONLINE";
  793. this.timeTable.push(tempArr);
  794. online--;
  795. totalCount--;
  796. } else if (scheduleList[i].type == "线下" && offline > 0) {
  797. tempArr.teachMode = "OFFLINE";
  798. this.timeTable.push(tempArr);
  799. offline--;
  800. totalCount--;
  801. }
  802. }
  803. }
  804. // 加一周
  805. if (scheduleList.length == 1) {
  806. dateOperation.setDate(dateOperation.getDate() + 7);
  807. } else if (
  808. scheduleList.every((item) => item.weekStr === scheduleList[0].weekStr)
  809. ) {
  810. // 标记循环次数(标记判断课程安排是不是同一天)
  811. forMark++;
  812. }
  813. }
  814. this.timeTable.sort((a, b) => {
  815. let aStr = dayjs(dayjs(a.classDate).format("YYYY-MM-DD") + " " + a.startClassTimeStr + ":00").valueOf();
  816. let bStr = dayjs(dayjs(b.classDate).format("YYYY-MM-DD") + " " + b.startClassTimeStr + ":00").valueOf();
  817. return aStr - bStr;
  818. });
  819. },
  820. getThinkDate(date, num) {
  821. let Stamp = date;
  822. Stamp.setDate(date.getDate() + num); // 获取当前月数的第几天
  823. return dayjs(Stamp).format('YYYY-MM-DD')
  824. },
  825. checkCourseList(isShowToast = true) {
  826. let form = this.form;
  827. let scheduleList = this.scheduleList || [];
  828. let hasOnLine = false; // 是否有线上课时安排
  829. let hasOffLine = false;
  830. scheduleList.forEach((item) => {
  831. if (item.type == "线上") {
  832. hasOnLine = true;
  833. }
  834. if (item.type == "线下") {
  835. hasOffLine = true;
  836. }
  837. });
  838. let statusList = this.statusList;
  839. let onlineClassesStatus = !form.onlineClassesNums && form.onlineClassesNums <= 0 ? true : false;
  840. let offlineClassesStatus = !form.offlineClassesNums && form.offlineClassesNums <= 0 ? true : false;
  841. if (statusList.hasOnline) {
  842. if (onlineClassesStatus) {
  843. if (isShowToast) {
  844. this.$toast("请输入线上课次数");
  845. }
  846. return false;
  847. }
  848. if (!onlineClassesStatus && !hasOnLine && form.onlineClassesNums > 0) {
  849. if (isShowToast) {
  850. this.$toast("课时安排缺少线上课类型");
  851. }
  852. return false;
  853. }
  854. }
  855. if (statusList.hasOffline && !statusList.hasOnline) {
  856. if (offlineClassesStatus) {
  857. if (isShowToast) {
  858. this.$toast("请输入线下课次数");
  859. }
  860. return false;
  861. }
  862. if (
  863. !offlineClassesStatus &&
  864. !hasOffLine &&
  865. form.offlineClassesNums > 0
  866. ) {
  867. if (isShowToast) {
  868. this.$toast("课时安排缺少线下课类型");
  869. }
  870. return false;
  871. }
  872. }
  873. if (
  874. statusList.hasOffline &&
  875. statusList.hasOnline &&
  876. parseFloat(form.onlineClassesNums || 0) + parseFloat(form.offlineClassesNums || 0) != this.form.totalClassTime
  877. ) {
  878. if (isShowToast) {
  879. this.$toast('线上次数+线下课次数不等于总课次数')
  880. }
  881. return false
  882. }
  883. if(!this.form.courseStart) {
  884. if(isShowToast) {
  885. this.$toast('请选择排课开始时间')
  886. }
  887. return false
  888. }
  889. return true;
  890. },
  891. formatStatus(type, teachMode) {
  892. // -1:所有;0:线上;1:线下
  893. this.teachMode = teachMode
  894. if(type == 'online' && teachMode == 0) {
  895. return true
  896. } else if(type == 'offline' && teachMode == 1) {
  897. return true
  898. } else if(teachMode == -1) {
  899. return true
  900. } else {
  901. return false
  902. }
  903. },
  904. formatter(type, value) {
  905. if (type === "year") {
  906. return `${value}年`;
  907. } else if (type === "month") {
  908. return `${value}月`;
  909. } else if (type === "day") {
  910. return `${value}日`;
  911. }
  912. return value;
  913. },
  914. }
  915. }
  916. </script>
  917. <style lang="less" scoped>
  918. @import url("../../assets/commonLess/variable.less");
  919. .program {
  920. background-color: #F5F5F5;
  921. min-height: 100vh;
  922. overflow: hidden;
  923. }
  924. .van-cell-group--inset {
  925. margin: .12rem .12rem 0;
  926. overflow: hidden;
  927. border-radius: 8px;
  928. .titleContent {
  929. padding: .14rem .16rem;
  930. }
  931. .titleStyle {
  932. font-size: .2rem;
  933. color: #333333;
  934. font-size: 500;
  935. }
  936. .labelStyle {
  937. padding-top: .08rem;
  938. color: #666666;
  939. font-size: .13rem;
  940. line-height: .2rem;
  941. }
  942. }
  943. .van-row {
  944. line-height: 0.4rem;
  945. border-top: 1px solid #edeef0;
  946. text-align: center;
  947. font-size: 0.14rem;
  948. &:first-child {
  949. border-top: 0;
  950. background: #edeef0;
  951. color: #444;
  952. font-size: 0.15rem;
  953. }
  954. }
  955. .tableContainer {
  956. max-height: 2.44rem;
  957. overflow: auto;
  958. .van-row {
  959. color: #444;
  960. &:first-child {
  961. border-top: 0;
  962. background: #fff;
  963. font-size: 0.14rem;
  964. }
  965. }
  966. }
  967. .van-block__title {
  968. padding: .12rem .14rem .06rem;
  969. color: #808080;
  970. font-size: .14rem;
  971. line-height: .2rem;
  972. }
  973. .button-group {
  974. margin: 0.3rem 0.26rem 0.2rem;
  975. .van-button--primary {
  976. background: @mColor;
  977. border-color: @mColor;
  978. font-size: 0.18rem;
  979. }
  980. }
  981. .title-time {
  982. display: flex;
  983. align-items: center;
  984. flex: 1 auto;
  985. color: #1A1A1A;
  986. font-size: .16rem;
  987. .week {
  988. padding-left: 0.4rem;
  989. padding-right: 0.15rem;
  990. }
  991. }
  992. /deep/.studentChose {
  993. border-radius: .1rem .1rem 0px 0px;
  994. overflow: auto;
  995. background: #F5F5F5;
  996. }
  997. /deep/.van-field__label {
  998. color: #1A1A1A;
  999. }
  1000. /deep/.van-field__control, .showText {
  1001. font-size: 16px;
  1002. color: #808080;
  1003. }
  1004. .addButton {
  1005. margin: .1rem .28rem;
  1006. border: 1px dashed #CFCFCF;
  1007. line-height: .42rem;
  1008. text-align: center;
  1009. background: #FBFBFB;
  1010. color: #666666;
  1011. font-size: .14rem;
  1012. border-radius: .05rem;
  1013. }
  1014. </style>