serviceOrder.vue 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367
  1. <template>
  2. <view class="container">
  3. <!-- 标签页 -->
  4. <view class="tabs">
  5. <view class="tab-item" :class="{ active: activeTab === 'pending' }" @click="switchTab('pending')">
  6. <text>待确认</text>
  7. <view class="tab-indicator" v-if="activeTab === 'pending'"></view>
  8. </view>
  9. <view class="tab-item" :class="{ active: activeTab === 'confirmed' }" @click="switchTab('confirmed')">
  10. <text>已确认</text>
  11. <view class="tab-indicator" v-if="activeTab === 'confirmed'"></view>
  12. </view>
  13. </view>
  14. <!-- 内容区域 -->
  15. <scroll-view class="content" scroll-y @scrolltolower="loadMore">
  16. <view class="order-list">
  17. <view class="month-group" v-for="(group, groupIndex) in groupedOrders" :key="groupIndex">
  18. <view class="month-header">
  19. <!-- <view class="month-icon">○</view> -->
  20. <text class="month-text">{{ group.month }}</text>
  21. </view>
  22. <view class="order-items">
  23. <view class="order-item" v-for="(item, index) in group.items" :key="index" @click="goToDetail(item)">
  24. <view class="order-content">
  25. <view class="order-title">服务确认单</view>
  26. <view class="order-time">{{ item.createTime }}</view>
  27. </view>
  28. <view class="order-action" v-if="activeTab === 'pending'">
  29. <view class="confirm-btn" @click.stop="goToConfirm(item)">去确认</view>
  30. </view>
  31. </view>
  32. </view>
  33. </view>
  34. </view>
  35. <!-- 空状态 -->
  36. <view class="empty-state" v-if="groupedOrders.length === 0">
  37. <text>暂无数据</text>
  38. </view>
  39. </scroll-view>
  40. </view>
  41. </template>
  42. <script>
  43. import { getServiceOrderList } from '@/api-js/serviceOrder'
  44. export default {
  45. data() {
  46. return {
  47. activeTab: 'pending', // pending: 待确认, confirmed: 已确认
  48. orderList: [],
  49. page: 1,
  50. pageSize: 20,
  51. hasMore: true
  52. }
  53. },
  54. computed: {
  55. // 按月份分组订单
  56. groupedOrders() {
  57. const groups = {}
  58. this.orderList.forEach(item => {
  59. const month = item.createTime ? item.createTime.substring(0, 7) : '未知'
  60. if (!groups[month]) {
  61. groups[month] = {
  62. month: month,
  63. items: []
  64. }
  65. }
  66. groups[month].items.push(item)
  67. })
  68. // 转换为数组并按月份倒序排列
  69. return Object.values(groups).sort((a, b) => {
  70. return b.month.localeCompare(a.month)
  71. })
  72. }
  73. },
  74. onLoad() {
  75. this.loadData()
  76. },
  77. methods: {
  78. async loadData(refresh = false) {
  79. if (refresh) {
  80. this.page = 1
  81. this.hasMore = true
  82. }
  83. if (!this.hasMore) return
  84. try {
  85. uni.showLoading({ title: '加载中...' })
  86. this.orderList = this.getDefaultData()
  87. // const res = await getServiceOrderList({
  88. // status: this.activeTab === 'pending' ? 0 : 1, // 0: 待确认, 1: 已确认
  89. // page: this.page,
  90. // pageSize: this.pageSize
  91. // })
  92. // uni.hideLoading()
  93. // if (res.code === 200 && res.data) {
  94. // const list = res.data.list || []
  95. // if (refresh) {
  96. // this.orderList = list
  97. // } else {
  98. // this.orderList = [...this.orderList, ...list]
  99. // }
  100. // this.hasMore = list.length >= this.pageSize
  101. // if (this.hasMore) {
  102. // this.page++
  103. // }
  104. // } else {
  105. // // 使用示例数据
  106. // this.orderList = this.getDefaultData()
  107. // }
  108. } catch (e) {
  109. uni.hideLoading()
  110. console.error('加载服务单列表失败', e)
  111. this.orderList = this.getDefaultData()
  112. }
  113. },
  114. getDefaultData() {
  115. uni.hideLoading()
  116. // 示例数据
  117. const list = []
  118. const currentDate = new Date()
  119. for (let i = 0; i < 3; i++) {
  120. const date1 = new Date(currentDate.getFullYear(), currentDate.getMonth() - 1, 23, 16, 0, 0)
  121. list.push({
  122. id: `order_${i + 1}`,
  123. title: '服务确认单',
  124. createTime: this.formatDate(date1),
  125. status: this.activeTab === 'pending' ? 0 : 1
  126. })
  127. }
  128. for (let i = 0; i < 3; i++) {
  129. const date2 = new Date(currentDate.getFullYear(), currentDate.getMonth() - 2, 23, 16, 0, 0)
  130. list.push({
  131. id: `order_${i + 4}`,
  132. title: '服务确认单',
  133. createTime: this.formatDate(date2),
  134. status: this.activeTab === 'pending' ? 0 : 1
  135. })
  136. }
  137. return list
  138. },
  139. formatDate(date) {
  140. const year = date.getFullYear()
  141. const month = String(date.getMonth() + 1).padStart(2, '0')
  142. const day = String(date.getDate()).padStart(2, '0')
  143. const hours = String(date.getHours()).padStart(2, '0')
  144. const minutes = String(date.getMinutes()).padStart(2, '0')
  145. const seconds = String(date.getSeconds()).padStart(2, '0')
  146. return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
  147. },
  148. switchTab(tab) {
  149. if (this.activeTab === tab) return
  150. this.activeTab = tab
  151. this.loadData(true)
  152. },
  153. loadMore() {
  154. this.loadData(false)
  155. },
  156. goBack() {
  157. uni.navigateBack()
  158. },
  159. goToDetail(item) {
  160. uni.navigateTo({
  161. url: `/pages_user/serviceOrderDetail?id=${item.id}&status=${item.status}`
  162. })
  163. },
  164. goToConfirm(item) {
  165. uni.navigateTo({
  166. url: `/pages_user/serviceOrderDetail?id=${item.id}&status=0`
  167. })
  168. }
  169. }
  170. }
  171. </script>
  172. <style lang="scss" scoped>
  173. .container {
  174. min-height: 100vh;
  175. background: #f5f5f5;
  176. display: flex;
  177. flex-direction: column;
  178. }
  179. .navbar {
  180. display: flex;
  181. align-items: center;
  182. justify-content: space-between;
  183. padding: 20rpx 24rpx;
  184. background: #fff;
  185. border-bottom: 1rpx solid #f0f0f0;
  186. .nav-left {
  187. width: 60rpx;
  188. .back-icon {
  189. font-size: 36rpx;
  190. color: #333;
  191. font-weight: bold;
  192. }
  193. }
  194. .nav-title {
  195. flex: 1;
  196. text-align: center;
  197. font-size: 36rpx;
  198. font-weight: bold;
  199. color: #333;
  200. }
  201. .nav-right {
  202. display: flex;
  203. align-items: center;
  204. gap: 24rpx;
  205. width: 60rpx;
  206. justify-content: flex-end;
  207. .more-icon {
  208. font-size: 32rpx;
  209. color: #333;
  210. }
  211. .eye-icon {
  212. font-size: 32rpx;
  213. color: #333;
  214. }
  215. }
  216. }
  217. .tabs {
  218. display: flex;
  219. background: #fff;
  220. border-bottom: 1rpx solid #f0f0f0;
  221. .tab-item {
  222. flex: 1;
  223. display: flex;
  224. flex-direction: column;
  225. align-items: center;
  226. justify-content: center;
  227. padding: 24rpx 0;
  228. position: relative;
  229. text {
  230. font-size: 30rpx;
  231. color: #999;
  232. }
  233. &.active {
  234. text {
  235. color: #333;
  236. font-weight: 500;
  237. }
  238. .tab-indicator {
  239. position: absolute;
  240. bottom: 0;
  241. left: 50%;
  242. transform: translateX(-50%);
  243. width: 80rpx;
  244. height: 6rpx;
  245. background: #388BFF;
  246. border-radius: 3rpx 3rpx 3rpx 3rpx;
  247. }
  248. }
  249. }
  250. }
  251. .content {
  252. flex: 1;
  253. }
  254. .order-list {
  255. padding: 24rpx;
  256. }
  257. .month-group {
  258. margin-bottom: 32rpx;
  259. &:last-child {
  260. margin-bottom: 0;
  261. }
  262. .month-header {
  263. display: flex;
  264. align-items: center;
  265. margin-bottom: 16rpx;
  266. .month-icon {
  267. width: 16rpx;
  268. height: 16rpx;
  269. border-radius: 50%;
  270. background: #ccc;
  271. margin-right: 16rpx;
  272. }
  273. .month-text {
  274. font-size: 28rpx;
  275. color: #666;
  276. }
  277. }
  278. .order-items {
  279. .order-item {
  280. background: #fff;
  281. border-radius: 16rpx;
  282. padding: 32rpx 24rpx;
  283. margin-bottom: 20rpx;
  284. display: flex;
  285. align-items: center;
  286. justify-content: space-between;
  287. &:last-child {
  288. margin-bottom: 0;
  289. }
  290. .order-content {
  291. flex: 1;
  292. .order-title {
  293. font-family: PingFang SC, PingFang SC;
  294. font-weight: 500;
  295. font-size: 28rpx;
  296. color: #333333;
  297. margin-bottom: 12rpx;
  298. }
  299. .order-time {
  300. font-family: PingFang SC, PingFang SC;
  301. font-weight: 400;
  302. font-size: 24rpx;
  303. color: #666666;
  304. }
  305. }
  306. .order-action {
  307. .confirm-btn {
  308. padding: 12rpx 32rpx;
  309. background: #388BFF;
  310. border-radius: 44rpx;
  311. font-family: PingFang SC, PingFang SC;
  312. font-weight: 500;
  313. font-size: 24rpx;
  314. color: #FFFFFF;
  315. }
  316. }
  317. }
  318. }
  319. }
  320. .empty-state {
  321. padding: 120rpx 24rpx;
  322. text-align: center;
  323. font-size: 28rpx;
  324. color: #999;
  325. }
  326. </style>