爱简收旧衣按件回收前端代码仓库
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.

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