index.vue 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557
  1. <script setup lang="ts">
  2. import { getWxCommonPayment, handleCommonPayMent, paySuccess } from '../utils/confirm-order'
  3. import { getArrayFieldMax, isWithin45Minutes, timeFormat } from '../utils/index'
  4. import router from '@/router'
  5. import { StaticUrl } from '@/config'
  6. definePage({
  7. name: 'film-submit-order',
  8. islogin: false,
  9. style: {
  10. navigationBarTitleText: '确认订单',
  11. backgroundColorBottom: '#fff',
  12. },
  13. })
  14. // const isDifferent = computed(() => {
  15. // return info.choose
  16. // })
  17. const showProtocol = ref(true)
  18. const isAgree = ref(false)
  19. const loading = ref(false)
  20. const { userInfo } = storeToRefs(useUserStore())
  21. const query = ref({
  22. memberId: unref(userInfo).id,
  23. channelId: unref(userInfo).channelId,
  24. shopId: '1',
  25. channelName: unref(userInfo).channelName,
  26. cinemaName: '',
  27. cinemaCode: '',
  28. movieCode: '',
  29. hallName: '',
  30. orderPayMode: '1',
  31. originPrice: 0,
  32. seatNames: '',
  33. sessionBeginTime: '',
  34. switchSeat: false,
  35. movieOrderItems: [] as Array<{ name: string, price: number }>,
  36. session: '',
  37. postImageUrl: '',
  38. movieName: '',
  39. fastTicket: false,
  40. })
  41. const info = ref({
  42. cinemaName: '',
  43. totalPrice: 0,
  44. phone: '',
  45. points: 0,
  46. language: '',
  47. planType: '',
  48. showTime: '',
  49. hallName: '',
  50. chooseSeatList: [] as Array<{ seatName: string, ticketPrice: number, fastPrice: number, areaId: string }>,
  51. })
  52. const isDiffrent = computed(() => {
  53. let flag = false
  54. const areaId = info.value.chooseSeatList[0].areaId
  55. for (const item of info.value.chooseSeatList) {
  56. if (item.areaId !== areaId) {
  57. flag = true
  58. break
  59. }
  60. }
  61. return flag
  62. })
  63. const totalPrice = computed(() => {
  64. let total = 0
  65. const scale = 10000
  66. if (!info.value.showTime)
  67. return 0
  68. const key = isWithin45Minutes(new Date(), info.value.showTime) ? 'fastPrice' : 'ticketPrice'
  69. const price = getArrayFieldMax(info.value.chooseSeatList, key) || 0
  70. total = (price * scale) * info.value.chooseSeatList.length / scale
  71. return total
  72. })
  73. function back() {
  74. showProtocol.value = false
  75. router.back()
  76. }
  77. function next() {
  78. if (!isAgree.value) {
  79. return useGlobalToast().show('请先勾选协议')
  80. }
  81. showProtocol.value = false
  82. }
  83. async function pay() {
  84. loading.value = true
  85. console.log(info.value.chooseSeatList)
  86. query.value.fastTicket = isWithin45Minutes(new Date(), info.value.showTime)
  87. query.value.movieOrderItems = info.value.chooseSeatList.map((item: any) => {
  88. if (isWithin45Minutes(new Date(), info.value.showTime)) { // 如果在45分钟内,快速出票
  89. return {
  90. name: item.seatName,
  91. price: item.fastPrice,
  92. }
  93. }
  94. else {
  95. return {
  96. name: item.seatName,
  97. price: item.ticketPrice,
  98. }
  99. }
  100. })
  101. const { data: orderNumber } = await Apis.film.addFilmOrder({ data: query.value })
  102. const res = await handleCommonPayMent(orderNumber)
  103. if (res.payType !== 1) {
  104. await getWxCommonPayment(res)
  105. await paySuccess()
  106. }
  107. else {
  108. await paySuccess()
  109. }
  110. loading.value = false
  111. }
  112. function call() {
  113. uni.makePhoneCall({
  114. phoneNumber: info.value.phone,
  115. })
  116. }
  117. async function getPoints() {
  118. const res = await Apis.xsb.findUserPoints({})
  119. info.value.points = res.data.availablePointsTotal as number
  120. }
  121. getPoints()
  122. function usePoints() {
  123. return (totalPrice.value * 100) >= info.value.points ? info.value.points : ((totalPrice.value * 10000) / 100)
  124. }
  125. onLoad((options) => {
  126. query.value = JSON.parse(options?.query)
  127. info.value = uni.getStorageSync('film-info')
  128. console.log(11111, info.value.chooseSeatList)
  129. })
  130. </script>
  131. <template>
  132. <view class="film-submit-order">
  133. <!-- 影片信息 -->
  134. <view class="movie-info-box block">
  135. <view class="movie-info">
  136. <image class="img" :src="query.postImageUrl" />
  137. <view class="title-box">
  138. <view class="title">
  139. {{ query.movieName }}
  140. </view>
  141. <view class="time-box">
  142. {{ timeFormat(info.showTime) }} {{ info.language }} {{ info.planType }}
  143. </view>
  144. </view>
  145. </view>
  146. <view class="phone-box">
  147. <view class="notice-box">
  148. <image class="icon" :src="`${StaticUrl}/film-error.png`" mode="scaleToFill" />
  149. <view class="text">
  150. 不支持线上改签、退款,具体请咨询商家。
  151. </view>
  152. </view>
  153. <image class="phone" :src="`${StaticUrl}/film-phone.png`" mode="scaleToFill" @click="call" />
  154. </view>
  155. </view>
  156. <!-- 座位信息 -->
  157. <view class="seat-info block">
  158. <view class="sub-title">
  159. {{ info.cinemaName }}
  160. </view>
  161. <view class="room-num">
  162. {{ query.hallName }}
  163. </view>
  164. <!-- <view class="area-price">
  165. 普通区¥29.9
  166. </view> -->
  167. <view class="seat-list">
  168. <view v-for="(item, index) in info.chooseSeatList" :key="index" class="item">
  169. <view class="label">
  170. {{ item.seatName }}
  171. </view>
  172. <view class="value">
  173. ¥{{ isWithin45Minutes(new Date(), info.showTime) ? item.fastPrice : item.ticketPrice }}
  174. </view>
  175. </view>
  176. </view>
  177. <!--
  178. <view class="area-price">
  179. 贵宾区¥39.9
  180. </view>
  181. <view class="seat-list">
  182. <view class="item">
  183. <view class="label">
  184. 2排11座
  185. </view>
  186. <view class="value">
  187. ¥29.9
  188. </view>
  189. </view>
  190. <view class="item">
  191. <view class="label">
  192. 2排11座
  193. </view>
  194. <view class="value">
  195. ¥29.9
  196. </view>
  197. </view>
  198. </view> -->
  199. </view>
  200. <!-- 价格信息 -->
  201. <view class="goods-price-box block">
  202. <view class="item">
  203. <view class="label">
  204. 商品金额
  205. </view>
  206. <view class="value">
  207. ¥{{ totalPrice || info.totalPrice }}
  208. </view>
  209. </view>
  210. <view v-if="isDiffrent" class="notice">
  211. 座位跨区,按最高价格计算。订单总价=最高座位价格×座位数
  212. </view>
  213. <view class="item">
  214. <view class="label">
  215. 积分({{ usePoints() }})
  216. </view>
  217. <view class="value price">
  218. -¥{{ usePoints() / 100 }}
  219. </view>
  220. </view>
  221. <!-- <view class="item">
  222. <view class="label">
  223. 平台券
  224. </view>
  225. <view class="value price">
  226. -¥14
  227. </view>
  228. </view> -->
  229. <view class="line" />
  230. <view class="total-box">
  231. <view class="text">
  232. 总计
  233. </view>
  234. <view class="total">
  235. ¥{{ (totalPrice * 100 - usePoints()) / 100 }}
  236. </view>
  237. </view>
  238. </view>
  239. <!-- 购票须知 -->
  240. <view class="notice-box block">
  241. <view class="sub-title">
  242. 购票须知
  243. </view>
  244. <view class="content-text">
  245. 1.请提前30分钟左右到达影院现场,通过影院自助取票机完成
  246. 取票。
  247. 2.若取票过程中遇到无法取票等其它问题,请联系影院工作人
  248. 员进行处理。
  249. 3.请及时关注电影开场时间,凭票有序检票入场。
  250. 4.如需开具电影票发票,可联系影院工作人员凭当日票根进行
  251. 开具,若遇到特殊情况请及时联系猫眼客服人员。
  252. 5.退票、改签服务请参考影院具体政策要求,特殊场次及部分
  253. 使用卡、券场次订单可能不支持此服务。
  254. 6.由于设备故障等不可抗力因素,存在少量影院调整/取消场次
  255. 的情况,系统将会自动退票退款。
  256. 7.由于影院系统不稳定等因素,存在少量出票失败的情况系统
  257. 将会自动退款请注意查收。
  258. 8.如有其他问题可联系在线客服,工作时间:9:00-22:00。
  259. </view>
  260. </view>
  261. <!-- 底部 -->
  262. <view class="footer-box">
  263. <view class="price-box">
  264. <view class="total">
  265. ¥{{ (totalPrice * 100 - usePoints()) / 100 }}
  266. </view>
  267. <view class="reduce">
  268. 共减¥{{ usePoints() / 100 }}
  269. </view>
  270. </view>
  271. <wd-button
  272. custom-style="width: 180rpx;height: 80rpx;background: #9ED605;border-radius: 40rpx 40rpx 40rpx 40rpx;" :loading="loading" @click="pay"
  273. >
  274. 立即支付
  275. </wd-button>
  276. </view>
  277. <!-- 协议弹窗 -->
  278. <wd-popup v-model="showProtocol" :close-on-click-modal="false" custom-style="border-radius:16rpx;">
  279. <view class="popup-box">
  280. <view class="title">
  281. 退改签协议
  282. </view>
  283. <view class="content-text">
  284. 用户点击同意本协议之前,请务必认真阅读完全理解本协议中
  285. 全部条款,特别是其中与用户权益有或可能具有重大关系的条
  286. 款(包括但不限于第1.2条、第1.3条、第2条、第3.2条)。当用
  287. 户按照页面提示阅读、点击确认同意本协议及完成支付购票时,
  288. 即表示用户已经充分阅读、理解并接受本协议的全部内容。如
  289. 用户不同意接受本协议的任何条款,或无法理解本协议相关条
  290. 款含义的,请不要进行后续操用户点击同意本协议之前,请务
  291. 必认真阅读完全理解木协议中全部条款 特别是其中与用户权
  292. 益有或可能,用户点击同意本协议之前,请务必认真阅读
  293. </view>
  294. <view class="radio-box">
  295. <wd-checkbox v-model="isAgree">
  296. 我已阅读并同意以上协议
  297. </wd-checkbox>
  298. </view>
  299. <view class="btn-box">
  300. <wd-button custom-class="btn" @click="next">
  301. 继续购票
  302. </wd-button>
  303. <wd-button custom-class="btn" type="text" @click="back">
  304. 暂不购票
  305. </wd-button>
  306. </view>
  307. </view>
  308. </wd-popup>
  309. </view>
  310. </template>
  311. <style lang="scss" scoped>
  312. .film-submit-order{
  313. padding: 20rpx 24rpx 300rpx;
  314. background: #F9F9F9;
  315. .block {
  316. background: #FFFFFF;
  317. border-radius: 16rpx 16rpx 16rpx 16rpx;
  318. padding: 24rpx;
  319. margin-bottom: 20rpx;
  320. }
  321. .sub-title {
  322. font-weight: bold;
  323. font-size: 32rpx;
  324. color: #222222;
  325. }
  326. .item {
  327. display: flex;
  328. justify-content: space-between;
  329. align-items: center;
  330. margin-top: 20rpx;
  331. .label {
  332. font-size: 28rpx;
  333. color: #222222;
  334. }
  335. .label.gray {
  336. color: #AAAAAA;
  337. }
  338. .value {
  339. font-size: 28rpx;
  340. color: #222222;
  341. }
  342. .value.price {
  343. color: #FF4A39;
  344. }
  345. }
  346. .item:first-child {
  347. margin-top: 0 !important;
  348. }
  349. .movie-info {
  350. display: flex;
  351. align-items: center;
  352. .img {
  353. width: 152rpx;
  354. height: 208rpx;
  355. margin-right: 24rpx;
  356. border-radius: 16rpx;
  357. vertical-align: bottom;
  358. }
  359. .title-box {
  360. display: flex;
  361. flex-direction: column;
  362. justify-content: center;
  363. .title {
  364. font-weight: bold;
  365. font-size: 32rpx;
  366. color: #222222;
  367. margin-bottom: 20rpx;
  368. }
  369. .time-box {
  370. font-size: 24rpx;
  371. color: #AAAAAA;
  372. }
  373. }
  374. }
  375. .phone-box{
  376. display: flex;
  377. justify-content: space-between;
  378. align-items: center;
  379. margin-top: 24rpx;
  380. .notice-box{
  381. display: flex;
  382. align-items: center;
  383. .icon{
  384. width: 32rpx;
  385. height: 32rpx;
  386. margin-right: 16rpx;
  387. }
  388. .text{
  389. font-size: 24rpx;
  390. color: #222222;
  391. }
  392. }
  393. .phone{
  394. width: 40rpx;
  395. height: 40rpx;
  396. }
  397. }
  398. .seat-info {
  399. .room-num {
  400. font-size: 28rpx;
  401. color: #222222;
  402. margin: 20rpx 0;
  403. }
  404. .area-price {
  405. font-size: 24rpx;
  406. color: #AAAAAA;
  407. margin: 20rpx 0;
  408. }
  409. .seat-list {}
  410. }
  411. .goods-price-box {
  412. .notice {
  413. font-size: 24rpx;
  414. color: #AAAAAA;
  415. margin-top: 16rpx;
  416. }
  417. .line {
  418. height: 2rpx;
  419. background: #F0F0F0;
  420. margin-top: 20rpx;
  421. }
  422. .total-box {
  423. display: flex;
  424. justify-content: space-between;
  425. align-items: center;
  426. margin-top: 30rpx;
  427. .text {
  428. font-weight: bold;
  429. font-size: 32rpx;
  430. color: #222222;
  431. }
  432. .total {
  433. font-weight: bold;
  434. font-size: 32rpx;
  435. color: #FF4A39;
  436. }
  437. }
  438. }
  439. .notice-box{
  440. .content-text{
  441. font-size: 24rpx;
  442. color: #222222;
  443. line-height: 42rpx;
  444. margin-top: 24rpx;
  445. }
  446. }
  447. .footer-box{
  448. position: fixed;
  449. bottom: 0;
  450. left: 0;
  451. box-sizing: border-box;
  452. padding: 12rpx 24rpx 76rpx;
  453. background: #fff;
  454. width: 100%;
  455. display: flex;
  456. justify-content: space-between;
  457. align-items: center;
  458. border-top: 1rpx solid #EEEEEE;
  459. .price-box{
  460. display: flex;
  461. align-items: center;
  462. .total{
  463. font-weight: bold;
  464. font-size: 40rpx;
  465. color: #FF4A39;
  466. }
  467. .reduce{
  468. font-size: 24rpx;
  469. color: #FF4A39;
  470. margin-left: 20rpx;
  471. }
  472. }
  473. }
  474. }
  475. .btn-box::v-deep .btn {
  476. width: 654rpx;
  477. height: 80rpx;
  478. margin-top: 24rpx;
  479. }
  480. .popup-box{
  481. width: 702rpx;
  482. height: 826rpx;
  483. background: #FFFFFF;
  484. border-radius: 16rpx 16rpx 16rpx 16rpx;
  485. padding: 28rpx 24rpx;
  486. box-sizing: border-box;
  487. .title{
  488. font-weight: bold;
  489. font-size: 28rpx;
  490. color: #222222;
  491. text-align: center;
  492. }
  493. .content-text{
  494. height: 500rpx;
  495. overflow-y: auto;
  496. font-size: 24rpx;
  497. color: #222222;
  498. line-height: 42rpx;
  499. margin-top: 24rpx;
  500. }
  501. .radio-box{
  502. margin-top: 24rpx;
  503. display: flex;
  504. align-items: center;
  505. justify-content: center;
  506. }
  507. .btn-box{
  508. display: flex;
  509. flex-direction: column;
  510. }
  511. }
  512. </style>