vipStudentList.vue 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693
  1. <template>
  2. <div>
  3. <div class="btnWrap">
  4. <el-button
  5. v-permission="'vipGroupManage/addVipGroupStudents'"
  6. @click="addStudentList"
  7. type="primary"
  8. >
  9. 新增学员
  10. </el-button>
  11. <el-button v-permission="'vipGroupManage/update'" @click="expireVisible = true" type="primary">
  12. 有效期调整
  13. </el-button>
  14. </div>
  15. <div class="tableWrap">
  16. <el-table
  17. :data="tableList"
  18. :header-cell-style="{ background: '#EDEEF0', color: '#444' }"
  19. >
  20. <el-table-column
  21. label="学员姓名"
  22. prop="userName"
  23. align="center"
  24. width="180"
  25. >
  26. </el-table-column>
  27. <el-table-column label="手机号" align="center" prop="phone" width="180">
  28. </el-table-column>
  29. <el-table-column prop="studentStatus" align="center" label="学员状态">
  30. <template slot-scope="scope">
  31. <div>
  32. {{ scope.row.studentStatus | studentStatus }}
  33. </div>
  34. </template>
  35. </el-table-column>
  36. <el-table-column prop="courseSalary" align="center" label="课程余额">
  37. <template slot-scope="scope">
  38. <div>
  39. {{ scope.row.courseSalary | moneyFormat }}
  40. </div>
  41. </template>
  42. </el-table-column>
  43. <el-table-column prop="applyDate" align="center" label="报名时间">
  44. <template slot-scope="scope">
  45. <div>
  46. {{ scope.row.applyDate | formatTimer }}
  47. </div>
  48. </template>
  49. </el-table-column>
  50. <el-table-column prop="refundDate" align="center" label="退课时间">
  51. <template slot-scope="scope">
  52. <div>
  53. {{ scope.row.refundDate | formatTimer }}
  54. </div>
  55. </template>
  56. </el-table-column>
  57. <el-table-column label="操作" align="center">
  58. <template slot-scope="scope">
  59. <div>
  60. <el-button
  61. type="text"
  62. v-if="
  63. scope.row.studentStatus == 0 || scope.row.studentStatus == 3
  64. "
  65. v-permission="'vipGroupManage/applyRefundForStudent'"
  66. @click="lookFee(scope)"
  67. >退学</el-button
  68. >
  69. <el-button
  70. type="text"
  71. v-if="scope.row.studentStatus == 0"
  72. @click="stopCourse(scope)"
  73. >休学</el-button
  74. >
  75. <el-button
  76. type="text"
  77. v-if="scope.row.studentStatus == 3"
  78. @click="recoveryCourse(scope)"
  79. >恢复</el-button
  80. >
  81. </div>
  82. </template>
  83. </el-table-column>
  84. </el-table>
  85. </div>
  86. <el-dialog title="学员列表" width="70%" :visible.sync="maskVisible">
  87. <el-form :model="maskForm" :inline="true">
  88. <el-form-item>
  89. <el-input
  90. placeholder="请输入学生姓名或手机号"
  91. @keyup.enter.native="search"
  92. v-model.trim="maskForm.search"
  93. ></el-input>
  94. </el-form-item>
  95. <el-form-item>
  96. <el-button type="danger" @click="search">搜索</el-button>
  97. </el-form-item>
  98. <el-form-item>
  99. <el-button type="primary" @click="reset">重置</el-button>
  100. </el-form-item>
  101. </el-form>
  102. <el-table
  103. :data="maskStudentList"
  104. :header-cell-style="{ background: '#EDEEF0', color: '#444' }"
  105. >
  106. <el-table-column label="" width="55">
  107. <template slot-scope="scope">
  108. <el-radio
  109. v-model.trim="activeStudent"
  110. :label="scope.row.id"
  111. @change.native="getTemplateRow(scope.$index, scope.row)"
  112. >&nbsp</el-radio
  113. >
  114. </template>
  115. </el-table-column>
  116. <el-table-column
  117. prop="userName"
  118. align="center"
  119. label="学生姓名"
  120. width="150"
  121. ></el-table-column>
  122. <el-table-column
  123. prop="phone"
  124. align="center"
  125. label="手机号"
  126. width="200"
  127. ></el-table-column>
  128. <el-table-column prop="courseSalary" label="课程余额"></el-table-column>
  129. </el-table>
  130. <pagination
  131. save-key="vipDetail-vipStudentList"
  132. sync
  133. :total.sync="rules.total"
  134. :page.sync="rules.page"
  135. :limit.sync="rules.limit"
  136. :page-sizes="rules.page_size"
  137. @pagination="getList"
  138. />
  139. <div slot="footer" class="dialog-footer">
  140. <el-button @click="maskVisible = false">取 消</el-button>
  141. <el-button type="primary" @click="addStudent">确 定</el-button>
  142. </div>
  143. </el-dialog>
  144. <el-dialog title="学员复学" width="800px" :visible.sync="adjustmentVisible">
  145. <el-form
  146. :model="adjustmentForm"
  147. label-position="right"
  148. label-width="120px"
  149. ref="adjustmentForm"
  150. :rules="adjustmentRules"
  151. :inline="true"
  152. >
  153. <el-form-item label="剩余课时">
  154. <el-input
  155. disabled
  156. v-model.trim="adjustmentForm.count"
  157. style="width: 200px !important"
  158. ></el-input>
  159. </el-form-item>
  160. <el-form-item label="任课老师" prop="teacher">
  161. <el-select
  162. v-model.trim="adjustmentForm.teacher"
  163. clearable
  164. filterable
  165. style="width: 200px !important"
  166. >
  167. <el-option
  168. v-for="(item, index) in teacherList"
  169. :key="index"
  170. :label="item.realName"
  171. :value="item.id"
  172. ></el-option>
  173. </el-select>
  174. </el-form-item>
  175. <!-- <br /> -->
  176. <el-form-item label="排课起始时间" prop="courseTime">
  177. <el-date-picker
  178. v-model.trim="adjustmentForm.courseTime"
  179. :picker-options="pickerOptions"
  180. style="width: 200px !important"
  181. type="date"
  182. value-format="yyyy-MM-dd"
  183. placeholder="选择日期"
  184. >
  185. </el-date-picker>
  186. </el-form-item>
  187. <el-form-item label="有效期截止" prop="expireDate">
  188. <el-date-picker
  189. :disabled="true"
  190. v-model.trim="adjustmentForm.expireDate"
  191. :picker-options="pickerOptions"
  192. style="width: 200px !important"
  193. type="date"
  194. value-format="yyyy-MM-dd"
  195. placeholder="选择日期"
  196. >
  197. </el-date-picker>
  198. </el-form-item>
  199. <el-form-item>
  200. <el-checkbox
  201. style="margin-left: 10px"
  202. v-model.trim="adjustmentForm.checked"
  203. >是否跳过节假日</el-checkbox
  204. >
  205. </el-form-item>
  206. </el-form>
  207. <div class="WeekWrap">
  208. <h3 style="margin-bottom: 20px">
  209. 循环次数
  210. <el-button type="text" style="margin-left: 10px" @click="addWeek"
  211. >添加</el-button
  212. >
  213. </h3>
  214. <div class="countWrap" style="margin-bottom: 10px">
  215. <div
  216. class="countItem"
  217. style="margin-bottom: 20px"
  218. v-for="(item, index) in weekList"
  219. :key="index"
  220. >
  221. <span class="title">循环周期: </span>
  222. <el-select
  223. v-model.trim="item.dayOfWeek"
  224. filterable
  225. clearable
  226. style="width: 200px !important"
  227. >
  228. <el-option
  229. v-for="(item, index) in weekDateList"
  230. :key="index"
  231. :label="item.label"
  232. :value="item.value"
  233. ></el-option>
  234. </el-select>
  235. <span style="margin-left: 10px">开始时间</span>
  236. <el-time-select
  237. style="margin-left: 10px; width: 100px"
  238. placeholder=""
  239. v-model.trim="item.startClassTime"
  240. :picker-options="{
  241. start: '04:30',
  242. step: '00:05',
  243. end: '23:55',
  244. }"
  245. >
  246. </el-time-select>
  247. <el-button
  248. style="margin-left: 10px"
  249. type="danger"
  250. @click="removeWeek(item)"
  251. icon="el-icon-delete"
  252. circle
  253. ></el-button>
  254. </div>
  255. </div>
  256. </div>
  257. <div slot="footer" class="dialog-footer">
  258. <el-button @click="adjustmentVisible = false">取 消</el-button>
  259. <el-button type="primary" @click="submieRecover">确 定</el-button>
  260. </div>
  261. </el-dialog>
  262. <!-- 有效期调整 -->
  263. <el-dialog title="有效期调整" width="400px" :visible.sync="expireVisible">
  264. <el-form
  265. :model="expireForm"
  266. ref="expireForm"
  267. :rules="expireRules"
  268. label-position="right"
  269. label-width="80px;"
  270. :inline="true"
  271. >
  272. <el-form-item label="课程结束时间" prop="coursesExpireDate">
  273. <el-date-picker
  274. v-model.trim="expireForm.coursesExpireDate"
  275. style="width: 200px !important"
  276. type="date"
  277. :picker-options="pickerOptions"
  278. value-format="yyyy-MM-dd"
  279. placeholder="选择日期"
  280. ></el-date-picker>
  281. </el-form-item>
  282. <!-- <div style="padding-left: 15px; color: red;">课程结束时间不得晚于,{{ expireForm.tempCoursesExpireDate }}</div> -->
  283. </el-form>
  284. <div slot="footer" class="dialog-footer">
  285. <el-button @click="expireVisible = false">取 消</el-button>
  286. <el-button type="primary" @click="submitExpireDate">确 定</el-button>
  287. </div>
  288. </el-dialog>
  289. </div>
  290. </template>
  291. <script>
  292. import pagination from "@/components/Pagination/index";
  293. import dayjs from "dayjs";
  294. import {
  295. findVipGroupStudents,
  296. leaveSchool,
  297. getStudentSurplusCourseFee,
  298. getHaveCourseBalanceStudents,
  299. addVipGroupStudents,
  300. vipPauseForStudent,
  301. getStudentPauseInfo,
  302. recoverForStudent,
  303. findTeacherWithVipGroupOrganAndSubject,
  304. vipGroupManageUpdate,
  305. } from "@/api/vipSeting";
  306. import { permission } from "@/utils/directivePage";
  307. export default {
  308. components: { pagination },
  309. data() {
  310. return {
  311. expireVisible: false,
  312. expireForm: {
  313. coursesExpireDate: "",
  314. },
  315. expireRules: {
  316. coursesExpireDate: [
  317. { required: true, message: "请选择课程结束时间", trigger: "blur" },
  318. ],
  319. },
  320. adjustmentVisible: false,
  321. tableList: [],
  322. id: "",
  323. maskStudentList: [],
  324. maskVisible: false,
  325. rules: {
  326. // 分页规则
  327. limit: 10, // 限制显示条数
  328. page: 1, // 当前页
  329. total: 0, // 总条数
  330. page_size: [10, 20, 40, 50], // 选择限制显示条数
  331. },
  332. activeStudent: "",
  333. maskForm: {
  334. search: "",
  335. },
  336. adjustmentForm: {
  337. count: "",
  338. courseTime: "",
  339. checked: false,
  340. addCount: "",
  341. courseType: "",
  342. fee: "",
  343. teacher: "",
  344. expireDate: "",
  345. },
  346. adjustmentRules: {
  347. courseTime: [{ required: true, message: "请选择开始时间" }],
  348. addCount: [{ required: true, message: "请输入加课次数" }],
  349. courseType: [{ required: true, message: "请选择课程类型" }],
  350. fee: [{ required: true, message: "请输入费用" }],
  351. teacher: [{ required: true, message: "请选择老师" }],
  352. expireDate: [{ required: true, message: "请输入有效期截止" }],
  353. },
  354. weekDateList: [
  355. { value: "1", label: "星期一" },
  356. { value: "2", label: "星期二" },
  357. { value: "3", label: "星期三" },
  358. { value: "4", label: "星期四" },
  359. { value: "5", label: "星期五" },
  360. { value: "6", label: "星期六" },
  361. { value: "7", label: "星期日" },
  362. ],
  363. weekList: [
  364. {
  365. dayOfWeek: "",
  366. startTime: "",
  367. endTime: "",
  368. moid: new Date().getTime(),
  369. },
  370. ],
  371. pickerOptions: {
  372. firstDayOfWeek: 1,
  373. disabledDate(time) {
  374. return time.getTime() + 86400000 <= new Date().getTime();
  375. },
  376. },
  377. teacherList: [],
  378. };
  379. },
  380. mounted() {
  381. this.__init();
  382. },
  383. activated() {
  384. this.__init();
  385. },
  386. methods: {
  387. permission(str) {
  388. console.log(permission(str));
  389. return permission(str);
  390. },
  391. __init() {
  392. let id = this.$route.query.id;
  393. this.id = id;
  394. this.rules.page = 1;
  395. this.getStudents();
  396. findTeacherWithVipGroupOrganAndSubject({ vipGroupId: this.id }).then(
  397. (res) => {
  398. if (res.code == 200) {
  399. this.teacherList = res.data;
  400. }
  401. }
  402. );
  403. },
  404. search() {
  405. this.rules.page = 1;
  406. this.getList();
  407. },
  408. reset() {
  409. this.rules.page = 1;
  410. this.maskForm.search = null;
  411. this.activeStudent = "";
  412. this.getList();
  413. },
  414. getStudents() {
  415. findVipGroupStudents({ vipGroupId: this.id }).then((res) => {
  416. if (res.code == 200) {
  417. this.tableList = res.data.rows;
  418. for (let i in this.tableList) {
  419. this.tableList[i].fee = 0;
  420. this.tableList[i].visible = false;
  421. }
  422. }
  423. });
  424. },
  425. // 删除循环周
  426. removeWeek(item) {
  427. for (let i in this.weekList) {
  428. if (this.weekList[i].id == item.id) {
  429. this.weekList.splice(i, 1);
  430. }
  431. }
  432. },
  433. leaveSchool(scope) {
  434. let studentId = scope.row.id;
  435. let vipGroupId = this.id;
  436. let amount = scope.row.fee;
  437. leaveSchool({ studentId, vipGroupId, amount }).then((res) => {
  438. if (res.code == 200) {
  439. this.$message.success("退学成功");
  440. this.getStudents();
  441. }
  442. });
  443. },
  444. lookFee(scope) {
  445. this.$confirm(`确定是否退学?`, "提示", {
  446. confirmButtonText: "确定",
  447. cancelButtonText: "取消",
  448. type: "warning",
  449. })
  450. .then(() => {
  451. let id = scope.row.id;
  452. if (scope.row.studentStatus == 3) {
  453. this.leaveSchool(scope);
  454. } else {
  455. getStudentSurplusCourseFee({
  456. studentId: id,
  457. vipGroupId: this.id,
  458. }).then((res) => {
  459. if (res.code == 200) {
  460. // scope.row.fee =
  461. this.$prompt("请输入退课金额", "提示", {
  462. confirmButtonText: "确定",
  463. cancelButtonText: "取消",
  464. inputValue: res.data.suplusCourseFee,
  465. })
  466. .then(({ value }) => {
  467. scope.row.fee = value;
  468. this.leaveSchool(scope);
  469. })
  470. .catch((res) => {});
  471. }
  472. });
  473. }
  474. })
  475. .catch(() => {});
  476. },
  477. addStudentList() {
  478. // 发请求 搜索学生
  479. if (this.tableList.length <= 0) {
  480. this.$confirm(
  481. "添加学员后,该课程组将无法通过购买途径加入,是否确认该操作?",
  482. "提示",
  483. {
  484. confirmButtonText: "确定",
  485. cancelButtonText: "取消",
  486. type: "warning",
  487. }
  488. )
  489. .then(() => {
  490. this.getList();
  491. })
  492. .catch(() => {});
  493. } else {
  494. this.getList();
  495. }
  496. },
  497. getList() {
  498. let search = this.maskForm.search || null;
  499. getHaveCourseBalanceStudents({
  500. organId: null,
  501. page: this.rules.page,
  502. rows: this.rules.limit,
  503. search,
  504. }).then((res) => {
  505. if (res.code == 200) {
  506. this.rules.total = res.data.total;
  507. this.maskStudentList = res.data.rows;
  508. this.maskVisible = true;
  509. }
  510. });
  511. },
  512. getTemplateRow(index, row) {
  513. this.activeStudent = row.id;
  514. },
  515. addStudent() {
  516. if (!this.activeStudent) {
  517. this.$message.error("请选择一名学生");
  518. return;
  519. }
  520. addVipGroupStudents({
  521. vipGroupId: this.id,
  522. studentIds: this.activeStudent,
  523. }).then((res) => {
  524. if (res.code == 200) {
  525. this.$message.success("添加成功");
  526. this.getStudents();
  527. }
  528. });
  529. },
  530. stopCourse(scope) {
  531. this.$confirm("是否休学?", "提示", {
  532. confirmButtonText: "确定",
  533. cancelButtonText: "取消",
  534. type: "warning",
  535. })
  536. .then(() => {
  537. // 发请求 申请休学
  538. vipPauseForStudent({
  539. vipGroupId: this.id,
  540. studentId: scope.row.id,
  541. }).then((res) => {
  542. if (res.code == 200) {
  543. this.$message.success("休学成功");
  544. this.getStudents();
  545. }
  546. });
  547. })
  548. .catch(() => {});
  549. },
  550. recoveryCourse(scope) {
  551. getStudentPauseInfo({
  552. studentId: scope.row.id,
  553. vipGroupId: this.id,
  554. }).then((res) => {
  555. if (res.code == 200) {
  556. if (res.data.isPause == 0) {
  557. // vip状态没暂停 不需要排课
  558. // 课程编号 学生编号
  559. const h = this.$createElement;
  560. this.$msgbox({
  561. title: "提示",
  562. message: h("p", null, [
  563. h("p", null, "将按照当前剩余课时安排上课"),
  564. // h("span", null, `排课截至时间:`),
  565. // h("span", { style: "color: red" }, `${res.data.expireDate}`), 辜经理同意注释
  566. ]),
  567. confirmButtonText: "确定",
  568. cancelButtonText: "取消",
  569. showCancelButton: true,
  570. })
  571. .then(() => {
  572. // 发请求 恢复上课
  573. recoverForStudent({
  574. vipGroupId: this.id,
  575. userId: scope.row.id,
  576. }).then((res) => {
  577. if (res.code == 200) {
  578. this.adjustmentVisible = false;
  579. this.getStudents();
  580. }
  581. });
  582. })
  583. .catch(() => {});
  584. } else {
  585. this.activeStudent = res.data.studentId;
  586. this.adjustmentVisible = true;
  587. this.adjustmentForm.teacher = res.data.teacherId;
  588. this.adjustmentForm.count = `${res.data.totalCourseTimes}+${res.data.giveCourseTimes}`;
  589. this.adjustmentForm.expireDate = res.data.expireDate;
  590. }
  591. }
  592. });
  593. // this.$confirm('是否休学?', '提示', {
  594. // confirmButtonText: '确定',
  595. // cancelButtonText: '取消',
  596. // type: 'warning'
  597. // }).then(() => {
  598. // // 发请求 申请休学
  599. // vipPauseForStudent({ vipGroupId: this.id, studentId: scope.row.id }).then(res => {
  600. // if (res.code == 200) {
  601. // this.$message.success('休学成功')
  602. // this.getList()
  603. // }
  604. // })
  605. // }).catch(() => {
  606. // });
  607. },
  608. addWeek() {
  609. // 添加循环周期
  610. this.weekList.push({
  611. dayOfWeek: "",
  612. startClassTime: "",
  613. endClassTime: "",
  614. id: new Date(),
  615. });
  616. },
  617. submieRecover() {
  618. if (this.weekList.length <= 0) {
  619. this.$message.error("排课循环次数不能为空");
  620. return;
  621. }
  622. this.$refs["adjustmentForm"].validate((res) => {
  623. if (res) {
  624. // 发请求
  625. let obj = {};
  626. obj.courseCreateStartTime = this.adjustmentForm.courseTime;
  627. obj.skipHoliday = this.adjustmentForm.checked;
  628. obj.teacherId = this.adjustmentForm.teacher;
  629. obj.userId = this.activeStudent;
  630. obj.courseTimes = [];
  631. obj.courseTimes = this.weekList;
  632. obj.vipGroupId = this.id;
  633. obj.expireDate = this.adjustmentForm.expireDate;
  634. recoverForStudent(obj).then((res) => {
  635. if (res.code == 200) {
  636. this.adjustmentVisible = false;
  637. this.getStudents();
  638. }
  639. });
  640. } else {
  641. this.$message.error("请填写必要参数");
  642. }
  643. });
  644. this.activeStudent;
  645. },
  646. submitExpireDate() {
  647. this.$refs.expireForm.validate((some) => {
  648. if (some) {
  649. vipGroupManageUpdate({
  650. id: this.id,
  651. coursesExpireDate: this.expireForm.coursesExpireDate,
  652. }).then((res) => {
  653. if (res.code == 200) {
  654. this.$message.success("有效期修改成功");
  655. this.expireVisible = false;
  656. this.getList();
  657. } else {
  658. this.$message.error(res.msg);
  659. }
  660. });
  661. } else {
  662. return;
  663. }
  664. });
  665. },
  666. },
  667. };
  668. </script>
  669. <style lang="scss" scoped>
  670. .title {
  671. display: inline-block;
  672. width: 108px;
  673. text-align: right;
  674. margin-right: 10px;
  675. }
  676. /deep/.el-input-number .el-input__inner {
  677. text-align: left !important;
  678. }
  679. .btnWrap {
  680. display: flex;
  681. flex-direction: row;
  682. justify-content: flex-start;
  683. margin-bottom: 15px;
  684. }
  685. </style>