extend.vue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565
  1. <template>
  2. <view class="checkin-brief-info" v-if="hasCheckinInfo">
  3. <view class="top-background">
  4. <view class="room-info-area">
  5. <view class="room-type-pic">
  6. <image :src="roomTypePicPath"></image>
  7. </view>
  8. <view class="room-info">
  9. <text>{{roomInfo}}</text>
  10. </view>
  11. <view class="room-type-price">
  12. <text>¥{{unitPrice}}</text>
  13. </view>
  14. </view>
  15. </view>
  16. <view class="checkin-date-info">
  17. <view class="date-item">
  18. <view class="date-item-title">
  19. <text>入住</text>
  20. </view>
  21. <view class="date-item-detail">
  22. <text>{{startDate}}</text>
  23. <text>{{startDay}}</text>
  24. </view>
  25. </view>
  26. <view class="date-item">
  27. <view class="date-item-title">
  28. <text>离店</text>
  29. </view>
  30. <view class="date-item-detail">
  31. <text>{{endDate}}</text>
  32. <text>{{endDay}}</text>
  33. </view>
  34. </view>
  35. <view class="checkin-date-other">
  36. <text>{{dayNumText}}</text>
  37. <text>{{breakfastNumText}}</text>
  38. </view>
  39. </view>
  40. <view class="guest-info">
  41. <view class="guest-info-item">
  42. <view class="guest-info-item-label">
  43. <text>住客姓名</text>
  44. </view>
  45. <view class="guest-info-item-content">
  46. <text>{{name}}</text>
  47. </view>
  48. </view>
  49. <view class="guest-info-item">
  50. <view class="guest-info-item-label">
  51. <text>联系电话</text>
  52. </view>
  53. <view class="guest-info-item-content">
  54. <text>{{phone}}</text>
  55. </view>
  56. </view>
  57. <view class="guest-info-item">
  58. <view class="guest-info-item-label">
  59. <text>离店时间</text>
  60. </view>
  61. <view class="guest-info-item-content">
  62. <text>{{endTime}}</text>
  63. </view>
  64. </view>
  65. </view>
  66. <view class="end-time-tip">
  67. <text>{{endTimeTip}}</text>
  68. </view>
  69. <view class="btn-area">
  70. <button v-if="fromExtend" hover-class="btn-hover" @click="showCalendar">续住</button>
  71. <button v-else hover-class="btn-hover" @click="confirmCheckout">退房</button>
  72. </view>
  73. <view class="">
  74. <uni-calendar ref="calendar" :insert="false" :lunar="true" :start-date="calendarStartDate"
  75. :date="calendarDate" @confirm="confirmEndTime"></uni-calendar>
  76. </view>
  77. </view>
  78. <view class="not-checkin" v-else>
  79. <view class="not-checkin-text">
  80. <text>您还未入住!</text>
  81. </view>
  82. <view class="not-checkin-text">
  83. <text>若已退房或房间到期,请至前台办理</text>
  84. </view>
  85. </view>
  86. </template>
  87. <script setup>
  88. import {
  89. computed,
  90. ref
  91. } from 'vue'
  92. import {
  93. onLoad
  94. } from '@dcloudio/uni-app'
  95. import {
  96. useHotelStore
  97. } from '@/store/hotelStore.js'
  98. import {
  99. ROOM_TYPE_PIC_URL
  100. } from '../../config'
  101. import moment from 'moment/moment'
  102. import {
  103. getDayOfWeek
  104. } from '../../utils/dateTimeUtil'
  105. import {
  106. useUserStore
  107. } from '../../store/userStore'
  108. import {
  109. useAppInfoStore
  110. } from '@/store/appInfoStore.js'
  111. let hotel = useHotelStore().hotel
  112. let fromExtend = ref(true)
  113. let hasCheckinInfo = ref(false)
  114. let roomTypePicPath = ref('')
  115. let roomInfo = ref('')
  116. let unitPrice = ref('')
  117. let breakfastNumText = ref('')
  118. let startDay = ref('')
  119. let startDate = ref('')
  120. let endDay = ref('')
  121. let endDate = ref('')
  122. let dayNumText = ref('')
  123. let name = ref('')
  124. let phone = ref('')
  125. let endTime = ref('')
  126. let endTimeTip = computed(() => {
  127. let text = fromExtend.value ? '续住' : '退房'
  128. let checkoutTime = hotel.checkoutTime.split(':')
  129. let checkoutTimeText = checkoutTime[0] + '点'
  130. if (checkoutTime[1] !== '00') {
  131. checkoutTimeText = checkoutTimeText + checkoutTime[1] + '分'
  132. }
  133. return '请在' + checkoutTimeText + '前办理' + text + ',逾期请至前台办理'
  134. })
  135. let orderId = ref('')
  136. let checkinInfoId = ref(0)
  137. let calendarStartDate = ref('')
  138. let calendarDate = ref('')
  139. let originEndDate = ref('')
  140. async function getCheckinInfo() {
  141. let res = await uni.request({
  142. url: `/user/checkinInfo/${hotel.hotelId}`,
  143. method: 'GET'
  144. })
  145. if (res.statusCode === 200 && res.data.success) {
  146. let checkinInfo = res.data.data
  147. if (checkinInfo) {
  148. //设置页面展示信息
  149. roomTypePicPath.value = ROOM_TYPE_PIC_URL + '/' + checkinInfo.roomPicPath
  150. roomInfo.value = checkinInfo.roomTypeName + checkinInfo.room
  151. unitPrice.value = checkinInfo.unitPrice
  152. startDate.value = moment(checkinInfo.startTime).format('MM月DD日')
  153. startDay.value = getDayOfWeek(new Date(checkinInfo.startTime))
  154. endDate.value = moment(checkinInfo.endTime).format('MM月DD日')
  155. endDay.value = getDayOfWeek(new Date(checkinInfo.endTime))
  156. dayNumText.value = '共' + checkinInfo.dayNum + '晚'
  157. breakfastNumText.value = checkinInfo.breakfastNum ? `含${checkinInfo.breakfastNum}早` : '不含早'
  158. name.value = checkinInfo.name
  159. phone.value = checkinInfo.phone
  160. endTime.value = moment(checkinInfo.endTime).format('M月DD日 HH:mm')
  161. orderId.value = checkinInfo.orderId
  162. checkinInfoId.value = checkinInfo.id
  163. //设置日历
  164. originEndDate.value = moment(new Date(checkinInfo.endTime)).format('YYYY-MM-DD')
  165. let date = new Date(checkinInfo.endTime)
  166. date.setDate(date.getDate() + 1)
  167. console.log('date', moment(new Date(date)).format('YYYY-MM-DD'))
  168. calendarStartDate.value = moment(new Date(date)).format('YYYY-MM-DD')
  169. calendarDate.value = moment(new Date(date)).format('YYYY-MM-DD')
  170. hasCheckinInfo.value = true
  171. } else {
  172. hasCheckinInfo.value = false
  173. }
  174. } else {
  175. uni.showToast({
  176. icon: 'none',
  177. title: '查询失败'
  178. })
  179. }
  180. }
  181. let canExtend = ref(false)
  182. async function queryCanExtend() {
  183. let res = await uni.request({
  184. url: `/scm/canExtend/${orderId.value}`,
  185. method: 'GET'
  186. })
  187. if (res.statusCode === 200 && res.data.data === true) {
  188. canExtend.value = true
  189. } else {
  190. canExtend.value = false
  191. }
  192. }
  193. let calendar = ref()
  194. function showCalendar() {
  195. if (!canExtend.value) {
  196. uni.showToast({
  197. icon: 'none',
  198. title: '不满足续住条件,请至前台办理'
  199. })
  200. return
  201. }
  202. calendar.value.open()
  203. }
  204. function confirmEndTime(date) {
  205. //获取日历选择的时间
  206. calendarDate.value = moment(new Date(date.fulldate)).format('YYYY-MM-DD ')
  207. let dayNum = (new Date(date.fulldate).valueOf() - new Date(originEndDate.value).valueOf()) / (24 * 60 * 60 * 1000)
  208. uni.showModal({
  209. title: '确认续住' + dayNum + '天吗?',
  210. success: async (res) => {
  211. if (res.confirm) {
  212. let createRes = await createExtendOrder(dayNum)
  213. if (!createRes.data.success) {
  214. uni.showToast({
  215. icon: 'none',
  216. title: '订单不满足续住条件,请至前台办理'
  217. })
  218. return
  219. }
  220. let payRes = await pay(createRes.data.data.orderId)
  221. if (payRes) {
  222. let extendRes = await extend(createRes.data.data.orderId)
  223. if (extendRes.data.success) {
  224. uni.showToast({
  225. icon: 'none',
  226. title: '续住办理成功!'
  227. })
  228. setTimeout(() => {
  229. uni.switchTab({
  230. url: '/pages/home/home'
  231. })
  232. }, 2000)
  233. } else {
  234. uni.showToast({
  235. icon: 'none',
  236. title: '续住办理失败!'
  237. })
  238. }
  239. }
  240. }
  241. }
  242. })
  243. }
  244. async function createExtendOrder(dayNum) {
  245. return await uni.request({
  246. url: '/scm/extend/orderId',
  247. method: 'POST',
  248. data: {
  249. id: checkinInfoId.value,
  250. dayNum: dayNum
  251. }
  252. })
  253. }
  254. let user = useUserStore()
  255. let appInfo = useAppInfoStore()
  256. async function pay(orderId) {
  257. if (hotel.paymentChannel === "YST") {
  258. return await ystPay(orderId)
  259. } else {
  260. return await nativePay(orderId)
  261. }
  262. }
  263. async function nativePay(orderId) {
  264. let res = await uni.request({
  265. url: '/order/payment/weChat',
  266. method: 'POST',
  267. data: {
  268. openid: user.userInfo.openid,
  269. orderId: orderId,
  270. appId: appInfo.appId
  271. }
  272. })
  273. if (res.statusCode === 200 && res.data.success) {
  274. res.data.data.package = res.data.data.packageStr
  275. let paymentResult = await uni.requestPayment(res.data.data)
  276. console.log("payment result:", paymentResult)
  277. if (paymentResult.errMsg === 'requestPayment:ok') {
  278. uni.showToast({
  279. icon: 'none',
  280. title: '支付成功'
  281. })
  282. return true
  283. }
  284. }
  285. return false
  286. }
  287. async function ystPay(orderId) {
  288. const {
  289. data: submitRes
  290. } = await uni.request({
  291. url: '/order/payment/ystPay',
  292. method: 'POST',
  293. data: {
  294. openid: user.userInfo.openid,
  295. orderId: orderId,
  296. appId: appInfo.appId
  297. }
  298. })
  299. // console.log("订单提交结果", submitRes)
  300. const payData = JSON.parse(submitRes.data)
  301. // console.log("支付数据", payData);
  302. const paymentResult = await uni.requestPayment(payData)
  303. // console.log("payment result:", paymentResult)
  304. // 调用支付成功,查询下订单支付情况
  305. const {
  306. data: paymentRes
  307. } = await uni.request({
  308. url: '/order/payment/queryYstPay',
  309. method: 'POST',
  310. data: {
  311. openid: user.userInfo.openid,
  312. orderId: orderId,
  313. appId: appInfo.appId
  314. }
  315. })
  316. console.log("订单支付结果", paymentRes)
  317. if (paymentRes.success && paymentRes.data === "S") {
  318. uni.showToast({
  319. icon:'none',
  320. title:'支付成功'
  321. })
  322. return true
  323. }
  324. return false
  325. }
  326. async function extend(orderId) {
  327. return await uni.request({
  328. url: `/scm/extend/${orderId}`,
  329. method: 'POST'
  330. })
  331. }
  332. function confirmCheckout() {
  333. uni.showModal({
  334. title: '您确定退房吗?',
  335. success: (res) => {
  336. if (res.confirm) {
  337. checkout()
  338. }
  339. }
  340. })
  341. }
  342. async function checkout() {
  343. let res = await uni.request({
  344. url: `/scm/checkout/${checkinInfoId.value}`,
  345. method: 'POST'
  346. });
  347. if (res.data.success) {
  348. uni.showToast({
  349. icon: 'none',
  350. title: '退房办理成功!'
  351. })
  352. setTimeout(() => {
  353. uni.switchTab({
  354. url: '/pages/home/home'
  355. })
  356. }, 1000)
  357. } else {
  358. uni.showModal({
  359. icon: 'none',
  360. title: '退房失败,请至前台办理'
  361. })
  362. }
  363. }
  364. onLoad(async (options) => {
  365. fromExtend.value = options.fromExtend === 'true'
  366. await getCheckinInfo()
  367. if (hasCheckinInfo.value) {
  368. await queryCanExtend()
  369. }
  370. })
  371. </script>
  372. <style lang="scss" scoped>
  373. .checkin-brief-info {
  374. .top-background {
  375. background-color: #a09cc4;
  376. overflow: hidden; //消除子元素margin-top的影响
  377. .room-info-area {
  378. border-radius: 30rpx;
  379. background-color: #FFFFFF;
  380. margin: 120rpx 20rpx;
  381. padding: 20rpx;
  382. display: flex;
  383. border-radius: 10rpx;
  384. .room-type-pic {
  385. image {
  386. width: 200rpx;
  387. height: 200rpx;
  388. border-radius: 10rpx;
  389. }
  390. }
  391. .room-info {
  392. margin-top: 40rpx;
  393. margin-left: 30rpx;
  394. display: flex;
  395. flex-direction: column;
  396. width: 280rpx;
  397. text {
  398. font-size: 40rpx;
  399. font-weight: bold;
  400. color: #333333;
  401. }
  402. }
  403. .room-type-price {
  404. width: 25%;
  405. margin-right: 20rpx;
  406. margin-left: 60rpx;
  407. margin-top: 70rpx;
  408. text {
  409. font-size: 40rpx;
  410. font-weight: bold;
  411. color: #FF5C58
  412. }
  413. }
  414. }
  415. }
  416. }
  417. .checkin-date-info {
  418. transform: translateY(-100rpx);
  419. border-radius: 30rpx;
  420. background-color: #FFFFFF;
  421. margin: 40rpx;
  422. padding: 20rpx;
  423. display: flex;
  424. background: #FFFFFF;
  425. border-radius: 10rpx;
  426. box-shadow: 0rpx 8rpx 11rpx 2rpx rgba(79, 121, 225, 0.19);
  427. .date-item {
  428. width: 35vw;
  429. .date-item-title {
  430. text {
  431. font-size: 30rpx;
  432. color: #666666;
  433. }
  434. }
  435. .date-item-detail {
  436. text {
  437. font-size: 28rpx;
  438. font-weight: bold;
  439. color: #333333;
  440. margin-right: 20rpx;
  441. }
  442. }
  443. }
  444. .checkin-date-other {
  445. display: flex;
  446. flex-direction: column;
  447. justify-content: space-around;
  448. text {
  449. color: #333333;
  450. font-size: 30rpx;
  451. font-weight: bold;
  452. }
  453. }
  454. }
  455. .guest-info {
  456. transform: translateY(-100rpx);
  457. .guest-info-item {
  458. display: flex;
  459. margin: 40rpx;
  460. border-bottom: 2rpx solid #e5e5e5;
  461. line-height: 70rpx;
  462. .guest-info-item-label {
  463. width: 240rpx;
  464. text {
  465. font-size: 36rpx;
  466. color: #666666;
  467. }
  468. }
  469. .guest-info-item-content {
  470. text {
  471. color: #333333;
  472. font-weight: bold;
  473. font-size: 32rpx;
  474. }
  475. }
  476. }
  477. }
  478. .end-time-tip {
  479. transform: translateY(-100rpx);
  480. display: flex;
  481. justify-content: center;
  482. text {
  483. font-size: 30rpx;
  484. }
  485. }
  486. .btn-area {
  487. transform: translateY(-60rpx);
  488. display: flex;
  489. justify-content: space-evenly;
  490. button {
  491. color: #a09cc4;
  492. border: 1rpx solid #a09cc4;
  493. background-color: #FFFFFF;
  494. font-weight: bold;
  495. font-size: 33rpx;
  496. width: 250rpx;
  497. }
  498. .btn-hover {
  499. color: #FFFFFF;
  500. background-color: #9e97c3;
  501. }
  502. }
  503. .not-checkin {
  504. margin-top: 40vh;
  505. .not-checkin-text {
  506. display: flex;
  507. justify-content: center;
  508. }
  509. }
  510. </style>