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

537 lines
18 KiB

1 week ago
1 week ago
1 week ago
1 week ago
1 week ago
1 week ago
1 week ago
1 week ago
1 week ago
1 week ago
1 week ago
1 week ago
1 week ago
  1. <template>
  2. <view class="order-detail-container">
  3. <!-- 顶部导航栏 -->
  4. <view class="nav-bar" :style="{paddingTop: statusBarHeight + 'px', height: (statusBarHeight + 44) + 'px'}">
  5. <uni-icons type="left" @tap="goBack" size="24" color="#222" />
  6. <text class="nav-title">订单详情</text>
  7. <view class="nav-icons">
  8. <!-- <uni-icons type="more" size="24" color="#222" /> -->
  9. </view>
  10. </view>
  11. <view class="main-content" :style="{ paddingTop: (statusBarHeight + 44 + 30) + 'px' }">
  12. <!-- 回收信息卡片高保真参考流程卡片 -->
  13. <view class="info-card process-card">
  14. <view v-if="order.cancelReason" class="cancel-reason">{{ order.cancelReason }}</view>
  15. <view class="info-card-header">
  16. <view class="info-title-wrap">
  17. <text class="info-title">回收信息</text>
  18. <view v-if="order.statusText === '不包邮'" class="tag-nobaoyou">不包邮</view>
  19. </view>
  20. <view class="status-tag" :class="order.statusClass" style="margin-left:auto;">{{ order.statusLabel }}</view>
  21. </view>
  22. <!-- 步骤条横向卡片式 -->
  23. <view class="steps-bar">
  24. <view v-for="(step, idx) in steps" :key="step.text" :class="['step-item', {active: idx === currentStep}]">
  25. <image :src="step.icon" class="step-icon" />
  26. <view class="step-label" :class="{active: idx === currentStep}">
  27. <text class="step-num">{{ ['①','②','③','④'][idx] }}</text>
  28. <text class="step-text">{{ step.text }}</text>
  29. </view>
  30. </view>
  31. </view>
  32. <!-- 订单基础信息左对齐分隔线按钮箭头标签等细节 -->
  33. <view class="base-info">
  34. <view v-for="(item, i) in baseInfo" :key="item.label" class="base-info-row">
  35. <view class="base-label-wrap">
  36. <text class="base-label">{{ item.label }}</text>
  37. </view>
  38. <view class="base-value-wrap">
  39. <text class="base-value">{{ item.value }}</text>
  40. <text v-if="item.copy" class="copy-btn" @tap="copyText(item.value)">复制</text>
  41. <uni-icons v-if="item.arrow" type="right" size="18" color="#bbb" />
  42. </view>
  43. <view v-if="i < baseInfo.length-1" class="divider"></view>
  44. </view>
  45. </view>
  46. </view>
  47. <!-- 订单明细卡片 -->
  48. <view class="info-card detail-card">
  49. <view class="card-header-bg">
  50. <view class="card-title">订单详情</view>
  51. </view>
  52. <view class="detail-content">
  53. <template v-if="order.status === 2">
  54. <view class="detail-row">
  55. <text class="detail-label">订单编号</text>
  56. <text class="detail-value">{{ order.orderNo }}</text>
  57. </view>
  58. <view class="detail-row">
  59. <text class="detail-label">合格结算</text>
  60. <text class="detail-value orange">¥ {{ order.settleAmount || order.estimate }}</text>
  61. </view>
  62. <view class="detail-row">
  63. <text class="detail-label">运费扣除</text>
  64. <text class="detail-value">¥ {{ order.freight || 0 }}</text>
  65. </view>
  66. <view class="detail-row total-row">
  67. <text class="detail-label">结算金额</text>
  68. <text class="detail-value total">¥ {{ order.settleAmount || order.estimate }}</text>
  69. </view>
  70. <view class="divider"></view>
  71. </template>
  72. <view v-for="item in order.items" :key="item.name" class="goods-row">
  73. <image :src="item.img" class="goods-img" />
  74. <view class="goods-info">
  75. <text class="goods-name">{{ item.name }}</text>
  76. <text class="goods-desc">{{ item.desc }}</text>
  77. <view class="goods-price-row">
  78. <text class="goods-price">¥ {{ item.price }} <text class="goods-unit">/</text></text>
  79. <text class="goods-count">x{{ item.count }}</text>
  80. <text class="goods-total">¥{{ item.total }}</text>
  81. </view>
  82. </view>
  83. </view>
  84. </view>
  85. </view>
  86. <!-- 质检结果卡片 -->
  87. <view v-if="order.status === 2" class="inspect-card">
  88. <view class="inspect-header">
  89. <text class="inspect-title">质检结果</text>
  90. </view>
  91. <view class="inspect-content">
  92. <view v-for="(item, i) in qcInfo" :key="item.label" class="inspect-row">
  93. <text class="inspect-label">{{ item.label }}</text>
  94. <text class="inspect-value">{{ item.value }}</text>
  95. <view v-if="i < qcInfo.length-1" class="inspect-divider"></view>
  96. </view>
  97. <view class="inspect-btn" @tap="goToInspection">点此查看质检报告详情</view>
  98. </view>
  99. </view>
  100. </view>
  101. <!-- 底部操作按钮 -->
  102. <view v-if="order.status === 0" class="footer-btns">
  103. <button class="btn-outline">驳回</button>
  104. <button class="btn-main">通过</button>
  105. </view>
  106. <view v-else-if="order.status === 1" class="footer-btns">
  107. <button class="btn-main" @tap="goToInspect">开始质检</button>
  108. </view>
  109. </view>
  110. </template>
  111. <script>
  112. import pullRefreshMixin from '../mixins/pullRefreshMixin.js'
  113. export default {
  114. mixins: [pullRefreshMixin],
  115. data() {
  116. return {
  117. statusBarHeight: 0,
  118. order: {
  119. status: 3, // 0:已预约, 1:待质检, 2:已结款, 3:已驳回, 4:已取消
  120. statusText: '已结款',
  121. statusLabel: '已结款',
  122. statusClass: 'settled',
  123. cancelReason: '', // 如有取消原因
  124. estimate: '73.6~75.8',
  125. qualityTime: '2025-03-20 11:00',
  126. orderNo: 'RE82738127861525',
  127. userName: '周小艺',
  128. userPhone: '13877881234',
  129. address: '海南省海口市秀英区秀英街道5单元183室',
  130. items: [
  131. { name: '羽绒服', desc: '允许脏破烂,160码以上', price: 8, count: 8, total: 64, img: '/static/coat1.png' },
  132. { name: '品牌羽绒服', desc: '允许脏破烂,160码以上', price: 10, count: 8, total: 8, img: '/static/coat2.png' }
  133. ]
  134. },
  135. steps: [],
  136. currentStep: 3, // 第4步高亮
  137. baseInfo: [
  138. // { label: '取消原因', value: this.order.cancelReason, show: !!this.order.cancelReason },
  139. { label: '订单编号', value: 'RE82738127861525', copy: true },
  140. { label: '用户名', value: '周小艺', arrow: true },
  141. { label: '用户电话', value: this.maskPhone('13877881234'), copy: true },
  142. { label: '取件地址', value: '海南省海口市秀英区秀英街道5单元183室' },
  143. { label: '质检完成时间', value: '2025-03-20 11:00' }
  144. ],
  145. qcInfo: [
  146. { label: '质检数量', value: '9 件' },
  147. { label: '质检合格', value: '7 件' },
  148. { label: '质量问题', value: '2 件' },
  149. { label: '不可回收', value: '0 件' },
  150. { label: '订单重量', value: '2.85 kg' }
  151. ]
  152. }
  153. },
  154. onLoad() {
  155. this.statusBarHeight = uni.getSystemInfoSync().statusBarHeight
  156. // 接收从订单列表页面传递过来的订单详情数据
  157. const eventChannel = this.getOpenerEventChannel()
  158. eventChannel.on('orderDetail', (data) => {
  159. this.order = data
  160. this.currentStep = this.getCurrentStep(data.status)
  161. console.log(this.order);
  162. })
  163. this.getAreaList();
  164. },
  165. methods: {
  166. async onRefresh() {
  167. await this.getAreaList()
  168. // 如有订单详情接口,也可在此处await
  169. },
  170. goBack() {
  171. uni.navigateBack()
  172. },
  173. copyText(text) {
  174. uni.setClipboardData({ data: text })
  175. },
  176. maskPhone(phone) {
  177. if (!phone) return '';
  178. return phone.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2');
  179. },
  180. getCurrentStep(status) {
  181. // 0:已预约, 1:待质检, 2:已结款, 3:已驳回, 4:已取消
  182. switch (status) {
  183. case 0: // 已预约
  184. return 0;
  185. case 1: // 待质检
  186. case 3: // 已驳回
  187. return 2;
  188. case 2: // 已结款
  189. return 3;
  190. case 4: // 已取消
  191. return 1;
  192. default:
  193. return 0;
  194. }
  195. },
  196. goToInspect() {
  197. uni.navigateTo({
  198. url: '/pages/manager/inspect'
  199. })
  200. },
  201. getAreaList() {
  202. this.$api('getAreaList', {}, (res) => {
  203. if (res.code == 200 && Array.isArray(res.result)) {
  204. // 按sort升序排序
  205. const sorted = res.result.slice().sort((a, b) => a.sort - b.sort)
  206. this.steps = sorted.map(item => ({ text: item.title, icon: item.image }))
  207. }
  208. })
  209. }
  210. }
  211. }
  212. </script>
  213. <style lang="scss" scoped>
  214. $order-card-radius: 32px;
  215. $order-card-padding: 40px 28px;
  216. .order-detail-container {
  217. background: #f8f8f8;
  218. min-height: 100vh;
  219. font-family: 'PingFang SC', 'Microsoft YaHei', Arial, sans-serif;
  220. display: flex;
  221. flex-direction: column;
  222. align-items: center;
  223. .nav-bar {
  224. position: fixed;
  225. top: 0; left: 0; right: 0;
  226. z-index: 100;
  227. background: #fff;
  228. display: flex;
  229. align-items: center;
  230. justify-content: space-between;
  231. .nav-title { flex: 1; text-align: center; font-size: 18px; font-weight: bold; }
  232. .nav-icons { display: flex; align-items: center; gap: 16px; }
  233. }
  234. .main-content {
  235. width: 100vw;
  236. min-width: 0;
  237. box-sizing: border-box;
  238. padding: 0 16px 60px 16px;
  239. display: flex;
  240. flex-direction: column;
  241. align-items: center;
  242. }
  243. .info-card {
  244. width: 100%;
  245. max-width: 375px;
  246. min-width: 0;
  247. box-sizing: border-box;
  248. margin: 0 auto 28px auto;
  249. padding: 16px 8px;
  250. background: #fff;
  251. border-radius: 20px;
  252. box-shadow: 0 2px 12px rgba(0,0,0,0.04);
  253. // color: blue;
  254. &.process-card {
  255. position: relative;
  256. .status-tag {
  257. position: absolute;
  258. right: 18px;
  259. top: 18px;
  260. font-size: 13px;
  261. border-radius: 12px;
  262. padding: 2px 10px;
  263. font-weight: 500;
  264. background: #e6f9e6;
  265. color: #1ecb1e;
  266. height: 22px;
  267. line-height: 18px;
  268. display: flex;
  269. align-items: center;
  270. &.orange {
  271. background: #fff7e6;
  272. color: #ffb400;
  273. font-size: 14px;
  274. border-radius: 12px;
  275. padding: 2px 14px;
  276. font-weight: 400;
  277. height: 22px;
  278. display: flex;
  279. align-items: center;
  280. }
  281. &.gray {
  282. background: #f5f5f5;
  283. color: #444;
  284. font-size: 14px;
  285. border-radius: 12px;
  286. padding: 2px 14px;
  287. font-weight: 400;
  288. height: 22px;
  289. display: flex;
  290. align-items: center;
  291. }
  292. &.blue {
  293. background: #eaf6ff;
  294. color: #2a9cfb;
  295. font-size: 14px;
  296. border-radius: 12px;
  297. padding: 2px 14px;
  298. font-weight: 400;
  299. height: 22px;
  300. display: flex;
  301. align-items: center;
  302. }
  303. }
  304. .status-tag.red {
  305. background: #fff0f0;
  306. color: #ff4d4f;
  307. }
  308. }
  309. .info-card-header {
  310. display: flex; align-items: center; justify-content: flex-start;
  311. .info-title-wrap {
  312. display: flex;
  313. align-items: center;
  314. .info-title { font-size: 18px; font-weight: bold; color: #222; }
  315. .tag-nobaoyou {
  316. margin-left: 8px;
  317. background: #fff0f0;
  318. color: #ff4d4f;
  319. font-size: 14px;
  320. border-radius: 12px;
  321. padding: 2px 14px;
  322. font-weight: 400;
  323. height: 22px;
  324. display: flex;
  325. align-items: center;
  326. }
  327. }
  328. .status-tag { margin-left: auto; }
  329. }
  330. .steps-bar {
  331. display: flex;
  332. justify-content: flex-start;
  333. align-items: flex-start;
  334. margin: 24px 0 24px 0;
  335. gap: 14px;
  336. .step-item {
  337. background: #fff8ea;
  338. border-radius: 16px;
  339. width: 76px;
  340. height: 76px;
  341. display: flex;
  342. flex-direction: column;
  343. align-items: center;
  344. box-shadow: 0 2px 8px rgba(255, 156, 0, 0.04);
  345. overflow: hidden;
  346. position: relative;
  347. .step-icon { width: 44px; height: 44px; margin: 6px 0 0 0; }
  348. .step-label {
  349. width: 100%; height: 28px; background: transparent; position: absolute; left: 0; bottom: 0; border-radius: 0 0 12px 12px; display: flex; align-items: center; justify-content: center; transition: background 0.2s; overflow: hidden;
  350. line-height: 1;
  351. .step-num, .step-text {
  352. font-size: 11px;
  353. color: #9b9b9b;
  354. font-weight: 600;
  355. line-height: 1;
  356. vertical-align: middle;
  357. }
  358. .step-num { margin-right: 2px; }
  359. }
  360. &.active .step-label { background: linear-gradient(90deg, #ffd01e 0%, #ff8917 100%); }
  361. &.active .step-num, &.active .step-text { color: #fff; font-weight: bold; }
  362. }
  363. }
  364. .base-info {
  365. margin-top: 18px;
  366. .base-info-row {
  367. display: flex;
  368. align-items: flex-start;
  369. padding: 0 0 0 0;
  370. position: relative;
  371. min-height: 36px;
  372. justify-content: space-between;
  373. .base-label-wrap { min-width: 80px; text-align: left; color: #8b8b8b; font-size: 13px;}
  374. .base-value-wrap { display: flex; align-items: center; flex: 1; justify-content: flex-end; }
  375. .base-value { color: #222; font-size: 15px; font-weight: 600; word-break: break-all; text-align: right; }
  376. .copy-btn { color: #ffb400; font-size: 13px; margin-left: 6px; font-weight: 400; padding: 1px 6px; border-radius: 6px; transition: background 0.2s; }
  377. .copy-btn:active { background: #fff7e6; }
  378. .divider { position: absolute; left: 16px; right: 16px; bottom: -1px; height: 1px; background: #f0f0f0; }
  379. uni-icons { margin-left: 6px; }
  380. }
  381. .base-info-row .base-value-wrap { flex-wrap: wrap; }
  382. .base-info-row .base-value { white-space: pre-line; line-height: 1.5; }
  383. }
  384. &.detail-card {
  385. position: relative;
  386. padding: 0;
  387. background: transparent;
  388. .card-header-bg {
  389. height: 56px;
  390. background: linear-gradient(180deg, #fff7e6 0%, #fff 100%);
  391. border-radius: 20px 20px 0 0;
  392. display: flex;
  393. align-items: flex-end;
  394. padding-left: 20px;
  395. padding-top: 12px;
  396. }
  397. .detail-content {
  398. background: #fff;
  399. border-radius: 0 0 20px 20px;
  400. padding: 20px 16px 16px 16px;
  401. }
  402. .detail-row {
  403. display: flex;
  404. align-items: center;
  405. min-height: 32px;
  406. .detail-label {
  407. color: #bcbcbc;
  408. font-size: 14px;
  409. width: 90px;
  410. flex-shrink: 0;
  411. }
  412. .detail-value {
  413. color: #222;
  414. font-size: 15px;
  415. font-weight: 500;
  416. text-align: left;
  417. margin-left: 0;
  418. flex: 1;
  419. }
  420. &.total-row .detail-value.total {
  421. color: #ffb400;
  422. font-size: 20px;
  423. font-weight: bold;
  424. text-align: right;
  425. flex: unset;
  426. margin-left: auto;
  427. min-width: 100px;
  428. }
  429. .orange { color: #ffb400; }
  430. }
  431. .divider { height: 1px; background: #f0f0f0; margin: 16px 0; }
  432. .goods-row { display: flex; align-items: flex-start; margin-bottom: 18px; }
  433. .goods-img { width: 48px; height: 48px; border-radius: 12px; margin-right: 12px; }
  434. .goods-info { flex: 1; }
  435. .goods-name { font-size: 15px; font-weight: bold; color: #222; margin-bottom: 2px; }
  436. .goods-desc { font-size: 13px; color: #bbb; margin-bottom: 8px; }
  437. .goods-price-row { display: flex; align-items: center; }
  438. .goods-price { color: #ffb400; font-size: 15px; font-weight: bold; margin-right: 8px; }
  439. .goods-unit { font-size: 13px; color: #bbb; }
  440. .goods-count { color: #bbb; font-size: 13px; margin-left: 8px; }
  441. .goods-total { color: #222; font-size: 15px; font-weight: bold; margin-left: auto; }
  442. }
  443. }
  444. .footer-btns {
  445. position: fixed; left: 0; right: 0; bottom: 0; background: #fff; display: flex; gap: 16px; padding: 16px 24px 32px 24px; z-index: 101;
  446. .btn-outline {
  447. flex: 1;
  448. height: 40px;
  449. border-radius: 16px;
  450. border: 1px solid #ffe09a;
  451. color: #ffb400;
  452. background: #fff0d2;
  453. font-size: 15px;
  454. font-weight: 500;
  455. box-shadow: none;
  456. padding: 0 18px;
  457. }
  458. .btn-main {
  459. flex: 1;
  460. height: 40px;
  461. border-radius: 16px;
  462. background: linear-gradient(90deg, #ffbe3d 0%, #ffac04 100%);
  463. color: #fff;
  464. border: none;
  465. font-size: 15px;
  466. font-weight: 500;
  467. box-shadow: none;
  468. padding: 0 18px;
  469. }
  470. }
  471. .cancel-reason {
  472. color: #bbb;
  473. font-size: 13px;
  474. margin-bottom: 10px;
  475. padding-left: 2px;
  476. }
  477. .inspect-card {
  478. max-width: 335px;
  479. width: 100%;
  480. margin: 0 auto 28px auto;
  481. border-radius: 24px;
  482. overflow: hidden;
  483. .inspect-header {
  484. height: 60px;
  485. background: linear-gradient(180deg, #fff7e6 0%, #fffbe6 100%);
  486. border-radius: 24px 24px 0 0;
  487. display: flex;
  488. align-items: flex-end;
  489. padding-left: 24px;
  490. }
  491. .inspect-title {
  492. font-size: 20px;
  493. font-weight: bold;
  494. color: #222;
  495. line-height: 60px;
  496. }
  497. .inspect-content {
  498. background: #fff;
  499. border-radius: 0 0 24px 24px;
  500. padding: 24px 24px 20px 24px;
  501. }
  502. .inspect-row {
  503. display: flex; align-items: center; justify-content: space-between;
  504. min-height: 40px;
  505. .inspect-label { color: #bcbcbc; font-size: 16px; }
  506. .inspect-value { color: #222; font-size: 16px; font-weight: 500; }
  507. .inspect-divider { height: 1px; background: #f0f0f0; margin: 0; width: 100%; position: absolute; left: 0; bottom: 0; }
  508. position: relative;
  509. }
  510. .inspect-btn {
  511. width: 100%;
  512. margin: 24px 0 0 0;
  513. height: 48px;
  514. border-radius: 24px;
  515. border: 1.5px solid #ffb400;
  516. color: #ffb400;
  517. background: #fff0d2;
  518. font-size: 18px;
  519. font-weight: 500;
  520. box-shadow: none;
  521. text-align: center;
  522. line-height: 48px;
  523. }
  524. }
  525. .card-title {
  526. font-size: 20px;
  527. font-weight: bold;
  528. color: #222;
  529. margin-bottom: 12px;
  530. }
  531. }
  532. </style>