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

10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
9 months ago
10 months ago
9 months ago
10 months ago
9 months ago
10 months ago
10 months ago
9 months ago
10 months ago
9 months ago
10 months ago
9 months ago
10 months ago
9 months ago
10 months ago
9 months ago
10 months ago
9 months ago
10 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
10 months ago
9 months ago
10 months ago
9 months ago
10 months ago
9 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
9 months ago
10 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.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 (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>