create-user-pay.vue 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536
  1. <template>
  2. <div>
  3. <el-alert title="班级信息" :closable="false" class="alert" type="info">
  4. </el-alert>
  5. <el-form :model="form" label-width="120px">
  6. <el-row>
  7. <el-col :span="12">
  8. <el-form-item label="声部班">
  9. <el-select v-model.trim="form.signClass" filterable clearable>
  10. <el-option
  11. v-for="(item, index) in signList.filter(
  12. (item) => item.lockFlag != 1
  13. )"
  14. :key="index"
  15. :value="item.id"
  16. :label="item.name"
  17. ></el-option>
  18. </el-select>
  19. </el-form-item>
  20. </el-col>
  21. <el-col :span="12">
  22. <el-form-item label="合奏班">
  23. <el-select v-model.trim="form.mixClass" filterable clearable>
  24. <el-option
  25. v-for="(item, index) in mixList.filter(
  26. (item) => item.lockFlag != 1
  27. )"
  28. :key="index"
  29. :value="item.id"
  30. :label="item.name"
  31. ></el-option>
  32. </el-select>
  33. </el-form-item>
  34. </el-col>
  35. <el-col :span="12">
  36. <el-form-item label="基础技能班">
  37. <el-select v-model.trim="form.highClass" filterable clearable>
  38. <el-option
  39. v-for="(item, index) in highList.filter(
  40. (item) => item.lockFlag != 1
  41. )"
  42. :key="index"
  43. :value="item.id"
  44. :label="item.name"
  45. ></el-option>
  46. </el-select>
  47. </el-form-item>
  48. </el-col>
  49. <el-col :span="12">
  50. <el-form-item label="临时班">
  51. <select-all
  52. style="width:230px!important"
  53. v-model.trim="form.snapClass"
  54. filterable
  55. clearable
  56. multiple
  57. >
  58. <el-option
  59. v-for="(item, index) in snapList.filter(
  60. (item) => item.lockFlag != 1
  61. )"
  62. :key="index"
  63. :value="item.id"
  64. :label="item.name"
  65. ></el-option>
  66. </select-all>
  67. </el-form-item>
  68. </el-col>
  69. <el-col :span="12">
  70. <el-form-item label="线上基础技能班">
  71. <el-select v-model.trim="form.highonline" filterable clearable>
  72. <el-option
  73. v-for="(item, index) in highonlineList.filter(
  74. (item) => item.lockFlag != 1
  75. )"
  76. :key="index"
  77. :value="item.id"
  78. :label="item.name"
  79. ></el-option>
  80. </el-select>
  81. </el-form-item>
  82. </el-col>
  83. </el-row>
  84. <!-- highonlineList -->
  85. </el-form>
  86. <el-alert title="课程信息设置" :closable="false" class="alert" type="info">
  87. </el-alert>
  88. <extraClass
  89. :form="eclass"
  90. ref="eclass"
  91. :isUserType="true"
  92. :isCommon="false"
  93. :isDisabled="true"
  94. :courseUnitPriceSettingsByType="organizationCourseUnitPriceSettingsByType"
  95. @priceChange="priceChange"
  96. @moneyChange="syncAllMoney"
  97. />
  98. <el-alert
  99. title="会员设置"
  100. :closable="false"
  101. class="alert"
  102. style="margin-top: 20px"
  103. type="info"
  104. v-if="courseViewType == 1"
  105. >
  106. </el-alert>
  107. <memberSetting
  108. v-if="courseViewType == 1"
  109. :addStudent="true"
  110. :form="memberForm"
  111. :isUserType="isUserType"
  112. :organId="baseInfo.organId"
  113. />
  114. <el-alert title="缴费设置" :closable="false" class="alert" type="info">
  115. </el-alert>
  116. <el-form ref="payment" :model="payment">
  117. <el-form-item
  118. label="缴费方式"
  119. prop="paymentPattern"
  120. label-width="160px"
  121. :rules="[
  122. { required: true, message: '请选择缴费方式', trigger: 'change' },
  123. ]"
  124. >
  125. <el-select
  126. style="width: 100% !important"
  127. v-model="payment.paymentPattern"
  128. placeholder="请选择缴费方式"
  129. :disabled="courseViewType == 1"
  130. >
  131. <el-option
  132. v-for="item in paymentPatternTypeOptions"
  133. :key="item.value"
  134. :label="item.label"
  135. :value="item.value"
  136. >
  137. </el-option>
  138. </el-select>
  139. </el-form-item>
  140. </el-form>
  141. <template v-if="payment.paymentPattern == 0">
  142. <el-collapse :value="collapse" @change="collapseChange">
  143. <el-collapse-item
  144. v-for="(item, index) in cycles"
  145. :key="index"
  146. :name="index"
  147. >
  148. <template slot="title">
  149. <div class="collapse-title">
  150. <span>缴费周期 {{ index + 1 }}</span>
  151. <i
  152. v-if="cycles.length > 1"
  153. class="el-icon-circle-close"
  154. @click.stop="removeCycle(index)"
  155. ></i>
  156. </div>
  157. </template>
  158. <paymentCycle
  159. ref="cycles"
  160. :form="item"
  161. :hidePaymentPattern="true"
  162. :isUserType="true"
  163. :isCommon="false"
  164. :isDisabled="true"
  165. />
  166. </el-collapse-item>
  167. </el-collapse>
  168. <el-button
  169. icon="el-icon-circle-plus-outline"
  170. plain
  171. type="info"
  172. size="small"
  173. style="width: 100%; margin: 20px 0"
  174. @click="addCycle"
  175. >新增缴费周期</el-button
  176. >
  177. </template>
  178. <paymentCycle
  179. v-else
  180. ref="cycle"
  181. :isUserType="true"
  182. :hidePaymentPattern="true"
  183. :form.sync="cycle"
  184. :isCommon="false"
  185. :isDisabled="true"
  186. :courseViewType="courseViewType"
  187. />
  188. <el-alert title="其它" :closable="false" class="alert" type="info">
  189. </el-alert>
  190. <otherform :form="other" ref="other" />
  191. <div slot="footer" class="dialog-footer">
  192. <p>缴费总金额:{{ money || 0 }}元</p>
  193. <div>
  194. <el-button @click="$listeners.close">取 消</el-button>
  195. <el-button type="primary" @click="submit">确认</el-button>
  196. </div>
  197. </div>
  198. </div>
  199. </template>
  200. <script>
  201. import numeral from "numeral";
  202. import paymentCycle from "../../../resetTeaming/modals/payment-cycle";
  203. import otherform from "../../../resetTeaming/modals/other";
  204. import extraClass from "../../../resetTeaming/modals/extra-class";
  205. import { musicGroupPaymentCalenderAdd } from "../../../resetTeaming/api";
  206. import { queryRemainCourseTypeDuration } from "../../api";
  207. import { courseType } from "@/constant";
  208. import { getTimes, objectToOptions } from "@/utils";
  209. import { paymentPatternType } from "@/constant";
  210. import memberSetting from "../../../resetTeaming/modals/member-setting";
  211. export default {
  212. props: [
  213. "snapList",
  214. "highList",
  215. "mixList",
  216. "signList",
  217. "highonlineList",
  218. "createdUserId",
  219. "organizationCourseUnitPriceSettings",
  220. "musicGroupId",
  221. "baseInfo",
  222. ],
  223. components: {
  224. paymentCycle,
  225. otherform,
  226. extraClass,
  227. memberSetting,
  228. },
  229. data() {
  230. return {
  231. courseTypeOptions: courseType,
  232. ids: "",
  233. form: {
  234. signClass: "",
  235. mixClass: "",
  236. highClass: "",
  237. snapClass: "",
  238. highonline: "",
  239. },
  240. payment: {
  241. paymentPattern: null,
  242. },
  243. other: {},
  244. cycle: {},
  245. eclass: [],
  246. collapse: [0],
  247. cycles: [{}],
  248. organizationCourseUnitPriceSettingsByType: {},
  249. paymentPatternTypeOptions: objectToOptions(paymentPatternType),
  250. isUserType: true,
  251. memberForm: {
  252. memberRankSettingId: "",
  253. memberValidDate: 6,
  254. memberPaymentAmount: "",
  255. originalMemberPaymentAmount: "",
  256. paymentDate: [],
  257. },
  258. money: "",
  259. courseViewType: "",
  260. };
  261. },
  262. watch: {
  263. "form.signClass"() {
  264. this.classChange();
  265. },
  266. "form.mixClass"() {
  267. this.classChange();
  268. },
  269. "form.highClass"() {
  270. this.classChange();
  271. },
  272. "form.snapClass"() {
  273. this.classChange();
  274. },
  275. "form.highonline"() {
  276. this.classChange();
  277. },
  278. "payment.paymentPattern"() {
  279. this.syncAllMoney();
  280. },
  281. "memberForm.memberPaymentAmount"() {
  282. this.syncAllMoney();
  283. },
  284. baseInfo(val) {
  285. this.formatCourse();
  286. this.courseViewType = val.courseViewType;
  287. if (val.courseViewType == 1) {
  288. this.payment.paymentPattern = "1";
  289. }
  290. },
  291. },
  292. mounted() {
  293. this.courseViewType = this.baseInfo.courseViewType;
  294. if (this.baseInfo.courseViewType == 1) {
  295. this.payment.paymentPattern = "1";
  296. }
  297. this.formatCourse();
  298. },
  299. methods: {
  300. addExtraClass() {
  301. this.eclass.push({});
  302. },
  303. priceChange(item, index) {
  304. const _ = [...this.eclass];
  305. const active =
  306. this.organizationCourseUnitPriceSettingsByType[item.courseType] || {};
  307. const price = Math.round(
  308. numeral(item.courseTotalMinuties || 1)
  309. .multiply(active.unitPrice || 1)
  310. .value()
  311. );
  312. item.courseCurrentPrice = price;
  313. item.courseOriginalPrice = price;
  314. _[index] = item;
  315. this.eclass = [..._];
  316. this.syncAllMoney();
  317. },
  318. syncAllMoney() {
  319. let money = 0;
  320. let first = 0;
  321. let other = 0;
  322. console.log(this.eclass);
  323. for (const item of this.eclass) {
  324. money += item.courseCurrentPrice;
  325. if (this.cycles && this.cycles.length) {
  326. if (item.isStudentOptional) {
  327. first += item.courseCurrentPrice;
  328. } else {
  329. const floorMoney = Math.floor(
  330. item.courseCurrentPrice / this.cycles.length
  331. );
  332. const remainder = item.courseCurrentPrice % this.cycles.length;
  333. first += floorMoney + remainder;
  334. other += floorMoney;
  335. }
  336. }
  337. }
  338. if (this.cycles.length) {
  339. const floorMoney = Math.floor(money / this.cycles.length);
  340. const remainder = money % this.cycles.length;
  341. this.cycles = this.cycles.map((item, index) => {
  342. return {
  343. ...item,
  344. paymentAmount: index === 0 ? first : other,
  345. };
  346. });
  347. }
  348. if (this.$refs.cycle) {
  349. this.$set(this.cycle, "paymentAmount", money);
  350. }
  351. money += parseFloat(this.memberForm.memberPaymentAmount);
  352. this.money = money;
  353. return money;
  354. },
  355. removeExtraClass(index) {
  356. this.eclass[index] = null;
  357. this.eclass = this.eclass.filter((item) => !!item);
  358. },
  359. formatCourse() {
  360. const organId = this.baseInfo?.organId;
  361. const chargeTypeId = this.baseInfo?.chargeTypeId;
  362. const _ = {};
  363. const list = (this.organizationCourseUnitPriceSettings || []).filter(
  364. (item) =>
  365. organId &&
  366. organId == item.organId &&
  367. chargeTypeId &&
  368. chargeTypeId == item.chargeTypeId
  369. );
  370. for (const item of list) {
  371. _[item.courseType] = item;
  372. }
  373. this.organizationCourseUnitPriceSettingsByType = _;
  374. return _;
  375. },
  376. getAllIds() {
  377. return [
  378. this.form.signClass,
  379. this.form.mixClass,
  380. this.form.highClass,
  381. this.form.highonline,
  382. ...this.form.snapClass,
  383. ].filter((item) => !!item);
  384. },
  385. async classChange() {
  386. try {
  387. const ids = this.getAllIds().join(",");
  388. if (ids) {
  389. const res = await queryRemainCourseTypeDuration({
  390. classGroupIdList: ids,
  391. });
  392. this.ids = ids;
  393. const _ = res.data.map((item) => {
  394. const active =
  395. this.organizationCourseUnitPriceSettingsByType[item.courseType] ||
  396. {};
  397. const money = Math.round(
  398. numeral(active.unitPrice || 1)
  399. .multiply(item.remainMinutes || 1)
  400. .value()
  401. );
  402. return {
  403. courseType: item.courseType,
  404. courseTotalMinuties: item.remainMinutes,
  405. courseOriginalPrice: money,
  406. courseCurrentPrice: money,
  407. };
  408. });
  409. this.eclass = [..._];
  410. this.syncAllMoney();
  411. }
  412. } catch (error) {
  413. console.log(error);
  414. }
  415. },
  416. getForms() {
  417. const { $refs: refs } = this;
  418. return [
  419. refs.eclass,
  420. refs.cycle,
  421. refs.payment,
  422. refs.other,
  423. ...(refs.cycles || []),
  424. ]
  425. .filter((item) => !!item)
  426. .map((item) => item.$refs.form || item);
  427. },
  428. addCycle() {
  429. this.cycles.push({});
  430. this.collapse.push(this.cycles.length);
  431. this.syncAllMoney();
  432. },
  433. removeCycle(index) {
  434. this.cycles[index] = null;
  435. this.cycles = this.cycles.filter((item) => !!item);
  436. if (this.collapse.includes(index)) {
  437. this.collapse.splice(index, 1);
  438. this.collapse = this.collapse.map((item, _index) =>
  439. _index - 1 >= index ? item-- : item
  440. );
  441. }
  442. this.syncAllMoney();
  443. },
  444. collapseChange(val) {
  445. this.collapse = val;
  446. },
  447. async submit() {
  448. const forms = this.getForms();
  449. const valided = [];
  450. for (const form of forms) {
  451. form.validate((valid) => {
  452. if (valid) {
  453. valided.push(form);
  454. }
  455. });
  456. }
  457. if (!this.getAllIds().length) {
  458. this.$message.error("请至少选择一个班级");
  459. return;
  460. }
  461. if (forms.length === valided.length) {
  462. const cyclelist =
  463. this.payment.paymentPattern == 0 ? this.cycles : [this.cycle];
  464. const data = {
  465. attribute1: this.ids,
  466. ...this.memberForm,
  467. musicGroupPaymentDateRangeList: cyclelist.map((item) => {
  468. const { paymentDate, paymentValid, ...other } = item;
  469. return {
  470. ...other,
  471. ...getTimes(paymentDate, [
  472. "startPaymentDate",
  473. "deadlinePaymentDate",
  474. ]),
  475. ...getTimes(paymentValid, [
  476. "paymentValidStartDate",
  477. "paymentValidEndDate",
  478. ]),
  479. paymentPattern: this.payment.paymentPattern,
  480. };
  481. }),
  482. paymentPattern: this.payment.paymentPattern,
  483. musicGroupId: this.musicGroupId,
  484. paymentType: "ADD_STUDENT",
  485. payUserType: "STUDENT",
  486. studentIds: this.createdUserId,
  487. musicGroupPaymentCalenderCourseSettingsList: this.eclass,
  488. ...this.other,
  489. };
  490. try {
  491. await musicGroupPaymentCalenderAdd(data);
  492. this.$message.success("提交成功");
  493. this.$listeners.submited();
  494. this.$listeners.close();
  495. } catch (error) {
  496. console.log(error);
  497. }
  498. }
  499. },
  500. },
  501. };
  502. </script>
  503. <style lang="less" scoped>
  504. .dialog-footer {
  505. margin-top: 20px;
  506. display: flex;
  507. flex-direction: row;
  508. align-items: center;
  509. justify-content: space-between;
  510. // text-align: right;
  511. p {
  512. font-size: 16px;
  513. margin-right: 20px;
  514. color: red;
  515. margin-left: 40px;
  516. }
  517. }
  518. .alert {
  519. margin-bottom: 10px;
  520. }
  521. .collapse-title {
  522. display: flex;
  523. justify-content: space-between;
  524. align-items: center;
  525. width: 100%;
  526. .el-icon-circle-close {
  527. font-size: 16px;
  528. margin-right: 10px;
  529. }
  530. }
  531. /deep/ .el-collapse-item__wrap {
  532. padding-top: 20px;
  533. }
  534. </style>