logisticsDetail.vue 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. <template>
  2. <el-drawer v-model="visible" title="物流信息" size="38%" direction="rtl" :before-close="handleClose" :close-on-click-modal="true">
  3. <div class="logistics-detail">
  4. <div class="section-title">单号查询</div>
  5. <el-form :model="form" label-width="80px">
  6. <el-form-item label="物流单号">
  7. <el-select v-model="form.selectedLogisticNo" placeholder="请选择物流单号" @change="handleLogisticNoChange" style="width: 100%">
  8. <el-option
  9. v-for="item in logisticsList"
  10. :key="item.id"
  11. :label="`${item.logisticNo},${getDictLabel(deliver_method, item.deliverMethod)}`"
  12. :value="item.logisticNo"
  13. />
  14. </el-select>
  15. </el-form-item>
  16. </el-form>
  17. <div class="section-title">物流信息</div>
  18. <el-timeline v-if="logisticsInfo.length > 0">
  19. <el-timeline-item v-for="(item, index) in logisticsInfo" :key="index" :timestamp="item.time" placement="top">
  20. <div class="timeline-content">
  21. <div class="timeline-status">{{ index + 1 }}</div>
  22. <div class="timeline-detail">
  23. <div>{{ item.time }}</div>
  24. <div>{{ item.location }}</div>
  25. <div>{{ item.status }}</div>
  26. </div>
  27. </div>
  28. </el-timeline-item>
  29. </el-timeline>
  30. <el-empty v-else description="暂无物流信息" />
  31. </div>
  32. </el-drawer>
  33. </template>
  34. <script setup lang="ts">
  35. import { listOrderDeliver, queryTrack } from '@/api/order/orderDeliver';
  36. import { OrderDeliverVO } from '@/api/order/orderDeliver/types';
  37. interface Props {
  38. modelValue: boolean;
  39. orderId?: string | number;
  40. }
  41. interface LogisticsInfo {
  42. time: string;
  43. location: string;
  44. status: string;
  45. }
  46. const props = defineProps<Props>();
  47. const emit = defineEmits(['update:modelValue']);
  48. const { proxy } = getCurrentInstance() as ComponentInternalInstance;
  49. const { deliver_method } = toRefs<any>(proxy?.useDict('deliver_method'));
  50. const visible = ref(false);
  51. const logisticsList = ref<OrderDeliverVO[]>([]);
  52. const form = ref({
  53. selectedLogisticNo: ''
  54. });
  55. const logisticsInfo = ref<LogisticsInfo[]>([]);
  56. watch(
  57. () => props.modelValue,
  58. (val) => {
  59. visible.value = val;
  60. if (val && props.orderId) {
  61. loadLogisticsList();
  62. }
  63. }
  64. );
  65. watch(visible, (val) => {
  66. emit('update:modelValue', val);
  67. });
  68. const loadLogisticsList = async () => {
  69. try {
  70. const res = await listOrderDeliver({
  71. orderId: props.orderId,
  72. pageNum: 1,
  73. pageSize: 100
  74. });
  75. logisticsList.value = res.rows || [];
  76. if (logisticsList.value.length > 0) {
  77. form.value.selectedLogisticNo = logisticsList.value[0].logisticNo;
  78. handleLogisticNoChange(form.value.selectedLogisticNo);
  79. }
  80. } catch (error) {
  81. console.error('Failed to load logistics list:', error);
  82. }
  83. };
  84. const handleLogisticNoChange = async (logisticNo: string) => {
  85. const selected = logisticsList.value.find((item) => item.logisticNo === logisticNo);
  86. if (!selected) return;
  87. try {
  88. const res = await queryTrack({
  89. logisticNo: logisticNo,
  90. pageNum: 1,
  91. pageSize: 100
  92. });
  93. if (res.data && Array.isArray(res.data) && res.data.length > 0) {
  94. logisticsInfo.value = res.data.map((item: any) => ({
  95. time: item.acceptTime || item.time || '',
  96. location: selected.orderCode ? `${selected.orderCode}` : '',
  97. status: item.context || ''
  98. }));
  99. } else {
  100. logisticsInfo.value = [
  101. {
  102. time: (selected as any).createTime || '',
  103. location: selected.orderCode ? `${selected.orderCode}` : '',
  104. status: '已下单'
  105. }
  106. ];
  107. }
  108. } catch (error) {
  109. console.error('Failed to query track:', error);
  110. logisticsInfo.value = [
  111. {
  112. time: (selected as any).createTime || '',
  113. location: selected.orderCode ? `${selected.orderCode}` : '',
  114. status: '已下单'
  115. }
  116. ];
  117. }
  118. };
  119. const getDictLabel = (dictOptions: any[], value: string) => {
  120. if (!dictOptions || !value) return value;
  121. const dict = dictOptions.find((item) => item.value === value);
  122. return dict ? dict.label : value;
  123. };
  124. const handleClose = () => {
  125. visible.value = false;
  126. form.value.selectedLogisticNo = '';
  127. logisticsInfo.value = [];
  128. };
  129. </script>
  130. <style scoped lang="scss">
  131. .logistics-detail {
  132. .section-title {
  133. font-size: 16px;
  134. font-weight: 500;
  135. margin-bottom: 16px;
  136. color: #303133;
  137. }
  138. .timeline-content {
  139. display: flex;
  140. align-items: flex-start;
  141. gap: 12px;
  142. .timeline-status {
  143. width: 24px;
  144. height: 24px;
  145. border-radius: 50%;
  146. background-color: #409eff;
  147. color: white;
  148. display: flex;
  149. align-items: center;
  150. justify-content: center;
  151. font-size: 12px;
  152. flex-shrink: 0;
  153. }
  154. .timeline-detail {
  155. flex: 1;
  156. div {
  157. margin-bottom: 4px;
  158. color: #606266;
  159. font-size: 14px;
  160. &:last-child {
  161. margin-bottom: 0;
  162. }
  163. }
  164. }
  165. }
  166. :deep(.el-timeline-item__timestamp) {
  167. display: none;
  168. }
  169. }
  170. </style>