ConfirmMingtou.vue 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555
  1. <template>
  2. <el-dialog
  3. :close-on-click-modal="false"
  4. :close-on-press-escape="false"
  5. :visible="visible"
  6. :before-close="handleClose"
  7. title="出票确认"
  8. width="80%">
  9. <el-form
  10. v-loading="submitDisable"
  11. element-loading-text="正在处理付款请求,请稍候"
  12. class="info-wrap"
  13. label-width="100px"
  14. :inline="true">
  15. <div style="margin-bottom: 20px;">
  16. 付款方式:
  17. <el-radio-group
  18. :disabled="createOrderStatus===1"
  19. size="medium"
  20. fill="#ff6934"
  21. v-model="currentPayChannel">
  22. <el-radio-button
  23. v-for="val in payChannelOptions"
  24. :key="val"
  25. :label="val"></el-radio-button>
  26. </el-radio-group>
  27. </div>
  28. <div class="btn-wrap">
  29. <span class="item">整单打折减免金额<Tip
  30. msg="输入整单减免金额后按回车。"
  31. style="margin-left: 0!important;"></Tip>:
  32. <el-input-number
  33. :min="0"
  34. :max="original_finalPrice"
  35. :precision="2"
  36. :controls="false"
  37. v-model.number="cutOrderMoney"
  38. ></el-input-number>
  39. </span>
  40. </div>
  41. <el-table
  42. border
  43. :data="orderInfo"
  44. style="margin-bottom:30px">
  45. <el-table-column
  46. prop="ticketName"
  47. label="票种名称">
  48. </el-table-column>
  49. <el-table-column
  50. label="游玩日期">
  51. <template slot-scope="scope">
  52. {{ scope.row.payDateBegin | formatDate }}
  53. </template>
  54. </el-table-column>
  55. <el-table-column
  56. prop="count"
  57. label="数量">
  58. </el-table-column>
  59. <!-- <el-table-column
  60. prop="total"
  61. label="小计">
  62. </el-table-column> -->
  63. </el-table>
  64. <el-form-item
  65. label="总数"
  66. class="warning">
  67. <span class="strong">
  68. {{ totalCount }}
  69. </span> 张
  70. </el-form-item>
  71. <el-form-item
  72. label="总金额"
  73. class="warning">
  74. <span class="strong">
  75. {{ orderPrice }}
  76. </span> 元
  77. </el-form-item>
  78. <el-form-item
  79. label="折扣"
  80. class="warning">
  81. <span class="strong">
  82. {{ discount }}
  83. </span> 折
  84. </el-form-item>
  85. <el-form-item
  86. v-if="cutOrderMoney===0"
  87. label="折后总额"
  88. class="warning">
  89. <span class="strong">
  90. {{ finalPrice }}
  91. </span> 元
  92. </el-form-item>
  93. <el-form-item
  94. v-else
  95. label="折后总额"
  96. class="warning">
  97. <span class="strong">
  98. <del>{{ original_finalPrice }}</del> {{ real_finalPrice }}
  99. </span> 元
  100. </el-form-item>
  101. <el-form-item
  102. label="检票方式"
  103. class="warning">
  104. <span class="strong">
  105. {{ getCheckWayName(formData?formData.checkWay:'') }}
  106. </span>
  107. </el-form-item>
  108. <el-form-item
  109. label="付款方式"
  110. class="warning">
  111. <span class="strong">
  112. {{ currentPayChannel }}
  113. </span>
  114. </el-form-item>
  115. <el-form-item
  116. v-if="currentPayChannel==='旅行社余额' && currentAgency"
  117. label="预付余额"
  118. class="warning">
  119. <span class="strong">
  120. {{ currentAgency.prepayment_amount }}
  121. </span>
  122. </el-form-item>
  123. <el-form-item
  124. v-if="currentPayChannel==='旅行社余额' && currentAgency"
  125. label="信用限额"
  126. class="warning">
  127. <span class="strong">
  128. {{ currentAgency.prepayment_type?currentAgency.prepayment_limit:'无限制' }}
  129. </span>
  130. </el-form-item>
  131. <br>
  132. <el-form-item
  133. label="备注">
  134. {{ formData.remark }}
  135. </el-form-item>
  136. </el-form>
  137. <div
  138. class="table-box"
  139. style="padding-top:0">
  140. <Collapse title="游客详情信息">
  141. <el-table
  142. stripe
  143. :data="tickets">
  144. <el-table-column
  145. type="index"
  146. width="50"
  147. >
  148. </el-table-column>
  149. <el-table-column
  150. prop="guestName"
  151. label="游客姓名"
  152. >
  153. </el-table-column>
  154. <el-table-column
  155. prop="guestPhone"
  156. label="游客电话"
  157. >
  158. </el-table-column>
  159. <el-table-column
  160. prop="guestIdentify"
  161. label="身份证"
  162. min-width="130"
  163. >
  164. </el-table-column>
  165. <el-table-column
  166. v-if="formData.checkWay===5"
  167. prop="ticketNo"
  168. label="票号"
  169. min-width="130"
  170. >
  171. </el-table-column>
  172. </el-table>
  173. </Collapse>
  174. </div>
  175. <div class="dialog-btn-wrap">
  176. 打印小票
  177. <el-switch
  178. style="margin:0 50px 0 10px"
  179. v-model="isPrintSmallTicket">
  180. </el-switch>
  181. 自动打印
  182. <el-switch
  183. style="margin:0 50px 0 10px"
  184. v-model="isPrint">
  185. </el-switch>
  186. <el-button @click="handleClose">
  187. 取消
  188. </el-button>
  189. <el-button
  190. type="warning"
  191. v-if="showRefresh"
  192. :disabled="isRefresh"
  193. @click="payRefresh">
  194. 刷新支付状态<span v-if="isRefresh">({{ countNum }})</span>
  195. </el-button>
  196. <el-button
  197. type="danger"
  198. v-if="scenicName==='狼山景区'||scenicName==='军山景区'"
  199. @click="$refs.queryPos.show()">
  200. 查询POS机交易
  201. </el-button>
  202. <el-button
  203. type="primary"
  204. :disabled="submitDisable"
  205. @click="submit()"
  206. >
  207. {{ submitDisable?'正在处理':'确认出票' }}
  208. </el-button>
  209. </div>
  210. <ReadValueCard
  211. ref="readValueCard"
  212. @getInfo="handleValueCardPay"
  213. @close="submitDisable=false"></ReadValueCard>
  214. <QueryPos
  215. ref="queryPos"
  216. @confirm="handleQueryPos"></QueryPos>
  217. <!-- GlobalComponents 全局组件 -->
  218. <PosQrCodeReader
  219. ref="posQrCodeReader"
  220. @getQrcode="submit"></PosQrCodeReader>
  221. <XSCQrCodeReader
  222. ref="XSCQrCodeReader"
  223. @getQrcode="submit"></XSCQrCodeReader>
  224. <ValueCardInput
  225. ref="ValueCardInput"
  226. @getMemberSystemId="submit"></ValueCardInput>
  227. </el-dialog>
  228. </template>
  229. <script>
  230. import mixin from './mixin'
  231. import posMixin from '@/pages/common/posMixin'
  232. // import confirmMixin from '../common/confirmMixin'
  233. import confirmMixinMingtou from '../common/confirmMixinMingtouGroup'
  234. import Tip from '@/components/Tip'
  235. export default {
  236. props: {
  237. discount: {
  238. type: Number,
  239. default: 0
  240. },
  241. // 当整单打折时,这个原有的最终价格不再是最终价格了。
  242. // 考虑到它本来是父组件computed算出来而且是传给本组件的props,
  243. // 也不能改变这个finalPrice的值。只能改变它的定义了。
  244. // 即,当整单打折时,它不再是真正的最终价格
  245. finalPrice: {
  246. type: Number,
  247. default: 0
  248. }
  249. },
  250. // mixins: [mixin, posMixin, confirmMixin],
  251. mixins: [mixin, posMixin, confirmMixinMingtou],
  252. computed: {
  253. agencyList () {
  254. return this.$store.state.app.agencyList
  255. },
  256. currentAgency () {
  257. return this.agencyList.find(i => i.id === this.formData.travelAgencyId)
  258. }
  259. },
  260. components: {
  261. Tip
  262. },
  263. data () {
  264. return {
  265. pageName: '团体售票',
  266. cutOrderMoney: 0, // 整单打折金额
  267. cutOrderMoney_work: 0, // 计算中不断变小的打折金额
  268. // 注意,因为cutOrderMoney被watch了,一改动它就会触发setParams
  269. // 为防止死循环,不断随着减price而变小的待减免金额另外用cutOrderMoney_work去存
  270. original_finalPrice: this.finalPrice, // 记录整单打折前的最终价格
  271. original_tickets: this.tickets, // 记录整单打折前的tickets,在mixin里去设初值
  272. // 注意,不应到mounted里去赋值,因为这个对话框只是显示和隐藏,并不会重新mount,
  273. // 应当到ConfirmMixinMingtouGroup.js的show方法里去赋初值
  274. real_finalPrice: this.finalPrice, // 真·最终价格
  275. // 注:这里初值不能写成this.finalPrice - this.cutOrderMoney,否则NAN
  276. printPlayDateBegin: ''
  277. }
  278. },
  279. watch: {
  280. cutOrderMoney (e) {
  281. this.cutOrderMoney_work = e // 重置待抵消减免金额
  282. this.resetTickets()
  283. console.log('整单打折减免金额:', this.cutOrderMoney)
  284. // finalPrice可能会变,故采用original_finalPrice减cutOrderMoney来算
  285. this.doOrderCut()
  286. }
  287. },
  288. methods: {
  289. resetTickets () {
  290. // 设置整单打折金额后再修改需要重置tickets,否则会在之前打折的基础上再打折
  291. this.tickets = JSON.parse(JSON.stringify(this.original_tickets))
  292. },
  293. doOrderCut () {
  294. // 根据cutOrderMoney去改写tickets里的price
  295. // 有可能finalPrice被重算,故先改tickets数组中的单价
  296. // 注意setParams里还用到this.orderInfo,这里面每个票种元素的tickets数组也要去改。
  297. console.log('整单操作前tickets:', this.tickets)
  298. console.log('价格们(整单操作前):' + this.tickets.map(item => item.price).join(' '))
  299. console.log('orderInfo:', this.orderInfo)
  300. // 修改setParams里面要用到的this.orderInfo票种数组内每个票种的tickets数组
  301. const reduceOrderInfo = (OrderInfo, cutOrderMoney) => {
  302. // OrderInfo是基于票种的数组,每个票种里面有tickets属性
  303. let result = []
  304. for (let ticketInfo of OrderInfo) {
  305. let tickets = ticketInfo.tickets
  306. let updatedInnerTickets = []
  307. for (let ticketItem of tickets) {
  308. if (cutOrderMoney > 0) {
  309. // 整单打折没减完
  310. if (ticketItem.price > cutOrderMoney) {
  311. // 该票价足够剩下的整单打折
  312. ticketItem.price -= cutOrderMoney
  313. cutOrderMoney = 0
  314. } else {
  315. cutOrderMoney -= ticketItem.price // 注意先后顺序
  316. ticketItem.price = 0
  317. }
  318. }
  319. ticketItem.discountPrice = ticketItem.price
  320. updatedInnerTickets.push(ticketItem)
  321. }
  322. console.log('票种价格:', tickets.map(item => item.price).join(' '))
  323. let updatedTicketInfo = { ...ticketInfo, tickets: updatedInnerTickets }
  324. result.push(updatedTicketInfo)
  325. }
  326. return result
  327. }
  328. // 修改打平后的tickets数组
  329. const reduceTickets = (tickets, cutOrderMoney) => {
  330. let result = []
  331. for (let ticket of tickets) {
  332. if (cutOrderMoney > 0) {
  333. // 整单打折没减完
  334. if (ticket.price >= cutOrderMoney) {
  335. // 该票价足够剩下的整单打折
  336. ticket.price -= cutOrderMoney
  337. cutOrderMoney = 0
  338. } else {
  339. cutOrderMoney -= ticket.price // 注意先后顺序
  340. ticket.price = 0
  341. }
  342. }
  343. ticket.discountPrice = ticket.price
  344. result.push(ticket)
  345. }
  346. return result
  347. }
  348. const tmpOrderInfo = reduceOrderInfo(this.orderInfo, this.cutOrderMoney_work) // setParams里面要用
  349. console.log('this.orderInfo', this.orderInfo)
  350. console.log('tmpOrderInfo:', tmpOrderInfo)
  351. this.orderInfo = tmpOrderInfo
  352. this.tickets = reduceTickets(this.tickets, this.cutOrderMoney_work) // 注意顺序
  353. console.log('整单操作后的tickets:', this.tickets)
  354. console.log('价格们(setParams前):' + this.tickets.map(item => item.price).join(' '))
  355. /**
  356. * 不能改变这个this.finalPrice,它是父组件OrderInfoMingtou的computed里的,
  357. * 而且作为props传给本组件。考虑用另外一个变量存,就是finalPrice和cutOrderMoney的差值。
  358. * 只有cutOrderMoney不为0时才用到,不然还用this.finalPrice。
  359. * 不过这样明显也改变了finalPrice的定义了,即,finalPrice它不再是真正的final。
  360. */
  361. this.real_finalPrice = this.original_finalPrice - this.cutOrderMoney
  362. },
  363. reduceTickets (tickets, cutOrderMoney) {
  364. let result = []
  365. for (let ticket of tickets) {
  366. if (cutOrderMoney > 0) {
  367. // 整单打折没减完
  368. if (ticket.price >= cutOrderMoney) {
  369. // 该票价足够剩下的整单打折
  370. ticket.price -= cutOrderMoney
  371. cutOrderMoney = 0
  372. } else {
  373. cutOrderMoney -= ticket.price // 注意先后顺序
  374. ticket.price = 0
  375. }
  376. }
  377. ticket.discountPrice = ticket.price
  378. result.push(ticket)
  379. }
  380. return result
  381. },
  382. setParams () {
  383. console.log('setParams被调用')
  384. // 之前是item.discountPrice为准,现在改成用item.price算
  385. let tickets = JSON.parse(JSON.stringify(this.tickets))
  386. let params = Object.assign({}, this.formData)
  387. // 一码多人
  388. if (params.checkWay - 0 === 2) {
  389. let allTickets = []
  390. // 这里的this.orderInfo来源于confirmMixinMingtouGroup.js里的 `this.orderInfo = JSON.parse(JSON.stringify(this.orderItems))`
  391. // 即,this.orderInfo是在show时对this.orderItems的深拷贝
  392. // this.orderInfo是基于票种的数组,每个票种里面再有各自的tickets
  393. // 而this.tickets是拍平了的
  394. this.orderInfo.forEach((item, index) => {
  395. let count = item.count
  396. let ticketOrderTravellers = item.tickets.map(i => {
  397. return {
  398. name: i.guestName,
  399. idType: i.guestIdentifyType,
  400. idNum: i.guestIdentify,
  401. mobile: i.guestPhone
  402. }
  403. })
  404. console.log('item.tickets:', item.tickets)
  405. /* 要注意item.tickets这个和this.tickets可不一样,这个item是this.orderInfo的元素 */
  406. console.log('setParams(item.tickets)初值:' + item.tickets.map(item => item.price).join(' '))
  407. console.log('setParams(this.tickets)初值:' + this.tickets.map(item => item.price).join(' '))
  408. let outerItem = item
  409. /**
  410. * 注意:下面的几处if代码段里重新定义了item,重新定义后的item是和tickets数组元素同类型的,其本身没有tickets属性。
  411. * 所以这里为了之后能访问到最初的item,需要用outerItem保存下。
  412. */
  413. // 注意,有可能出现某个票种钱被打折完了,而出现全0,若去找price为0的票就找不到了,然后obj为空,付款就在那空转
  414. let obj = JSON.parse(JSON.stringify(item.tickets[0]))
  415. obj.ticketOrderTravellers = ticketOrderTravellers
  416. let originItem = JSON.stringify(obj)
  417. if (!this.maxNum) {
  418. let item = JSON.parse(originItem)
  419. item.checkNum = count
  420. /**
  421. * 加入整单打折后,老实用加法
  422. * item.price = this.$NP.times(item.checkNum, item.discountPrice)
  423. */
  424. item.price = outerItem.tickets.reduce((accumulator, ticket) => this.$NP.plus(accumulator, ticket.price), 0)
  425. allTickets.push(item)
  426. return
  427. }
  428. if (count > this.maxNum) {
  429. let num = Math.ceil(count / this.maxNum)
  430. for (let i = 0; i < num - 1; i++) {
  431. let newItem = JSON.parse(originItem)
  432. newItem.checkNum = this.maxNum
  433. const subtotal = outerItem.tickets
  434. .slice(i * this.maxNum, (i + 1) * this.maxNum)
  435. .reduce((sum, ticket) => sum + ticket.price, 0)
  436. /* newItem.price = this.$NP.times(this.maxNum, newItem.discountPrice) */
  437. newItem.price = subtotal
  438. allTickets.push(newItem)
  439. }
  440. let lastItem = JSON.parse(originItem)
  441. // 处理最后一张票,这张最后的票没达到单票容纳人的上限
  442. if (count % this.maxNum === 0) {
  443. lastItem.checkNum = this.maxNum
  444. } else {
  445. lastItem.checkNum = count % this.maxNum
  446. }
  447. /* lastItem.price = this.$NP.times(lastItem.checkNum, lastItem.discountPrice) */
  448. // 由于整单打折,含票价为0的元素,不再能用乘法。
  449. // outerItem.tickets里面除了之前的元素,要把剩下的元素的price加起来赋给lastItem.price。
  450. // lastItem算价格时从之前的位置,一直切片到最后
  451. lastItem.price = outerItem.tickets
  452. .slice((num - 1) * this.maxNum, outerItem.tickets.length)
  453. .reduce((sum, ticket) => sum + ticket.price, 0)
  454. allTickets.push(lastItem)
  455. console.log('一码多人时的allTickets: ', allTickets)
  456. } else {
  457. /**
  458. * 注意,之前的item和下面重新定义的item并不相同。
  459. * 之前的item是有tickets属性的,而下面重新定义并覆盖了之前的item。
  460. * 要访问之前有tickets属性的item,需要通过outerItem来访问。
  461. */
  462. let item = JSON.parse(originItem)
  463. item.checkNum = count
  464. /* item.price = this.$NP.times(item.checkNum, item.discountPrice) */
  465. item.price = outerItem.tickets.reduce((accumulator, ticket) => this.$NP.plus(accumulator, ticket.price), 0)
  466. allTickets.push(item)
  467. }
  468. })
  469. params.tickets = allTickets
  470. } else {
  471. params.tickets = tickets.map(item => {
  472. /* item.price = item.discountPrice */
  473. item.price = item.price + 0 // 目前discountPrice是没有使用的
  474. return item
  475. })
  476. }
  477. params.payChannel = this.currentPayChannel
  478. /* params.price = this.finalPrice */
  479. // finalPrice不再是真正的最终价格,不过由于它是props也不好改变它,转而用变量real_finalPrice
  480. params.price = (this.cutOrderMoney === 0) ? this.finalPrice : this.real_finalPrice
  481. params.sellDevice = this.sellerDevice && this.sellerDevice.ip4
  482. console.log('setParams弄完之后的params:', params)
  483. this.params = params
  484. },
  485. createSuccessCallback () {
  486. // 更新旅行社余额
  487. this.currentPayChannel === '旅行社余额' && (this.$store.dispatch('getAgencyList'))
  488. }
  489. }
  490. }
  491. </script>
  492. <style lang="scss" scoped>
  493. .btn-wrap {
  494. padding-top: 20px; text-align: center;
  495. }
  496. .info-wrap {
  497. padding: 20px 20px;
  498. ::v-deep .el-form-item__label{
  499. font-size: 16px;
  500. }
  501. ::v-deep .el-form-item{
  502. display: inline-block;
  503. }
  504. .strong {
  505. font-size: 1.8em; font-weight: bold;
  506. }
  507. }
  508. </style>
  509. <style lang="scss" scoped>
  510. .btn-wrap {
  511. display: flex; width: 100%; margin-bottom: 10px; font-size: 14px; justify-content: space-between; align-items: center;
  512. .el-input {
  513. display: inline-block; width: 100px;
  514. }
  515. }
  516. </style>