ArrangeWork.vue 16 KB

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