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

528 lines
13 KiB

3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
2 weeks ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
2 weeks 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.logisticsTrajectory ? order.logisticsTrajectory.createTime : order.goTime }}</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 (state == 4) return '已驳回'
  189. if (state == 5) return '已退款'
  190. if (status == 0) return '【在线预约】'
  191. if (status == 1 && state == 0) return '【待取件】快递员正在赶来'
  192. if (status == 1 && state == 1) return '【已取件】快递员正在送至质检'
  193. if (status == 2 && state == 1) return '【质检中】质检员正在质检'
  194. if (status == 3 && state == 1) return '【待结款】待平台确认结款项'
  195. if (status == 3 && state == 2) return '【已结款】平台已结款至账户'
  196. return ''
  197. },
  198. goBack() {
  199. uni.navigateBack()
  200. },
  201. showMore() {
  202. // 显示更多选项
  203. },
  204. onShare() {
  205. // 分享功能
  206. },
  207. viewAll() {
  208. this.switchTab(0) // 切换到全部标签
  209. },
  210. // 加载更多
  211. loadMore() {
  212. if (this.loading || !this.hasMore) return
  213. this.page++
  214. this.fetchOrderList(true)
  215. },
  216. goToDetail(order) {
  217. uni.navigateTo({
  218. url: `/pages/subcomponent/detail?id=${order.id}`
  219. })
  220. },
  221. // 处理订单状态变化
  222. handleOrderStatusChanged() {
  223. // 重置页码并刷新当前标签页的数据
  224. this.page = 1
  225. this.hasMore = true
  226. this.orderList = []
  227. this.fetchOrderList()
  228. },
  229. // scroll-view 下拉刷新
  230. onRefresherRefresh() {
  231. this.refreshData()
  232. },
  233. // 通用刷新方法
  234. async refreshData() {
  235. this.refresherTriggered = true
  236. this.page = 1
  237. this.hasMore = true
  238. this.orderList = []
  239. try {
  240. await this.fetchOrderList()
  241. uni.showToast({
  242. title: '刷新成功',
  243. icon: 'success',
  244. duration: 1000
  245. })
  246. } catch (error) {
  247. uni.showToast({
  248. title: '刷新失败',
  249. icon: 'none'
  250. })
  251. } finally {
  252. setTimeout(() => {
  253. this.refresherTriggered = false
  254. uni.stopPullDownRefresh()
  255. }, 1000)
  256. }
  257. }
  258. }
  259. }
  260. </script>
  261. <style lang="scss" scoped>
  262. .container {
  263. background: #f8f8f8;
  264. padding-bottom: calc(140rpx + env(safe-area-inset-bottom));
  265. overflow: hidden;
  266. height: 100vh;
  267. }
  268. .nav-bar {
  269. display: flex;
  270. align-items: center;
  271. height: calc(150rpx + var(--status-bar-height));
  272. padding: 0 32rpx;
  273. padding-top: var(--status-bar-height);
  274. background: #fff;
  275. position: fixed;
  276. top: 0;
  277. left: 0;
  278. right: 0;
  279. z-index: 999;
  280. box-sizing: border-box;
  281. .back {
  282. padding: 20rpx;
  283. margin-left: -20rpx;
  284. }
  285. .title {
  286. flex: 1;
  287. text-align: center;
  288. font-size: 34rpx;
  289. font-weight: 500;
  290. color: #222;
  291. }
  292. .right-btns {
  293. display: flex;
  294. align-items: center;
  295. gap: 32rpx;
  296. .more, .target {
  297. font-size: 40rpx;
  298. color: #333;
  299. }
  300. }
  301. }
  302. .content {
  303. padding: 30rpx 0 0 0;
  304. margin-top: calc(150rpx + var(--status-bar-height) + 80rpx);
  305. height: 100%;
  306. display: flex;
  307. flex-direction: column;
  308. overflow: hidden;
  309. box-sizing: border-box;
  310. .page-header {
  311. display: flex;
  312. justify-content: space-between;
  313. align-items: center;
  314. margin-bottom: 30rpx;
  315. .page-title {
  316. font-size: 34rpx;
  317. font-weight: bold;
  318. }
  319. .view-all {
  320. font-size: 28rpx;
  321. color: #999;
  322. }
  323. }
  324. }
  325. .tabs {
  326. display: flex;
  327. background: #f5f5f5;
  328. border-radius: 8rpx;
  329. margin-bottom: 30rpx;
  330. position: fixed;
  331. top: calc(150rpx + var(--status-bar-height));
  332. left: 0;
  333. width: 100%;
  334. z-index: 998;
  335. box-sizing: border-box;
  336. .tab-item {
  337. flex: 1;
  338. height: 80rpx;
  339. display: flex;
  340. align-items: center;
  341. justify-content: center;
  342. font-size: 28rpx;
  343. color: #666;
  344. position: relative;
  345. &.active {
  346. color: #333;
  347. background-color: #e7e7e7;
  348. font-weight: 500;
  349. border-radius: 8rpx;
  350. }
  351. .badge {
  352. position: absolute;
  353. top: 10rpx;
  354. right: 10rpx;
  355. min-width: 32rpx;
  356. height: 32rpx;
  357. line-height: 32rpx;
  358. text-align: center;
  359. background: #ff4d4f;
  360. color: #fff;
  361. border-radius: 16rpx;
  362. font-size: 24rpx;
  363. padding: 0 6rpx;
  364. }
  365. }
  366. }
  367. .order-list {
  368. flex: 1;
  369. min-height: 0;
  370. overflow-y: scroll;
  371. padding-bottom: env(safe-area-inset-bottom);
  372. box-sizing: border-box;
  373. }
  374. .order-card {
  375. background: #fff;
  376. border-radius: 16rpx;
  377. padding: 40rpx;
  378. margin-bottom: 20rpx;
  379. .order-header {
  380. margin-bottom: 20rpx;
  381. .order-id {
  382. font-size: 28rpx;
  383. color: #999;
  384. .order-ids{
  385. font-family: PingFang SC;
  386. font-weight: 600;
  387. font-size: 16px;
  388. line-height: 140%;
  389. letter-spacing: 0%;
  390. color: #333;
  391. }
  392. }
  393. }
  394. .order-content {
  395. display: flex;
  396. align-items: center;
  397. margin-bottom: 20rpx;
  398. flex-direction: row;
  399. justify-content: space-between;
  400. .order-image {
  401. width: 160rpx;
  402. height: 160rpx;
  403. border-radius: 8rpx;
  404. // background-color: #fff8ea;
  405. background: #fffbe6;
  406. padding: 20rpx;
  407. box-sizing: border-box;
  408. display: block;
  409. }
  410. .order-arrow {
  411. padding: 0 30rpx;
  412. color: #999;
  413. }
  414. .order-info {
  415. // flex: 1;
  416. display: flex;
  417. flex-direction: column;
  418. text-align: center;
  419. .order-count {
  420. font-size: 32rpx;
  421. font-weight: 500;
  422. margin-bottom: 10rpx;
  423. background-color: #fffaee;
  424. // display: block;
  425. }
  426. .order-price {
  427. font-size: 28rpx;
  428. color: #666;
  429. margin-bottom: 10rpx;
  430. background-color: #fffaee;
  431. // display: block;
  432. }
  433. .order-estimate {
  434. display: inline-block;
  435. background:#ffac07;
  436. color: #fff;
  437. font-size: 26rpx;
  438. padding: 8rpx 20rpx;
  439. border-radius: 20rpx;
  440. }
  441. }
  442. }
  443. .order-status {
  444. display: flex;
  445. align-items: center;
  446. padding: 20rpx;
  447. background: #f4f4f4;
  448. border-radius: 8rpx;
  449. .status-icon {
  450. width: 80rpx;
  451. height: 80rpx;
  452. margin-right: 20rpx;
  453. }
  454. .status-info {
  455. flex: 1;
  456. .status-text {
  457. font-size: 28rpx;
  458. color: #333;
  459. margin-bottom: 6rpx;
  460. display: block;
  461. }
  462. .status-time {
  463. font-size: 26rpx;
  464. color: #999;
  465. }
  466. }
  467. }
  468. }
  469. .order-loading {
  470. display: flex;
  471. flex-direction: column;
  472. align-items: center;
  473. justify-content: center;
  474. padding: 30rpx 0;
  475. color: #ffac07;
  476. font-size: 28rpx;
  477. }
  478. .loading-text {
  479. margin-top: 10rpx;
  480. color: #ffac07;
  481. font-size: 26rpx;
  482. }
  483. .order-nomore {
  484. text-align: center;
  485. color: #bbb;
  486. font-size: 24rpx;
  487. padding: 20rpx 0 40rpx 0;
  488. }
  489. .nomore-text {
  490. color: #bbb;
  491. font-size: 24rpx;
  492. }
  493. </style>