ArrangeWork.vue 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489
  1. <template>
  2. <div class="arrangeWork">
  3. <m-header v-if="headerStatus" />
  4. <van-cell :title="classGroupName" title-style="font-size: .18rem; color: #333333;">
  5. <template #extra>
  6. <van-tag type="primary" plain style="background-color: #F1FCF9">课后训练</van-tag>
  7. </template>
  8. </van-cell>
  9. <van-cell class="endTime" readonly is-link title-style="font-size: .16rem; color: #333333;" @click="studentStatus = true">
  10. <template #title>
  11. <span style="padding-right: .3rem">学员共<i style="font-style: normal; color: #01C1B5">{{ params.studentCount }}</i>人</span>
  12. <span>会员 <i style="font-style: normal; color: #FF802C">{{ params.studentMemberCount }}</i>人</span>
  13. </template>
  14. </van-cell>
  15. <div class="container">
  16. <div class="formGroup">
  17. <div class="dot"></div>
  18. <div class="formTitle">训练标题</div>
  19. <van-field v-model="title" type="text" maxlength="25" show-word-limit placeholder="请输入训练标题" />
  20. </div>
  21. <div class="formGroup">
  22. <div class="dot"></div>
  23. <div class="formTitle">训练内容</div>
  24. <van-field rows="4" v-model="content" maxlength="600" show-word-limit type="textarea" placeholder="请输入训练内容" />
  25. </div>
  26. </div>
  27. <van-cell class="endTime" style="padding: 0.12rem 0.21rem;" title="训练提交截止时间" @click="onEndTime" is-link readonly placeholder="请选择截止时间" >
  28. <template #title>
  29. <div class="dot"></div>
  30. 训练提交截止时间
  31. </template>
  32. <template #default>
  33. {{ dateSection.showStartDate }}
  34. </template>
  35. </van-cell>
  36. <div class="accompanyHeader">
  37. 云教练曲目选择<span>(若作业内容非云教练曲目则无需选择)</span>
  38. </div>
  39. <van-tabs v-model="tabActive" color="#01C1B5">
  40. <van-tab v-for="(item, i) in params.subjectList" :title="item.subjectName" :name="item.id" :key="i" class="van-hairline--bottom">
  41. <van-cell class="endTime" v-for="(music, index) in item.musicScoreIdList" :key="index" style="margin-top: 0;padding: 0.12rem 0.21rem;" title="训练曲目" @click="onSelectAccompany(item, index)" readonly is-link placeholder="请选择训练曲目" >
  42. <template #title>
  43. <div class="dot"></div>训练曲目
  44. </template>
  45. <template #default>
  46. <template v-if="music.name">
  47. {{ music.name }}
  48. </template>
  49. <span v-else style="color: #808080;">请选择</span>
  50. </template>
  51. <template #right-icon>
  52. <span v-if="music.name" @click.stop="delAccompany(item.musicScoreIdList, index)" style="color: #999999; font-size: .12rem; padding-left: .12rem"><van-icon size="14" name="delete-o" />删除</span>
  53. </template>
  54. <template #extra>
  55. <!-- <span><van-icon name="delete-o" />删除</span> -->
  56. <span style="height: 24px;font-size: 16px;line-height: 24px;color: #DADADA !important;"><van-icon name="arrow" /></span>
  57. </template>
  58. </van-cell>
  59. <div class="addAccompaniment" v-if="item.musicScoreIdList.length < 3" @click="addCloud(item)">
  60. <van-icon name="plus" size="16px" />增加云教练训练曲目
  61. </div>
  62. </van-tab>
  63. </van-tabs>
  64. <div class="button-group">
  65. <van-button type="primary" round size="large" @click="onSubmit">确定</van-button>
  66. </div>
  67. <!-- 日期开始弹窗 -->
  68. <van-popup position="bottom" v-model="dateSection.status">
  69. <van-datetime-picker
  70. v-model="dateSection.currentDate"
  71. type="date"
  72. :min-date="dateSection.minDate"
  73. :max-date="dateSection.maxDate"
  74. :formatter="formatter"
  75. @confirm="confirmStartTime()"
  76. @cancel="dateSection.status = false"
  77. />
  78. </van-popup>
  79. <van-popup position="bottom" v-model="accompanimentStatus" :style="{ height: '100%' }" style=" border-radius: 0; overflow: inherit;">
  80. <!-- <van-sticky>
  81. <m-header name="作业曲目" :backUrl="backUrl" />
  82. </van-sticky> -->
  83. <accompaniment-modal @onSelectMusic="onSelectMusic" ref="accompaniment" style="margin-bottom: 0.8rem;" :searchSubjectId="tabActive" />
  84. <div class="button-group-popup">
  85. <span class="btn" @click="accompanimentStatus = false">关闭</span>
  86. </div>
  87. </van-popup>
  88. <van-popup
  89. v-model="studentStatus"
  90. :lock-scroll="true"
  91. position="bottom"
  92. :style="{ height: '180%' }"
  93. >
  94. <!-- <van-sticky>
  95. <m-header name="学员列表" :backUrl="backUrlStudent" />
  96. </van-sticky> -->
  97. <student-list-model :dataList="dataList" style="margin-bottom: 0.8rem;" :dataSubjectList="dataSubjectList"></student-list-model>
  98. <div class="button-group-popup">
  99. <span class="btn" @click="studentStatus = false">关闭</span>
  100. </div>
  101. </van-popup>
  102. </div>
  103. </template>
  104. <script>
  105. import MHeader from "@/components/MHeader"
  106. import { browser, _throttle } from '@/common/common'
  107. import dayjs from "dayjs";
  108. import { addHomeWork } from '@/api/audition'
  109. import { getCourseStudents } from '@/api/teacher'
  110. import AccompanimentModal from './modal/accompanimentModal'
  111. import StudentListModel from './modal/studentList'
  112. import cleanDeep from 'clean-deep'
  113. import deepClone from '@/helpers/deep-clone'
  114. export default {
  115. name: "teacherList",
  116. components: { MHeader, AccompanimentModal, StudentListModel },
  117. data() {
  118. let tempDate = new Date() // 默认显示T+3
  119. tempDate.setDate(tempDate.getDate() + 3)
  120. let query = this.$route.query
  121. return {
  122. headerStatus: true,
  123. backUrl: {
  124. status: true,
  125. callBack: () => {
  126. this.accompanimentStatus = false
  127. }
  128. },
  129. backUrlStudent: {
  130. status: true,
  131. callBack: () => {
  132. this.studentStatus = false
  133. }
  134. },
  135. classGroupName: query.classGroupName,
  136. courseId: query.courseId || query.id,
  137. dateSection: {
  138. status: false,
  139. minDate: new Date(),
  140. maxDate: new Date(2025, 10, 1),
  141. currentDate: tempDate,
  142. showStartDate: dayjs(tempDate).format("YYYY年MM月DD日"),
  143. musicScoreId: null,
  144. musicScoreName: null,
  145. },
  146. title: '',
  147. content: '', // 课程编号
  148. expiryDate: null, // 作业截止日期
  149. accompanimentStatus: false, // 伴奏弹窗
  150. tabActive: 0,
  151. tabOriginActive: 0,
  152. tabActiveList: {}, // 选中当前信息
  153. tabActiveIndex: 0,
  154. params: {
  155. studentCount: 0, // 学员总数
  156. studentMemberCount: 0, // 学员会员数
  157. subjectIdList: [],
  158. subjectList: []
  159. },
  160. dataList: [],
  161. dataSubjectList: [],
  162. studentStatus: false,
  163. };
  164. },
  165. async mounted() {
  166. let params = this.$route.query;
  167. if (params.Authorization) {
  168. localStorage.setItem("Authorization", decodeURI(params.Authorization));
  169. localStorage.setItem("userInfo", decodeURI(params.Authorization));
  170. }
  171. document.title = '布置训练'
  172. if(browser().android || browser().iPhone) {
  173. this.headerStatus = false
  174. }
  175. await getCourseStudents({ courseScheduleId: this.courseId }).then(res => {
  176. const result = res.data
  177. if(result.code != 200) { return }
  178. const tempData = result.data || []
  179. let params = {
  180. studentCount: tempData.length, // 学员总数
  181. studentMemberCount: 0, // 学员会员数
  182. subjectIdList: [],
  183. subjectList: []
  184. }
  185. tempData.forEach(item => {
  186. // 判断当前学员是否有会员
  187. if(item.memberRankSettingId) {
  188. params.studentMemberCount++
  189. }
  190. if(!params.subjectIdList.includes(item.subjectIdList || -1)) {
  191. params.subjectIdList.push(item.subjectIdList || -1)
  192. params.subjectList.push({
  193. id: item.subjectIdList || -1,
  194. subjectName: item.subjectName || '无声部',
  195. userIdList: [item.userId],
  196. musicScoreIdList: [{
  197. id: null,
  198. name: null,
  199. }], // 默认加一个空的,做占位
  200. })
  201. } else {
  202. params.subjectList.forEach(subject => {
  203. if(subject.id == item.subjectIdList) {
  204. subject.userIdList.push(item.userId)
  205. }
  206. if(!item.subjectIdList && subject.id == -1) {
  207. subject.userIdList.push(item.userId)
  208. }
  209. })
  210. }
  211. if(!item.subjectIdList) {
  212. item.subjectIdList = -1
  213. }
  214. });
  215. this.dataList = tempData
  216. // 默认选中第1条数据
  217. this.tabActive = deepClone(params.subjectIdList[0])
  218. this.tabOriginActive = deepClone(params.subjectIdList[0])
  219. this.dataSubjectList = deepClone(params.subjectList)
  220. this.params = params
  221. })
  222. },
  223. methods: {
  224. onSelectAccompany(item, index) {
  225. if(this.tabActive != this.tabOriginActive && this.$refs.accompaniment) {
  226. this.tabOriginActive = JSON.parse(JSON.stringify(this.tabActive))
  227. this.$refs.accompaniment.onSearch()
  228. }
  229. this.accompanimentStatus = true
  230. this.tabActiveList = item
  231. this.tabActiveIndex = index
  232. },
  233. delAccompany(music, index) {
  234. if(music.length == 1) {
  235. music[0] = {
  236. id: null,
  237. name: null,
  238. }
  239. this.$forceUpdate()
  240. } else {
  241. music.splice(index, 1)
  242. }
  243. },
  244. addCloud(item) {
  245. if(item.musicScoreIdList.length < 3) {
  246. item.musicScoreIdList.push('')
  247. }
  248. },
  249. formatter(type, val) {
  250. if (type === "year") {
  251. return `${val}年`
  252. } else if (type === "month") {
  253. return `${val}月`
  254. } else if (type == "day") {
  255. return `${val}日`
  256. }
  257. return val
  258. },
  259. onSelectMusic(value) {
  260. this.tabActiveList.musicScoreIdList[this.tabActiveIndex] = {
  261. id: value.examSongId,
  262. name: value.examSongName
  263. }
  264. this.accompanimentStatus = false
  265. },
  266. confirmStartTime() {
  267. this.dateSection.showStartDate = dayjs(this.dateSection.currentDate).format("YYYY年MM月DD日")
  268. this.dateSection.status = false
  269. },
  270. onEndTime() {
  271. this.dateSection.status = true
  272. },
  273. onSubmit: _throttle(function(type) {
  274. if(!this.title) {
  275. this.$toast('请输入训练标题')
  276. return
  277. }
  278. if(!this.content) {
  279. this.$toast('请输入训练内容')
  280. return
  281. }
  282. let tempSubjectList = this.params.subjectList
  283. let musicScoreSubjectDto = []
  284. let notAccompanySong = [] // 用于判断没有设置训练曲目的数据
  285. let subjectLength = this.params.subjectIdList.length
  286. tempSubjectList.forEach(subject => {
  287. let scoreIdList = []
  288. subject.musicScoreIdList.forEach(music => {
  289. if(music.id) {
  290. scoreIdList.push(music.id)
  291. }
  292. })
  293. if(scoreIdList.length <= 0) {
  294. notAccompanySong.push(subject.subjectName)
  295. }
  296. musicScoreSubjectDto.push({
  297. musicScoreIdList: scoreIdList,
  298. userIdList: subject.userIdList
  299. })
  300. })
  301. if(notAccompanySong.length - subjectLength <= 0 && notAccompanySong != 0 && type[0] != 1) {
  302. // console.log('选择曲目')
  303. this.$dialog.confirm({
  304. message: notAccompanySong.join(',') + '声部未选择云教练训练曲目,是否继续布置作业?',
  305. confirmButtonColor: '#01C1B5'
  306. }).then(() => {
  307. // on confirm
  308. this.onSubmit(1)
  309. })
  310. .catch(() => {
  311. // on cancel
  312. });
  313. return
  314. }
  315. this.$toast.loading({
  316. message: '加载中...',
  317. duration: 10000,
  318. forbidClick: true,
  319. loadingType: 'spinner',
  320. })
  321. let query = this.$route.query
  322. let params = {
  323. title: this.title,
  324. content: this.content,
  325. courseScheduleId: query.courseId,
  326. expiryDate: dayjs(this.dateSection.currentDate).format('YYYY-MM-DD'),
  327. musicScoreSubjectDto: musicScoreSubjectDto
  328. }
  329. addHomeWork(cleanDeep({...params})).then(res => {
  330. let result = res.data
  331. this.$toast.clear()
  332. if(result.code == 200) {
  333. this.$toast('作业布置成功')
  334. setTimeout(() => {
  335. let query = this.$route.query
  336. this.$router.replace({
  337. path: '/courseEvaluation',
  338. query: {
  339. id: query.id,
  340. reviewId: query.reviewId,
  341. isInside: query.isInside,
  342. work: 1
  343. }
  344. })
  345. }, 500);
  346. } else {
  347. this.$toast(result.msg)
  348. }
  349. })
  350. }, 500)
  351. }
  352. };
  353. </script>
  354. <style lang="less" scoped>
  355. @import url("../../assets/commonLess/variable.less");
  356. .arrangeWork {
  357. min-height: 100vh;
  358. overflow-y: auto;
  359. overflow-x: hidden;
  360. background-color: #F3F4F8;
  361. }
  362. /deep/.van-cell {
  363. font-size: .14rem;
  364. padding: .12rem .16rem;
  365. line-height: .24rem;
  366. }
  367. .arrowDown {
  368. display: inline-block;
  369. margin-left: .1rem;
  370. width: 0.1rem;
  371. height: 0.07rem;
  372. background: url('../../assets/images/audition/arrow_down.png') no-repeat center center;
  373. background-size: 100%;
  374. }
  375. .container {
  376. .formGroup {
  377. position: relative;
  378. margin-top: .1rem;
  379. background: #ffffff;
  380. }
  381. .formTitle {
  382. padding: .12rem .21rem 0;
  383. font-size: .16rem;
  384. color: #333333;
  385. font-weight: 500;
  386. }
  387. }
  388. .endTime {
  389. margin-top: .1rem;
  390. .van-cell__title {
  391. font-size: .16rem;
  392. color: #1A1A1A;
  393. }
  394. /deep/.van-cell__value {
  395. // width: 30%;
  396. justify-content: flex-end;
  397. text-align: right;
  398. font-size: .16rem;
  399. color: #1A1A1A;
  400. display: flex;
  401. align-items: center;
  402. }
  403. }
  404. .button-group {
  405. margin: 0.3rem 0.26rem 0.2rem;
  406. .van-button--primary {
  407. background: @mColor;
  408. border: 1px solid @mColor;
  409. font-size: 0.18rem;
  410. }
  411. }
  412. .dot {
  413. width: 4px;
  414. height: 0.17rem;
  415. background: #01c1b5;
  416. border-radius: 3px;
  417. position: absolute;
  418. z-index: 98;
  419. top: .15rem;
  420. left: 0.12rem;
  421. }
  422. /deep/.van-tab {
  423. font-size: 16px;
  424. }
  425. /deep/.van-popup--bottom {
  426. border-radius: 0px 0px 0px 0px!important;
  427. overflow: auto!important;
  428. }
  429. .accompanyHeader {
  430. font-size: .14rem;
  431. padding: .16rem .08rem .14rem;
  432. color: #333;
  433. font-weight: 500;
  434. span {
  435. color: #808080;
  436. }
  437. }
  438. .addAccompaniment {
  439. width: 80%;
  440. height: .42rem;
  441. line-height: .42rem;
  442. text-align: center;
  443. margin: .1rem auto 0;
  444. border: 1px dashed #CCC;
  445. background: #FBFBFB;
  446. font-size: .14rem;
  447. color: #666666;
  448. }
  449. .button-group-popup {
  450. position: fixed;
  451. bottom: 0;
  452. padding: 0.1rem 0;
  453. width: 100%;
  454. text-align: center;
  455. background-color: #ffffff;
  456. .btn {
  457. line-height: 0.5rem;
  458. display: inline-block;
  459. border: 1px solid @mColor;
  460. border-radius: 0.4rem;
  461. color: @mColor;
  462. background: #fff;
  463. font-size: 0.18rem;
  464. width: 90%;
  465. &.primary {
  466. color: #fff;
  467. background: @mColor;
  468. }
  469. }
  470. .btn + .btn {
  471. margin-left: 0.1rem;
  472. }
  473. }
  474. </style>