爱简收旧衣按件回收前端代码仓库
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

526 lines
12 KiB

2 months ago
1 month ago
2 months ago
1 month ago
2 months ago
1 month ago
2 months ago
2 months ago
1 month ago
2 months ago
1 month ago
2 months ago
1 month ago
2 months ago
2 months ago
1 month ago
2 months ago
1 month ago
2 months ago
1 month ago
2 months ago
1 month ago
2 months ago
1 month ago
2 months ago
1 month ago
2 months ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
2 months ago
1 month ago
2 months ago
1 month ago
2 months ago
1 month ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
1 month ago
2 months ago
1 month ago
2 months ago
  1. <template>
  2. <view class="container">
  3. <!-- 顶部导航 -->
  4. <view class="nav-bar">
  5. <view class="back" @tap="goBack">
  6. <uni-icons type="left" size="20"></uni-icons>
  7. </view>
  8. <text class="title">我的订单</text>
  9. </view>
  10. <!-- 页面内容 -->
  11. <view class="content">
  12. <!-- <view class="page-header">
  13. <text class="page-title">我的订单</text>
  14. <text class="view-all" @tap="viewAll">查看全部 ></text>
  15. </view> -->
  16. <!-- 标签页 -->
  17. <view class="tabs">
  18. <view
  19. v-for="(tab, index) in tabs"
  20. :key="index"
  21. class="tab-item"
  22. :class="{ active: currentTab === index }"
  23. @tap="switchTab(index)"
  24. >
  25. <text>{{ tab.name }}</text>
  26. <text v-if="tab.count > 0" class="badge">{{ tab.count }}</text>
  27. </view>
  28. </view>
  29. <!-- 订单列表 -->
  30. <scroll-view
  31. scroll-y
  32. class="order-list"
  33. @scrolltolower="loadMore"
  34. refresher-enabled
  35. :refresher-triggered="refresherTriggered"
  36. @refresherrefresh="onRefresherRefresh"
  37. >
  38. <view
  39. v-for="order in orderList"
  40. :key="order.id"
  41. class="order-card"
  42. @tap="goToDetail(order)"
  43. >
  44. <!-- 订单号 -->
  45. <view class="order-header">
  46. <text class="order-id">
  47. 订单编号
  48. <text class="order-ids">{{order.ordeNo || order.id}}</text>
  49. </text>
  50. </view>
  51. <!-- 订单内容 -->
  52. <view class="order-content">
  53. <image class="order-image" :src="order.image || '/static/回收/衣物.png'" mode="aspectFill"></image>
  54. <view class="order-arrow">
  55. <text class="iconfont"></text>
  56. </view>
  57. <view class="order-info">
  58. <text class="order-count">{{ order.num || order.count || 1 }} </text>
  59. <!-- <text class="order-price">¥ {{ order.onePrice || order.priceRange || 0 }} /</text> -->
  60. <view class="order-estimate">
  61. <text v-if="order.price ">结算 ¥{{ order.price || 0 }}</text>
  62. <text v-else>预估 ¥{{ order.estimatedPrice || 0 }}</text>
  63. </view>
  64. </view>
  65. </view>
  66. <!-- 订单状态 -->
  67. <view class="order-status">
  68. <image class="status-icon" :src="order.statusIcon || '/static/my/【待取件】快递员正在赶来.png'" mode="aspectFill"></image>
  69. <view class="status-info">
  70. <text class="status-text">{{ getOrderStatusText(order) }}</text>
  71. <text class="status-time">{{ order.goTime || order.statusTime }}</text>
  72. </view>
  73. </view>
  74. </view>
  75. <view class="order-loading" v-if="loading">
  76. <uni-loading color="#ffac07" :size="40" />
  77. <text class="loading-text">加载中...</text>
  78. </view>
  79. <view class="order-nomore" v-else-if="!hasMore && orderList.length">
  80. <text class="nomore-text">没有更多订单了</text>
  81. </view>
  82. </scroll-view>
  83. </view>
  84. </view>
  85. </template>
  86. <script>
  87. export default {
  88. data() {
  89. return {
  90. tabs: [
  91. { name: '全部', count: 0 },
  92. { name: '进行中', count: 0 },
  93. { name: '已完成', count: 0 }
  94. ],
  95. currentTab: 0,
  96. orderList: [], // 当前显示的订单列表
  97. loading: false,
  98. page: 1,
  99. pageSize: 10,
  100. hasMore: true,
  101. refresherTriggered: false
  102. }
  103. },
  104. created() {
  105. this.fetchOrderList()
  106. // 监听订单状态变化事件
  107. uni.$on('orderStatusChanged', this.handleOrderStatusChanged)
  108. },
  109. onShow() {
  110. this.fetchOrderList()
  111. },
  112. onPullDownRefresh() {
  113. // 页面级下拉刷新(备用)
  114. this.refreshData()
  115. },
  116. onUnload() {
  117. // 移除事件监听
  118. uni.$off('orderStatusChanged', this.handleOrderStatusChanged)
  119. },
  120. methods: {
  121. async fetchOrderList(isLoadMore = false) {
  122. this.loading = true
  123. try {
  124. let statusArr = []
  125. if (this.currentTab === 1) statusArr = [0, 1, 2] // 进行中
  126. else if (this.currentTab === 2) statusArr = [3] // 已完成
  127. else statusArr = [] // 全部
  128. let allOrders = []
  129. if (statusArr.length === 0) {
  130. // 全部
  131. await new Promise(resolve => {
  132. this.$api('getOrderListPage', { pageSize: this.pageSize, current: this.page }, res => {
  133. if (res && res.code === 200 && res.result && Array.isArray(res.result.records)) {
  134. allOrders = res.result.records
  135. if (isLoadMore) {
  136. this.orderList = this.orderList.concat(allOrders)
  137. } else {
  138. this.orderList = allOrders
  139. }
  140. this.hasMore = allOrders.length === this.pageSize
  141. }
  142. resolve()
  143. })
  144. })
  145. } else {
  146. // 多状态合并
  147. for (let status of statusArr) {
  148. await new Promise(resolve => {
  149. this.$api('getOrderListPage', { status, pageSize: this.pageSize, current: this.page }, res => {
  150. if (res && res.code === 200 && res.result && Array.isArray(res.result.records)) {
  151. allOrders = allOrders.concat(res.result.records)
  152. }
  153. resolve()
  154. })
  155. })
  156. }
  157. // 去重
  158. const map = {}
  159. allOrders = allOrders.filter(item => {
  160. if (map[item.id]) return false
  161. map[item.id] = 1
  162. return true
  163. })
  164. if (isLoadMore) {
  165. this.orderList = this.orderList.concat(allOrders)
  166. } else {
  167. this.orderList = allOrders
  168. }
  169. this.hasMore = allOrders.length === this.pageSize
  170. }
  171. } catch (error) {
  172. console.error('获取订单列表失败:', error)
  173. } finally {
  174. this.loading = false
  175. }
  176. },
  177. switchTab(index) {
  178. if (this.currentTab === index) return
  179. this.currentTab = index
  180. this.page = 1 // 重置页码
  181. this.hasMore = true
  182. this.orderList = []
  183. this.fetchOrderList()
  184. },
  185. getOrderStatusText(order) {
  186. const { status, state } = order
  187. if (state == 3) return '已取消'
  188. if (status == 0) return '【在线预约】'
  189. if (status == 1 && state == 0) return '【待取件】快递员正在赶来'
  190. if (status == 1 && state == 1) return '【已取件】快递员正在送至质检'
  191. if (status == 2 && state == 1) return '【质检中】质检员正在质检'
  192. if (status == 3 && state == 1) return '【待结款】待平台确认结款项'
  193. if (status == 3 && state == 2) return '【已结款】平台已结款至账户'
  194. return ''
  195. },
  196. goBack() {
  197. uni.navigateBack()
  198. },
  199. showMore() {
  200. // 显示更多选项
  201. },
  202. onShare() {
  203. // 分享功能
  204. },
  205. viewAll() {
  206. this.switchTab(0) // 切换到全部标签
  207. },
  208. // 加载更多
  209. loadMore() {
  210. if (this.loading || !this.hasMore) return
  211. this.page++
  212. this.fetchOrderList(true)
  213. },
  214. goToDetail(order) {
  215. uni.navigateTo({
  216. url: `/pages/subcomponent/detail?id=${order.id}`
  217. })
  218. },
  219. // 处理订单状态变化
  220. handleOrderStatusChanged() {
  221. // 重置页码并刷新当前标签页的数据
  222. this.page = 1
  223. this.hasMore = true
  224. this.orderList = []
  225. this.fetchOrderList()
  226. },
  227. // scroll-view 下拉刷新
  228. onRefresherRefresh() {
  229. this.refreshData()
  230. },
  231. // 通用刷新方法
  232. async refreshData() {
  233. this.refresherTriggered = true
  234. this.page = 1
  235. this.hasMore = true
  236. this.orderList = []
  237. try {
  238. await this.fetchOrderList()
  239. uni.showToast({
  240. title: '刷新成功',
  241. icon: 'success',
  242. duration: 1000
  243. })
  244. } catch (error) {
  245. uni.showToast({
  246. title: '刷新失败',
  247. icon: 'none'
  248. })
  249. } finally {
  250. setTimeout(() => {
  251. this.refresherTriggered = false
  252. uni.stopPullDownRefresh()
  253. }, 1000)
  254. }
  255. }
  256. }
  257. }
  258. </script>
  259. <style lang="scss" scoped>
  260. .container {
  261. background: #f8f8f8;
  262. padding-bottom: calc(140rpx + env(safe-area-inset-bottom));
  263. overflow: hidden;
  264. height: 100vh;
  265. }
  266. .nav-bar {
  267. display: flex;
  268. align-items: center;
  269. height: calc(150rpx + var(--status-bar-height));
  270. padding: 0 32rpx;
  271. padding-top: var(--status-bar-height);
  272. background: #fff;
  273. position: fixed;
  274. top: 0;
  275. left: 0;
  276. right: 0;
  277. z-index: 999;
  278. box-sizing: border-box;
  279. .back {
  280. padding: 20rpx;
  281. margin-left: -20rpx;
  282. }
  283. .title {
  284. flex: 1;
  285. text-align: center;
  286. font-size: 34rpx;
  287. font-weight: 500;
  288. color: #222;
  289. }
  290. .right-btns {
  291. display: flex;
  292. align-items: center;
  293. gap: 32rpx;
  294. .more, .target {
  295. font-size: 40rpx;
  296. color: #333;
  297. }
  298. }
  299. }
  300. .content {
  301. padding: 30rpx 0 0 0;
  302. margin-top: calc(150rpx + var(--status-bar-height) + 80rpx);
  303. height: 100%;
  304. display: flex;
  305. flex-direction: column;
  306. overflow: hidden;
  307. box-sizing: border-box;
  308. .page-header {
  309. display: flex;
  310. justify-content: space-between;
  311. align-items: center;
  312. margin-bottom: 30rpx;
  313. .page-title {
  314. font-size: 34rpx;
  315. font-weight: bold;
  316. }
  317. .view-all {
  318. font-size: 28rpx;
  319. color: #999;
  320. }
  321. }
  322. }
  323. .tabs {
  324. display: flex;
  325. background: #f5f5f5;
  326. border-radius: 8rpx;
  327. margin-bottom: 30rpx;
  328. position: fixed;
  329. top: calc(150rpx + var(--status-bar-height));
  330. left: 0;
  331. width: 100%;
  332. z-index: 998;
  333. box-sizing: border-box;
  334. .tab-item {
  335. flex: 1;
  336. height: 80rpx;
  337. display: flex;
  338. align-items: center;
  339. justify-content: center;
  340. font-size: 28rpx;
  341. color: #666;
  342. position: relative;
  343. &.active {
  344. color: #333;
  345. background-color: #e7e7e7;
  346. font-weight: 500;
  347. border-radius: 8rpx;
  348. }
  349. .badge {
  350. position: absolute;
  351. top: 10rpx;
  352. right: 10rpx;
  353. min-width: 32rpx;
  354. height: 32rpx;
  355. line-height: 32rpx;
  356. text-align: center;
  357. background: #ff4d4f;
  358. color: #fff;
  359. border-radius: 16rpx;
  360. font-size: 24rpx;
  361. padding: 0 6rpx;
  362. }
  363. }
  364. }
  365. .order-list {
  366. flex: 1;
  367. min-height: 0;
  368. overflow-y: scroll;
  369. padding-bottom: env(safe-area-inset-bottom);
  370. box-sizing: border-box;
  371. }
  372. .order-card {
  373. background: #fff;
  374. border-radius: 16rpx;
  375. padding: 40rpx;
  376. margin-bottom: 20rpx;
  377. .order-header {
  378. margin-bottom: 20rpx;
  379. .order-id {
  380. font-size: 28rpx;
  381. color: #999;
  382. .order-ids{
  383. font-family: PingFang SC;
  384. font-weight: 600;
  385. font-size: 16px;
  386. line-height: 140%;
  387. letter-spacing: 0%;
  388. color: #333;
  389. }
  390. }
  391. }
  392. .order-content {
  393. display: flex;
  394. align-items: center;
  395. margin-bottom: 20rpx;
  396. flex-direction: row;
  397. justify-content: space-between;
  398. .order-image {
  399. width: 160rpx;
  400. height: 160rpx;
  401. border-radius: 8rpx;
  402. // background-color: #fff8ea;
  403. background: #fffbe6;
  404. padding: 20rpx;
  405. box-sizing: border-box;
  406. display: block;
  407. }
  408. .order-arrow {
  409. padding: 0 30rpx;
  410. color: #999;
  411. }
  412. .order-info {
  413. // flex: 1;
  414. display: flex;
  415. flex-direction: column;
  416. text-align: center;
  417. .order-count {
  418. font-size: 32rpx;
  419. font-weight: 500;
  420. margin-bottom: 10rpx;
  421. background-color: #fffaee;
  422. // display: block;
  423. }
  424. .order-price {
  425. font-size: 28rpx;
  426. color: #666;
  427. margin-bottom: 10rpx;
  428. background-color: #fffaee;
  429. // display: block;
  430. }
  431. .order-estimate {
  432. display: inline-block;
  433. background:#ffac07;
  434. color: #fff;
  435. font-size: 26rpx;
  436. padding: 8rpx 20rpx;
  437. border-radius: 20rpx;
  438. }
  439. }
  440. }
  441. .order-status {
  442. display: flex;
  443. align-items: center;
  444. padding: 20rpx;
  445. background: #f4f4f4;
  446. border-radius: 8rpx;
  447. .status-icon {
  448. width: 80rpx;
  449. height: 80rpx;
  450. margin-right: 20rpx;
  451. }
  452. .status-info {
  453. flex: 1;
  454. .status-text {
  455. font-size: 28rpx;
  456. color: #333;
  457. margin-bottom: 6rpx;
  458. display: block;
  459. }
  460. .status-time {
  461. font-size: 26rpx;
  462. color: #999;
  463. }
  464. }
  465. }
  466. }
  467. .order-loading {
  468. display: flex;
  469. flex-direction: column;
  470. align-items: center;
  471. justify-content: center;
  472. padding: 30rpx 0;
  473. color: #ffac07;
  474. font-size: 28rpx;
  475. }
  476. .loading-text {
  477. margin-top: 10rpx;
  478. color: #ffac07;
  479. font-size: 26rpx;
  480. }
  481. .order-nomore {
  482. text-align: center;
  483. color: #bbb;
  484. font-size: 24rpx;
  485. padding: 20rpx 0 40rpx 0;
  486. }
  487. .nomore-text {
  488. color: #bbb;
  489. font-size: 24rpx;
  490. }
  491. </style>