user-pay-form.vue 18 KB

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