orders.vue 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375
  1. <template>
  2. <view class="hotel-customer-theme orders-page">
  3. <!-- 房间信息 -->
  4. <view v-if="roomInfo" class="o-room-bar">
  5. <text>🏨 {{ roomInfo.roomNo }} {{ roomInfo.roomTypeName || '' }}</text>
  6. </view>
  7. <!-- 加载中 -->
  8. <view v-if="loading && orderList.length === 0" class="o-loading">
  9. <text>加载中...</text>
  10. </view>
  11. <!-- 空状态 -->
  12. <view v-else-if="orderList.length === 0" class="o-empty">
  13. <text class="o-empty-icon">📋</text>
  14. <text class="o-empty-text">暂无订单记录</text>
  15. <view class="o-empty-btn" @click="goMenu">去点餐</view>
  16. </view>
  17. <!-- 订单列表 -->
  18. <scroll-view v-else scroll-y class="o-scroll" @scrolltolower="loadMore">
  19. <view class="o-list">
  20. <view
  21. v-for="item in orderList"
  22. :key="item.id"
  23. class="o-card"
  24. @click="viewDetail(item)"
  25. >
  26. <!-- 卡片头部 -->
  27. <view class="o-card-header">
  28. <text class="o-card-no">{{ item.orderNo }}</text>
  29. <text class="o-card-status" :class="'o-st--' + item.status">{{ statusLabel(item.status) }}</text>
  30. </view>
  31. <!-- 菜品摘要 -->
  32. <view class="o-card-body">
  33. <view v-for="(dish, dIdx) in getItemsPreview(item)" :key="dIdx" class="o-card-dish">
  34. <text class="o-card-dish-name">{{ dish.dishName }} ×{{ dish.quantity }}</text>
  35. <text class="o-card-dish-spec" v-if="dish.specDesc">{{ dish.specDesc }}</text>
  36. </view>
  37. <text v-if="getItemCount(item) > 3" class="o-card-more">...等{{ getItemCount(item) }}件商品</text>
  38. </view>
  39. <!-- 卡片底部 -->
  40. <view class="o-card-footer">
  41. <text class="o-card-time">{{ item.createTime }}</text>
  42. <text class="o-card-amount">¥{{ item.totalAmount }}</text>
  43. </view>
  44. <!-- 待支付订单显示支付按钮 -->
  45. <view v-if="item.status === 0" class="o-card-pay">
  46. <text class="o-card-pay-btn" @click.stop="goPay(item)">去支付</text>
  47. </view>
  48. <!-- 退单原因 -->
  49. <view v-if="item.status === 9 && item.refundReason" class="o-card-refund">
  50. <text>↩️ {{ item.refundReason }}</text>
  51. </view>
  52. </view>
  53. </view>
  54. <!-- 加载更多 -->
  55. <view v-if="loading" class="o-load-more">
  56. <text>加载中...</text>
  57. </view>
  58. <view v-else-if="noMore" class="o-load-more o-load-more--end">
  59. <text>没有更多了</text>
  60. </view>
  61. </scroll-view>
  62. <!-- 底部 TabBar -->
  63. <HotelTabBar current="order" />
  64. </view>
  65. </template>
  66. <script setup>
  67. import { computed, ref } from 'vue'
  68. import { useHotelOrderStore } from '@/store'
  69. import api from '@/api'
  70. import HotelTabBar from '@/components/hotel/HotelTabBar.vue'
  71. const orderStore = useHotelOrderStore()
  72. const roomInfo = computed(() => orderStore.roomInfo)
  73. const orderList = ref([])
  74. const loading = ref(false)
  75. const noMore = ref(false)
  76. const pageNum = ref(1)
  77. const pageSize = 10
  78. // 页面加载时获取订单
  79. import { onMounted } from 'vue'
  80. onMounted(() => {
  81. loadOrders()
  82. })
  83. async function loadOrders() {
  84. if (loading.value) return
  85. loading.value = true
  86. try {
  87. const res = await api.hotelOrderPage({
  88. tenantId: orderStore.tenantId,
  89. pageNum: pageNum.value,
  90. pageSize: pageSize.value,
  91. // 按房间过滤(如果后端支持)
  92. roomNo: roomInfo.value?.roomNo,
  93. })
  94. const data = res?.data || res
  95. const list = data?.records || (Array.isArray(data) ? data : [])
  96. if (pageNum.value === 1) {
  97. orderList.value = list
  98. } else {
  99. orderList.value = orderList.value.concat(list)
  100. }
  101. if (list.length < pageSize.value) {
  102. noMore.value = true
  103. }
  104. } catch (e) {
  105. console.error('加载订单列表失败:', e)
  106. uni.showToast({ title: '加载失败', icon: 'none' })
  107. } finally {
  108. loading.value = false
  109. }
  110. }
  111. function loadMore() {
  112. if (noMore.value || loading.value) return
  113. pageNum.value++
  114. loadOrders()
  115. }
  116. function getItemsPreview(order) {
  117. if (!order.items) return []
  118. return order.items.slice(0, 3)
  119. }
  120. function getItemCount(order) {
  121. if (!order.items) return 0
  122. return order.items.length
  123. }
  124. function statusLabel(status) {
  125. const map = {
  126. 0: '待支付', 1: '待接单', 2: '已接单', 3: '备餐中',
  127. 4: '已出餐', 5: '配送中', 6: '已完成',
  128. 7: '已拒单', 8: '退单审核中', 9: '已退单',
  129. 10: '超时取消',
  130. }
  131. return map[status] || '未知'
  132. }
  133. function viewDetail(order) {
  134. if (order.status === 0) {
  135. // 待支付订单跳转到支付页
  136. uni.redirectTo({
  137. url: `/pages/hotel/customer/pay?orderId=${order.id}&tenantId=${order.tenantId}&amount=${order.totalAmount}`,
  138. })
  139. } else {
  140. uni.redirectTo({
  141. url: `/pages/hotel/customer/order-status?orderId=${order.id}`,
  142. })
  143. }
  144. }
  145. function goPay(order) {
  146. uni.redirectTo({
  147. url: `/pages/hotel/customer/pay?orderId=${order.id}&tenantId=${order.tenantId}&amount=${order.totalAmount}`,
  148. })
  149. }
  150. function goMenu() {
  151. uni.redirectTo({ url: '/pages/hotel/customer/menu' })
  152. }
  153. </script>
  154. <style lang="scss" scoped>
  155. .orders-page {
  156. min-height: 100vh;
  157. background: var(--h-bg, #F7F7F7);
  158. padding-bottom: calc(140rpx + env(safe-area-inset-bottom));
  159. }
  160. /* 房间信息条 */
  161. .o-room-bar {
  162. padding: 20rpx 28rpx;
  163. font-size: 24rpx;
  164. color: var(--h-txt-sub, #666);
  165. }
  166. /* 加载/空状态 */
  167. .o-loading {
  168. display: flex;
  169. justify-content: center;
  170. align-items: center;
  171. min-height: 300rpx;
  172. color: var(--h-txt-light, #999);
  173. font-size: 26rpx;
  174. }
  175. .o-empty {
  176. display: flex;
  177. flex-direction: column;
  178. align-items: center;
  179. justify-content: center;
  180. min-height: 400rpx;
  181. }
  182. .o-empty-icon {
  183. font-size: 80rpx;
  184. margin-bottom: 20rpx;
  185. }
  186. .o-empty-text {
  187. font-size: 28rpx;
  188. color: var(--h-txt-light, #999);
  189. margin-bottom: 32rpx;
  190. }
  191. .o-empty-btn {
  192. padding: 20rpx 64rpx;
  193. border-radius: 48rpx;
  194. background: var(--h-pri, #2D5016);
  195. color: #fff;
  196. font-size: 28rpx;
  197. font-weight: 600;
  198. }
  199. /* 滚动区域 */
  200. .o-scroll {
  201. height: calc(100vh - 140rpx - env(safe-area-inset-bottom));
  202. }
  203. .o-list {
  204. padding: 0 28rpx;
  205. }
  206. /* 订单卡片 */
  207. .o-card {
  208. background: #fff;
  209. border-radius: var(--h-rad, 20rpx);
  210. padding: 28rpx 24rpx;
  211. margin-bottom: 16rpx;
  212. box-shadow: var(--h-shd, 0 2rpx 8rpx rgba(0, 0, 0, 0.04));
  213. }
  214. .o-card:active {
  215. opacity: 0.85;
  216. }
  217. .o-card-header {
  218. display: flex;
  219. justify-content: space-between;
  220. align-items: center;
  221. margin-bottom: 16rpx;
  222. }
  223. .o-card-no {
  224. font-size: 24rpx;
  225. color: var(--h-txt-light, #999);
  226. }
  227. .o-card-status {
  228. font-size: 22rpx;
  229. font-weight: 600;
  230. padding: 4rpx 16rpx;
  231. border-radius: 20rpx;
  232. }
  233. /* 状态标签颜色 */
  234. .o-st--0 { background: #FEF3C7; color: #92400E; }
  235. .o-st--1 { background: #FEF3C7; color: #92400E; }
  236. .o-st--2 { background: #DCFCE7; color: #166534; }
  237. .o-st--3 { background: #FFEDD5; color: #9A3412; }
  238. .o-st--4 { background: #DBEAFE; color: #1E40AF; }
  239. .o-st--5 { background: #EDE9FE; color: #5B21B6; }
  240. .o-st--6 { background: #DCFCE7; color: #166534; }
  241. .o-st--7 { background: #FEE2E2; color: #991B1B; }
  242. .o-st--8 { background: #FEF9C3; color: #854D0E; }
  243. .o-st--9 { background: #FEF9C3; color: #854D0E; }
  244. .o-st--10 { background: #F3F4F6; color: #6B7280; }
  245. /* 菜品摘要 */
  246. .o-card-body {
  247. padding: 12rpx 0;
  248. border-top: 1rpx solid var(--h-bdr-lt, #F5F5F5);
  249. border-bottom: 1rpx solid var(--h-bdr-lt, #F5F5F5);
  250. }
  251. .o-card-dish {
  252. display: flex;
  253. align-items: center;
  254. gap: 12rpx;
  255. padding: 4rpx 0;
  256. }
  257. .o-card-dish-name {
  258. font-size: 26rpx;
  259. color: var(--h-txt, #1A1A1A);
  260. }
  261. .o-card-dish-spec {
  262. font-size: 22rpx;
  263. color: var(--h-txt-light, #999);
  264. }
  265. .o-card-more {
  266. font-size: 22rpx;
  267. color: var(--h-txt-light, #999);
  268. margin-top: 4rpx;
  269. }
  270. /* 卡片底部 */
  271. .o-card-footer {
  272. display: flex;
  273. justify-content: space-between;
  274. align-items: center;
  275. padding-top: 16rpx;
  276. }
  277. .o-card-time {
  278. font-size: 22rpx;
  279. color: var(--h-txt-light, #999);
  280. }
  281. .o-card-amount {
  282. font-size: 34rpx;
  283. font-weight: 700;
  284. color: var(--h-warn, #D97706);
  285. }
  286. /* 待支付按钮 */
  287. .o-card-pay {
  288. display: flex;
  289. justify-content: flex-end;
  290. padding: 12rpx 0 4rpx;
  291. }
  292. .o-card-pay-btn {
  293. padding: 12rpx 40rpx;
  294. border-radius: 32rpx;
  295. background: var(--h-pri, #2D5016);
  296. color: #fff;
  297. font-size: 24rpx;
  298. font-weight: 600;
  299. }
  300. .o-card-pay-btn:active {
  301. opacity: 0.8;
  302. }
  303. /* 退单原因 */
  304. .o-card-refund {
  305. margin-top: 12rpx;
  306. padding: 12rpx 16rpx;
  307. background: #FFF8E1;
  308. border-radius: var(--h-rad-sm, 12rpx);
  309. font-size: 22rpx;
  310. color: var(--h-txt-sub, #666);
  311. }
  312. /* 加载更多 */
  313. .o-load-more {
  314. text-align: center;
  315. padding: 24rpx 0;
  316. font-size: 24rpx;
  317. color: var(--h-txt-light, #999);
  318. }
  319. .o-load-more--end {
  320. color: var(--h-bdr, #eee);
  321. }
  322. </style>