makeOut.vue 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633
  1. <template>
  2. <view class="hotel-makeOut">
  3. <view class="content">
  4. <view class="info-box">
  5. <zs-img class="img" :src="form.roomPic" width="200rpx" height="200rpx" radius="full"></zs-img>
  6. <view class="info">
  7. <view class="title">
  8. {{form.roomNameCn}}
  9. </view>
  10. <view class="desc-box">
  11. <view class="text" v-if="info.area">
  12. {{info.area}}
  13. </view>
  14. <view class="text" v-if="info.window">
  15. {{info.window |filterWindow}}
  16. </view>
  17. </view>
  18. <view class="notice">
  19. {{info.meal}} {{info.cancel}}
  20. </view>
  21. </view>
  22. </view>
  23. <view class="bottom-box">
  24. <view class="time-box">
  25. <view class="item">
  26. <view class="label">
  27. 入住
  28. </view>
  29. <view class="time">
  30. {{form.checkin | filterDate('time')}}
  31. </view>
  32. </view>
  33. <view class="item">
  34. <view class="label">
  35. 离店
  36. </view>
  37. <view class="time">
  38. {{form.checkout | filterDate('time')}}
  39. </view>
  40. </view>
  41. </view>
  42. <view class="day">
  43. {{day}}晚
  44. </view>
  45. </view>
  46. </view>
  47. <view class="content room-box">
  48. <view class="sub-title">
  49. 房间数
  50. </view>
  51. <view class="desc">
  52. 1间房 每间房2 成人 0儿童
  53. </view>
  54. <view class="tab-box">
  55. <view class="tab" :class="[d+1 == form.numberofRooms?'active':'',d%4?'ml20':'']" v-for="(i,d) in 8" :key="d" @click="choose(d)">
  56. {{i+1}}间
  57. </view>
  58. </view>
  59. </view>
  60. <view class="content add-box">
  61. <view class="sub-title">
  62. 入住信息
  63. </view>
  64. <u--form
  65. labelPosition="left"
  66. :model="form"
  67. ref="uForm"
  68. labelWidth="150rpx"
  69. >
  70. <view v-for="(item,index) in form.customerInfo" :key="index">
  71. <view class="title-box">
  72. <view class="title">
  73. 房间{{index+1}}
  74. </view>
  75. </view>
  76. <u-form-item
  77. label="姓名"
  78. :prop="`customerInfo.${index}.value`"
  79. :ref="'customerInfo.'+index+'.value'"
  80. borderBottom
  81. required
  82. >
  83. <u--input
  84. v-model="item.value"
  85. border="none"
  86. ></u--input>
  87. </u-form-item>
  88. </view>
  89. <u-form-item
  90. label="联系手机"
  91. prop="contactPhone"
  92. borderBottom
  93. ref="item1"
  94. required
  95. >
  96. <u--input
  97. v-model="form.contactPhone"
  98. border="none"
  99. :maxlength="11"
  100. ></u--input>
  101. </u-form-item>
  102. </u--form>
  103. </view>
  104. <view class="content ticket-box">
  105. <view class="sub-title">
  106. 发票信息
  107. </view>
  108. <view class="box" @click="handleCall">
  109. <view class="ticket-title">
  110. 不需要发票
  111. </view>
  112. <view class="ticket-desc">
  113. 需要发票可以 联系 400-797-5000 客服开票
  114. </view>
  115. <image class="icon" src="../static/right.png" mode=""></image>
  116. </view>
  117. </view>
  118. <view class="btn-box">
  119. <view class="left">
  120. <view class="num">
  121. 共{{form.numberofRooms}}件
  122. </view>
  123. <view class="total">
  124. 合计 <view class="price-box">
  125. <view class="unit">
  126. </view>
  127. <view class="price">
  128. {{form.totalPrice}}
  129. </view>
  130. </view>
  131. </view>
  132. </view>
  133. <button class="btn" @click="handleBuy" :loading="loading">
  134. 提交订单
  135. </button>
  136. </view>
  137. </view>
  138. </template>
  139. <script>
  140. import {addOrder} from '@/api/hotel.js'
  141. import {
  142. creatPayOrder,
  143. queryPayOrder,
  144. } from '@/api/payment.js'
  145. export default {
  146. data() {
  147. return {
  148. tab: 0,
  149. day:1,
  150. form:{
  151. "roomNameCn":'',
  152. "hotelIdPic":'',
  153. "roomPic":'',
  154. "channel":'ZhongShu',
  155. "checkin": "",
  156. "checkout": "",
  157. "childAges": [
  158. 0
  159. ],
  160. "childNum": 0,
  161. "contactEmail": "",
  162. "contactName": "",
  163. "contactPhone": "",
  164. "customerArriveTime": "",
  165. "customerInfo": [
  166. {
  167. value:''
  168. }
  169. ],
  170. "hotelId": 0,
  171. "manNum": 0,
  172. "numberofRooms": 1,
  173. "ratePlanId": "",
  174. "specialRemarks": "",
  175. "totalPrice": 0
  176. },
  177. rules:{
  178. contactPhone:[
  179. {
  180. required: true,
  181. message: '请输入手机号',
  182. trigger: ['blur', 'change']
  183. },
  184. {
  185. // 自定义验证函数,见上说明
  186. validator: (rule, value, callback) => {
  187. // 上面有说,返回true表示校验通过,返回false表示不通过
  188. // uni.$u.test.mobile()就是返回true或者false的
  189. return uni.$u.test.mobile(value);
  190. },
  191. message: '手机号码不正确',
  192. // 触发器可以同时用blur和change
  193. trigger: ['change','blur'],
  194. }
  195. ],
  196. customerInfo:[]
  197. },
  198. loading:false,
  199. query: {
  200. "msgType": "wx.unifiedOrder",
  201. "orderDesc": "",
  202. "orderNo": "",
  203. "channel":'ZhongShu',
  204. "subOpenId": "",
  205. "userId": ""
  206. },
  207. averagePrice:0,
  208. info:{
  209. roomNameCn:'',
  210. area:'',
  211. window:'',
  212. meal:'',
  213. cancel:''
  214. }
  215. }
  216. },
  217. computed: {
  218. price() {
  219. this.form.totalPrice = this.averagePrice*this.form.numberofRooms
  220. return ''
  221. }
  222. },
  223. filters: {
  224. filterDate: function(value,type) {
  225. let timestamp = new Date(value).getTime()
  226. if(type == 'time'){
  227. return uni.$u.timeFormat(timestamp, 'mm月dd日');
  228. }else if(type == 'day'){
  229. let arr = ['日','一','二','三','四','五','六']
  230. let num = new Date(timestamp).getDay()
  231. return `周${arr[num]}`
  232. }
  233. },
  234. filterWindow:function(val){
  235. if(val === 0){
  236. return '无窗'
  237. }else if(val === 1){
  238. return '部分有窗'
  239. }else if(val === 2){
  240. return '有窗'
  241. }else if(val === 3){
  242. return '不确定'
  243. }else if(val === 4){
  244. return '内窗'
  245. }else if(val === 5){
  246. return '天窗'
  247. }else if(val === 6){
  248. return '封闭窗'
  249. }else if(val === 7){
  250. return '飘窗'
  251. }
  252. },
  253. },
  254. methods: {
  255. handleCall() {
  256. uni.makePhoneCall({
  257. phoneNumber: '400-797-5000' //仅为示例
  258. });
  259. },
  260. setRules() {
  261. this.form.customerInfo.forEach((ele, index) => {
  262. this.$set(this.rules, `customerInfo.${index}.value`, [
  263. {
  264. required: true,
  265. message: '请填写姓名',
  266. trigger: ['blur', 'change']
  267. },
  268. ])
  269. })
  270. this.$nextTick(() => {
  271. this.$refs.uForm.setRules(this.rules)
  272. })
  273. },
  274. choose(val){
  275. this.form.numberofRooms = val+1
  276. this.form.manNum = val+1
  277. if(this.form.customerInfo.length<val+1){
  278. let num = val+1 - this.form.customerInfo.length
  279. for (let index = 0; index < num; index++) {
  280. this.form.customerInfo.push({
  281. value:''
  282. })
  283. }
  284. }else if(this.form.customerInfo.length>val+1){
  285. this.form.customerInfo.length = val+1
  286. }
  287. this.$nextTick(()=>{
  288. this.setRules()
  289. })
  290. console.log('this.form.customerInfo',this.form.customerInfo,this.form.numberofRooms);
  291. },
  292. addOrder(query){
  293. if (this.loading) return
  294. this.loading = true
  295. uni.showLoading({
  296. title: '支付中'
  297. })
  298. let that = this
  299. addOrder(query).then(res=>{
  300. if(res.state == 'Success'){
  301. this.query.orderNo = res.content.orderNo
  302. this.query.subOpenId = JSON.parse(uni.getStorageSync('userInfo')).openId
  303. this.query.orderDesc = '测试商品名'
  304. creatPayOrder(this.query).then(data => {
  305. that.payData = JSON.parse(data.content.miniPayRequest)
  306. if (data.content.miniPayRequest == null) return uni.hideLoading()
  307. console.log(666,that.payData);
  308. uni.requestPayment({
  309. "provider": "wxpay",
  310. "orderInfo": that.payData,
  311. "appid": that.payData
  312. .appId, // 微信开放平台 - 应用 - AppId,注意和微信小程序、公众号 AppId 可能不一致
  313. "paySign": that.payData.paySign,
  314. "nonceStr": that.payData.nonceStr, // 随机字符串
  315. "package": that.payData.package, // 固定值
  316. // "prepayid": that.payData.package, // 统一下单订单号
  317. "timeStamp": that.payData.timeStamp, // 时间戳(单位:秒)
  318. "signType": that.payData.signType, //签名算法
  319. success(msg) {
  320. console.log('msg', that.query.orderNo);
  321. queryPayOrder(that.query.orderNo).then(res1 => {
  322. that.loading = false
  323. uni.hideLoading()
  324. if (res1.state == 'Success') {
  325. uni.reLaunch({
  326. url: '/my/order/index'
  327. })
  328. }
  329. })
  330. },
  331. fail(e) {
  332. console.log('err', e);
  333. that.loading = false
  334. uni.hideLoading()
  335. uni.showToast({
  336. title: '取消支付',
  337. icon: 'fail'
  338. })
  339. // 取消支付后
  340. uni.reLaunch({
  341. url:'/my/order/hotel/detail?id='+that.query.orderNo
  342. })
  343. }
  344. })
  345. })
  346. }
  347. })
  348. },
  349. handleBuy() {
  350. this.$refs.uForm.validate().then(res => {
  351. let form = JSON.parse(JSON.stringify(this.form))
  352. let list = []
  353. form.customerInfo.map(item=>{
  354. list.push(item.value)
  355. })
  356. form.customerInfo = list
  357. form.contactName = form.customerInfo[0]
  358. form.customerArriveTime = uni.$u.timeFormat(new Date(form.checkin).getTime()+1000*60*60*24 , 'yyyy-mm-dd')+' 17:00:00'
  359. this.addOrder(form)
  360. })
  361. }
  362. },
  363. onReady() {
  364. // this.$refs.uForm.setRules(this.rules)
  365. this.setRules()
  366. },
  367. onLoad(options) {
  368. this.form.checkin = options.checkInDate
  369. this.form.checkout = options.checkOutDate
  370. this.day = options.day
  371. let that = this
  372. const eventChannel = this.getOpenerEventChannel();
  373. eventChannel.on('pay', function(data) {
  374. let {manNum,childNum,hotelId,ratePlanId,averagePrice,roomPic,hotelPic,roomNameCn,area,window,meal,cancel} = data
  375. that.form.manNum = manNum
  376. that.form.childNum = childNum
  377. that.form.hotelId = hotelId
  378. that.form.ratePlanId = ratePlanId
  379. that.averagePrice = averagePrice
  380. that.form.hotelIdPic = hotelPic
  381. that.form.roomPic = roomPic
  382. that.form.roomNameCn = roomNameCn
  383. that.info = {
  384. area,
  385. window,
  386. meal,
  387. cancel
  388. }
  389. console.log('数据:',data);
  390. })
  391. }
  392. }
  393. </script>
  394. <style lang="scss" scoped>
  395. .hotel-makeOut{
  396. background: #F9F9F9;
  397. border-radius: 0rpx 0rpx 0rpx 0rpx;
  398. padding: 20rpx 24rpx;
  399. .content{
  400. padding: 24rpx;
  401. background: #fff;
  402. border-radius: 16rpx 16rpx 16rpx 16rpx;
  403. margin-bottom: 20rpx;
  404. .sub-title{
  405. font-weight: 600;
  406. font-size: 28rpx;
  407. color: #222222;
  408. padding-bottom: 26rpx;
  409. border-bottom: 1rpx solid #F0F0F0;
  410. }
  411. }
  412. .info-box{
  413. display: flex;
  414. .info{
  415. margin-left: 20rpx;
  416. flex: 1;
  417. .title{
  418. font-weight: 600;
  419. font-size: 36rpx;
  420. color: #222222;
  421. width: 450rpx;
  422. white-space: nowrap;
  423. overflow: hidden;
  424. text-overflow: ellipsis;
  425. margin-top: 20rpx;
  426. }
  427. .desc-box{
  428. display: flex;
  429. align-items: center;
  430. font-size: 24rpx;
  431. color: #AAAAAA;
  432. margin-top: 20rpx;
  433. .text{
  434. padding-right: 10rpx;
  435. }
  436. .text+.text{
  437. border-left: 2rpx solid #F0F0F0;
  438. padding-left: 10rpx;
  439. }
  440. }
  441. .notice{
  442. font-size: 24rpx;
  443. color: #AAAAAA;
  444. margin-top: 20rpx;
  445. }
  446. }
  447. }
  448. .bottom-box{
  449. display: flex;
  450. align-items: center;
  451. padding-top: 26rpx;
  452. border-top: 1rpx solid #F0F0F0;
  453. margin-top: 20rpx;
  454. .time-box{
  455. flex: 1;
  456. display: flex;
  457. .item{
  458. display: flex;
  459. align-items: center;
  460. .label{
  461. font-weight: 300;
  462. font-size: 24rpx;
  463. color: #AAAAAA;
  464. }
  465. .time{
  466. font-size: 24rpx;
  467. color: #222222;
  468. margin-left: 20rpx;
  469. }
  470. }
  471. .item+.item{
  472. margin-left: 80rpx;
  473. }
  474. }
  475. .day{
  476. font-size: 24rpx;
  477. color: #222222;
  478. }
  479. }
  480. .room-box{
  481. .desc{
  482. font-weight: 300;
  483. font-size: 24rpx;
  484. color: #AAAAAA;
  485. margin-top: 20rpx;
  486. }
  487. .tab-box{
  488. display: flex;
  489. flex-wrap: wrap;
  490. .ml20{
  491. margin-left: 20rpx;
  492. }
  493. .tab{
  494. width: 148rpx;
  495. height: 52rpx;
  496. line-height: 52rpx;
  497. text-align: center;
  498. background: #F0F0F0;
  499. border-radius: 8rpx 8rpx 8rpx 8rpx;
  500. font-weight: 300;
  501. font-size: 28rpx;
  502. color: #AAAAAA;
  503. margin-top: 20rpx;
  504. }
  505. .tab.active{
  506. background: #E2ECFF;
  507. color: #3879F9;
  508. }
  509. }
  510. }
  511. .add-box{
  512. .title-box{
  513. display: flex;
  514. align-items: center;
  515. justify-content: space-between;
  516. margin-top: 20rpx;
  517. .title{
  518. font-size: 28rpx;
  519. color: #222222;
  520. }
  521. .add-btn{
  522. font-weight: 300;
  523. font-size: 24rpx;
  524. color: #3879F9;
  525. }
  526. }
  527. }
  528. .btn-box {
  529. position: fixed;
  530. bottom: 0%;
  531. left: 0%;
  532. z-index: 2;
  533. width: 100%;
  534. display: flex;
  535. align-items: center;
  536. justify-content: space-between;
  537. box-sizing: border-box;
  538. padding: 10rpx 24rpx env(safe-area-inset-bottom);
  539. background: #fff;
  540. border-top: 1rpx solid #EEEEEE;
  541. .left{
  542. display: flex;
  543. align-items: flex-end;
  544. .num{
  545. font-weight: 300;
  546. font-size: 24rpx;
  547. color: #AAAAAA;
  548. }
  549. .total{
  550. font-weight: 300;
  551. font-size: 24rpx;
  552. color: #222222;
  553. display: flex;
  554. align-items: flex-end;
  555. margin-left: 6rpx;
  556. .price-box{
  557. display: flex;
  558. align-items: flex-end;
  559. font-weight: 400;
  560. font-size: 32rpx;
  561. color: #222222;
  562. .unit{
  563. font-size: 20rpx;
  564. }
  565. }
  566. }
  567. }
  568. .btn {
  569. width: 280rpx;
  570. height: 80rpx;
  571. line-height: 80rpx;
  572. text-align: center;
  573. background: $uni-color-primary;
  574. border-radius: 40rpx;
  575. font-weight: 600;
  576. font-size: 28rpx;
  577. color: #FFFFFF;
  578. margin: 0;
  579. }
  580. }
  581. .ticket-box{
  582. .box{
  583. position: relative;
  584. .ticket-title{
  585. font-weight: 400;
  586. font-size: 24rpx;
  587. color: #222222;
  588. margin-top: 20rpx;
  589. }
  590. .ticket-desc{
  591. font-weight: 300;
  592. font-size: 24rpx;
  593. color: #AAAAAA;
  594. margin-top: 20rpx;
  595. }
  596. .icon{
  597. width: 48rpx;
  598. height: 48rpx;
  599. position: absolute;
  600. right: 0%;
  601. top: 50%;
  602. margin-top: -24rpx;
  603. }
  604. }
  605. }
  606. }
  607. </style>