index.vue 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377
  1. <template>
  2. <view class="">
  3. <!-- 物流头部信息 -->
  4. <view class="logistics-header">
  5. <view>
  6. <view class="company-info mar-b-16">
  7. <u-icon name="car" size="25" color="#8b8b8b" class="logistics-header-icon"></u-icon>
  8. <text class="label">物流公司:</text>
  9. <text class="value">{{ order.expressCompany ||'-'}}</text>
  10. </view>
  11. <view class="tracking-info mar-b-16">
  12. <u-icon name="order" size="25" color="#8b8b8b" class="logistics-header-icon"></u-icon>
  13. <text class="label">单号:</text>
  14. <text class="value">{{ order.expressCode ||'-'}}</text>
  15. <text class="copy-btn" v-if="order.expressCode" @tap="copyContent">复制</text>
  16. </view>
  17. <view class="company-info mar-b-16 ">
  18. <u-icon name="coupon" size="25" color="#8b8b8b" class="logistics-header-icon"></u-icon>
  19. <text class="label">发货备注:</text>
  20. <text class="value">{{ order.deliveryRemark ||'-'}}</text>
  21. </view>
  22. </view>
  23. </view>
  24. <!-- 物流轨迹列表 -->
  25. <view class="logistics-trace" :key="stepsKey">
  26. <template v-if="logisticsList && logisticsList.length">
  27. <uni-steps :options="logisticsList" direction="column" :active="0"></uni-steps>
  28. </template>
  29. <template v-else>
  30. <view class="no-data">暂无物流信息</view>
  31. </template>
  32. </view>
  33. </view>
  34. </template>
  35. <script>
  36. import {
  37. queryExpress,
  38. orderDetail
  39. } from '@/config/api.js'
  40. export default {
  41. data() {
  42. return {
  43. stepsKey: 0,
  44. logisticsCompany: '',
  45. logisticsNumber: '',
  46. logisticsList: [],
  47. order: {},
  48. stats: null,
  49. orderId: "",
  50. };
  51. },
  52. methods: {
  53. copyContent() {
  54. uni.setClipboardData({
  55. data: this.logisticsNumber,
  56. success: () => {
  57. uni.showToast({
  58. title: '复制成功',
  59. icon: 'none'
  60. });
  61. }
  62. });
  63. },
  64. async getLogisticsInfo() {
  65. try {
  66. const params = {
  67. number: this.order.expressCode
  68. };
  69. // 如果是顺丰快递,使用lastFour字段
  70. if (this.order.expressCompany === '顺丰快递' && this.order.lastFour) {
  71. params.mobile = this.order.lastFour;
  72. }
  73. const res = await queryExpress(params);
  74. if (res.code === 200) {
  75. const data = JSON.parse(res.data).data;
  76. console.log("res", res)
  77. this.logisticsCompany = data.expressCompanyName;
  78. // 转换物流轨迹数据格式为uview-plus steps所需格式
  79. if (data.logisticsTraceDetails && Array.isArray(data.logisticsTraceDetails)) {
  80. this.logisticsList = data.logisticsTraceDetails.reverse().map(item => ({
  81. title: item.desc || '',
  82. desc: item.time ? this.formatTimestamp(item.time) : ''
  83. }));
  84. this.stepsKey++
  85. } else {
  86. this.logisticsList = [];
  87. }
  88. } else {
  89. this.logisticsList = [];
  90. uni.showToast({
  91. title: '获取物流信息失败',
  92. icon: 'none'
  93. });
  94. }
  95. } catch (e) {
  96. this.logisticsList = [];
  97. uni.showToast({
  98. title: '获取物流信息失败',
  99. icon: 'none'
  100. });
  101. }
  102. },
  103. formatTimestamp(timestamp) {
  104. const date = new Date(Number(timestamp));
  105. const year = date.getFullYear();
  106. const month = String(date.getMonth() + 1).padStart(2, '0');
  107. const day = String(date.getDate()).padStart(2, '0');
  108. const hours = String(date.getHours()).padStart(2, '0');
  109. const minutes = String(date.getMinutes()).padStart(2, '0');
  110. return `${year}-${month}-${day} ${hours}:${minutes}`;
  111. },
  112. previewImage(current, urls) {
  113. uni.previewImage({
  114. current,
  115. urls
  116. });
  117. },
  118. async getDeliveryStats() {
  119. try {
  120. const res = await deliveryStats({
  121. workOrderId: this.order.id
  122. });
  123. if (res.code === 200) {
  124. this.stats = res.data;
  125. } else {
  126. uni.showToast({
  127. title: '获取发货统计信息失败',
  128. icon: 'none'
  129. });
  130. }
  131. } catch (e) {
  132. uni.showToast({
  133. title: '获取发货统计信息失败',
  134. icon: 'none'
  135. });
  136. }
  137. },
  138. goToDeliverRecord() {
  139. uni.navigateTo({
  140. url: `/pagesA/production/deliver-record?workOrderId=${this.order.id}`
  141. });
  142. },
  143. async getOrderDetail() {
  144. try {
  145. const res = await orderDetail({
  146. id: this.orderId
  147. })
  148. if (res.success && res.data) {
  149. this.order = res.data.order
  150. this.orderListVos = res.data.orderListVos || []
  151. this.getLogisticsInfo();
  152. }
  153. } catch (e) {
  154. this.$u.toast('获取订单详情失败')
  155. }
  156. },
  157. },
  158. onLoad(op) {
  159. this.orderId = op.id;
  160. this.getOrderDetail()
  161. }
  162. };
  163. </script>
  164. <style scoped>
  165. .contact-section {
  166. position: fixed;
  167. left: 0;
  168. right: 0;
  169. bottom: 0;
  170. background: #fff;
  171. padding: 20rpx 30rpx;
  172. display: flex;
  173. justify-content: space-around;
  174. .contact-item {
  175. display: flex;
  176. flex-direction: column;
  177. align-items: center;
  178. padding: 20rpx;
  179. text {
  180. font-size: 24rpx;
  181. color: #666;
  182. margin-top: 10rpx;
  183. }
  184. }
  185. }
  186. .container {
  187. padding: 20rpx;
  188. background-color: #f8f8f8;
  189. }
  190. .logistics-header {
  191. background-color: #ffffff;
  192. border-radius: 10rpx;
  193. padding: 32rpx;
  194. box-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.1);
  195. margin-bottom: 20rpx;
  196. }
  197. .company-info,
  198. .tracking-info {
  199. display: flex;
  200. align-items: center;
  201. }
  202. .label {
  203. color: #666;
  204. font-size: 28rpx;
  205. margin-right: 10rpx;
  206. }
  207. .value {
  208. color: #333;
  209. font-size: 28rpx;
  210. font-weight: 500;
  211. }
  212. .copy-btn {
  213. background: #229fff;
  214. color: #fff;
  215. border-radius: 20rpx;
  216. padding: 6rpx 22rpx;
  217. font-size: 26rpx;
  218. margin-left: 8rpx;
  219. box-shadow: 0 2rpx 6rpx rgba(34, 159, 255, 0.08);
  220. cursor: pointer;
  221. transition: background 0.2s;
  222. }
  223. .copy-btn:active {
  224. background-color: #e5e5e5;
  225. }
  226. .logistics-trace {
  227. min-height: calc(100vh - 20rpx - 32rpx - 20rpx - 20rpx);
  228. /* 减去容器内边距、头部内边距、头部底部外边距和自身内边距 */
  229. background-color: #ffffff;
  230. border-radius: 10rpx;
  231. padding: 20rpx;
  232. box-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.1);
  233. }
  234. .steps-item {
  235. padding: 20rpx 0;
  236. }
  237. .steps-title {
  238. font-size: 28rpx;
  239. color: #333;
  240. margin-bottom: 10rpx;
  241. }
  242. .steps-time {
  243. font-size: 24rpx;
  244. color: #999;
  245. }
  246. .no-data {
  247. text-align: center;
  248. padding: 40rpx;
  249. color: #999;
  250. font-size: 28rpx;
  251. }
  252. .goods-photos {
  253. padding: 20rpx;
  254. background-color: #fff;
  255. border-radius: 12rpx;
  256. }
  257. .photo-section {
  258. margin-bottom: 30rpx;
  259. display: flex;
  260. flex-direction: column;
  261. }
  262. .photo-section:last-child {
  263. margin-bottom: 0;
  264. }
  265. .photo-section .section-title {
  266. font-size: 28rpx;
  267. color: #333;
  268. font-weight: bold;
  269. margin-bottom: 20rpx;
  270. }
  271. .photo-section .photo-grid {
  272. display: flex;
  273. flex-wrap: nowrap;
  274. gap: 20rpx;
  275. overflow-x: auto;
  276. padding-bottom: 10rpx;
  277. }
  278. .photo-section .photo-grid .photo-item {
  279. width: 200rpx;
  280. height: 200rpx;
  281. flex-shrink: 0;
  282. border-radius: 8rpx;
  283. background-color: #f5f5f5;
  284. }
  285. .delivery-stats {
  286. background-color: #ffffff;
  287. border-radius: 10rpx;
  288. padding: 20rpx;
  289. box-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.1);
  290. margin-bottom: 20rpx;
  291. }
  292. .stats-header {
  293. display: flex;
  294. justify-content: space-between;
  295. align-items: center;
  296. padding-bottom: 20rpx;
  297. border-bottom: 1rpx solid #eee;
  298. }
  299. .stats-title {
  300. font-size: 30rpx;
  301. font-weight: bold;
  302. color: #333;
  303. }
  304. .stats-content {
  305. padding-top: 20rpx;
  306. display: grid;
  307. grid-template-columns: repeat(2, 1fr);
  308. gap: 20rpx;
  309. }
  310. .stats-item {
  311. display: flex;
  312. flex-direction: column;
  313. align-items: center;
  314. padding: 10rpx;
  315. }
  316. .stats-label {
  317. color: #666;
  318. font-size: 26rpx;
  319. margin-bottom: 8rpx;
  320. }
  321. .stats-value {
  322. color: #229fff;
  323. font-size: 32rpx;
  324. font-weight: bold;
  325. }
  326. .record-btn {
  327. display: flex;
  328. align-items: center;
  329. background: #ecf5ff;
  330. padding: 10rpx 20rpx;
  331. border-radius: 30rpx;
  332. cursor: pointer;
  333. }
  334. .record-btn text {
  335. color: #229fff;
  336. font-size: 26rpx;
  337. margin-left: 8rpx;
  338. }
  339. </style>