<template>
|
<div class="app-container">
|
<el-form :model="searchForm" :inline="true">
|
<el-form-item label="关键字">
|
<el-input
|
v-model="searchForm.keyword"
|
style="width: 240px"
|
placeholder="订单号/承运商/车牌"
|
clearable
|
:prefix-icon="Search"
|
@keyup.enter="handleQuery"
|
/>
|
</el-form-item>
|
<el-form-item label="发货日期">
|
<el-date-picker
|
v-model="searchForm.shipDateRange"
|
value-format="YYYY-MM-DD"
|
format="YYYY-MM-DD"
|
type="daterange"
|
start-placeholder="开始日期"
|
end-placeholder="结束日期"
|
clearable
|
@change="changeDateRange"
|
@clear="clearRange"
|
/>
|
</el-form-item>
|
|
<el-form-item>
|
<el-button type="primary" @click="handleQuery" style="margin-left: 10px">
|
搜索
|
</el-button>
|
<el-button @click="resetQuery">重置</el-button>
|
</el-form-item>
|
</el-form>
|
|
<div class="table_list">
|
<PIMTable
|
rowKey="id"
|
:column="tableColumn"
|
:tableData="tableData"
|
:page="page"
|
:isSelection="false"
|
:tableLoading="tableLoading"
|
@pagination="pagination"
|
:total="page.total"
|
>
|
<template #statusSlot="{ row }">
|
<el-tag :type="trackStatusTagType(row.trackStatus)">
|
{{ trackStatusText(row.trackStatus) }}
|
</el-tag>
|
</template>
|
</PIMTable>
|
</div>
|
|
<!-- 轨迹查看弹窗 -->
|
<el-dialog
|
v-model="trackVisible"
|
title="在途轨迹查询"
|
width="760px"
|
:close-on-click-modal="false"
|
destroy-on-close
|
>
|
<div style="margin-bottom: 10px; color: #606266">
|
<div>订单号:{{ currentRow.orderCode }}</div>
|
<div>承运商:{{ currentRow.carrierName }} 车牌:{{ currentRow.vehicleNo }}</div>
|
<div>发货时间:{{ currentRow.shipTime || "-" }}</div>
|
</div>
|
|
<el-empty v-if="trackLoading" description="加载轨迹中..." />
|
<el-empty v-else-if="trackList.length === 0" description="暂无轨迹" />
|
|
<el-timeline v-else>
|
<el-timeline-item
|
v-for="item in trackList"
|
:key="item.id"
|
:timestamp="item.time"
|
:type="item.type"
|
:hollow="false"
|
>
|
<div style="font-weight: 600">{{ item.title }}</div>
|
<div style="color: #909399">{{ item.remark }}</div>
|
</el-timeline-item>
|
</el-timeline>
|
|
<template #footer>
|
<el-button @click="trackVisible = false">关闭</el-button>
|
</template>
|
</el-dialog>
|
</div>
|
</template>
|
|
<script setup>
|
import { ref, reactive, onMounted } from "vue";
|
import { Search } from "@element-plus/icons-vue";
|
import { ElMessage, ElMessageBox } from "element-plus";
|
import PIMTable from "@/components/PIMTable/PIMTable.vue";
|
import {
|
getDeliveryTrackPage,
|
getDeliveryTrackDetail,
|
deleteDeliveryTrack,
|
} from "@/api/inventoryManagement/CarrierManagement";
|
|
// ------------------ 轨迹(暂时假数据:等后端轨迹接口字段确定后替换) ------------------
|
const pad2 = (n) => String(n).padStart(2, "0");
|
const formatDateTime = (d = new Date()) => {
|
const yyyy = d.getFullYear();
|
const mm = pad2(d.getMonth() + 1);
|
const dd = pad2(d.getDate());
|
const hh = pad2(d.getHours());
|
const mi = pad2(d.getMinutes());
|
const ss = pad2(d.getSeconds());
|
return `${yyyy}-${mm}-${dd} ${hh}:${mi}:${ss}`;
|
};
|
|
const trackStatusText = (s) => {
|
const map = { 0: "待发车", 1: "运输中", 2: "已到达", 3: "异常" };
|
return map[s] ?? "-";
|
};
|
|
const trackStatusTagType = (s) => {
|
const map = { 0: "info", 1: "warning", 2: "success", 3: "danger" };
|
return map[s] ?? "info";
|
};
|
|
const buildMockTrack = (row) => {
|
const now = Date.now();
|
const isArrived = Number(row.trackStatus) === 2;
|
const isAbnormal = Number(row.trackStatus) === 3;
|
|
const base = [
|
{
|
id: 1,
|
time: formatDateTime(new Date(now - 1000 * 60 * 60 * 18)),
|
title: "已创建发货单",
|
location: row.origin || "-",
|
remark: "已完成装车",
|
type: "primary",
|
},
|
{
|
id: 2,
|
time: formatDateTime(new Date(now - 1000 * 60 * 60 * 12)),
|
title: "车辆已发车",
|
location: row.origin || "-",
|
remark: "从仓库出发",
|
type: "success",
|
},
|
{
|
id: 3,
|
time: formatDateTime(new Date(now - 1000 * 60 * 60 * 6)),
|
title: isAbnormal ? "运输异常" : "途经节点",
|
location: "中转站",
|
remark: isAbnormal ? "车辆故障,等待处理" : "正常",
|
type: isAbnormal ? "danger" : "warning",
|
},
|
];
|
|
if (isArrived) {
|
base.push({
|
id: 4,
|
time: formatDateTime(new Date(now - 1000 * 60 * 60)),
|
title: "已到达",
|
location: row.destination || "-",
|
remark: "已签收",
|
type: "success",
|
});
|
}
|
|
return base;
|
};
|
|
// ------------------ 列表查询(来自后端 /fakeWarehousing/deliveryTrack/list) ------------------
|
// 后端样例字段:
|
// id, orderId, orderCode, carrierName, shipTime, vehicleNo, driverName, driverPhone, remark,
|
// createTime, updateTime, createUser, updateUser, tenantId
|
const normalizeRow = (raw = {}) => {
|
const id = raw.id ?? raw.trackId ?? raw.deliveryTrackId;
|
|
return {
|
id,
|
orderId: raw.orderId ?? null,
|
orderCode: raw.orderCode ?? raw.orderNo ?? "-",
|
carrierName: raw.carrierName ?? raw.carrier ?? "-",
|
shipTime: raw.shipTime ?? raw.shipDate ?? "-",
|
vehicleNo: raw.vehicleNo ?? raw.carNo ?? "-",
|
driverName: raw.driverName ?? raw.driver ?? "-",
|
driverPhone: raw.driverPhone ?? raw.driverTel ?? "-",
|
remark: raw.remark ?? "",
|
createTime: raw.createTime ?? "-",
|
updateTime: raw.updateTime ?? raw.modifyTime ?? "-",
|
};
|
};
|
|
const tableData = ref([]);
|
const tableLoading = ref(false);
|
|
const page = reactive({
|
current: 1,
|
size: 10,
|
total: 0,
|
layout: "total, sizes, prev, pager, next, jumper",
|
});
|
|
const searchForm = reactive({
|
keyword: "",
|
trackStatus: "",
|
shipDateRange: [],
|
shipDateStart: undefined,
|
shipDateEnd: undefined,
|
});
|
|
const tableColumn = ref([
|
{ label: "订单号", prop: "orderCode", width: 180 },
|
{ label: "承运商", prop: "carrierName", width: 180 },
|
{ label: "车牌号", prop: "vehicleNo", width: 130 },
|
{ label: "发货时间", prop: "shipTime", width: 170 },
|
{ label: "司机", prop: "driverName", width: 120 },
|
{ label: "司机电话", prop: "driverPhone", width: 140 },
|
{ label: "更新时间", prop: "updateTime", width: 170 },
|
{
|
label: "操作",
|
prop: "action",
|
dataType: "action",
|
fixed: "right",
|
width: 120,
|
operation: [
|
{ name: "查看轨迹", clickFun: (row) => openTrack(row) },
|
{ name: "删除", clickFun: (row) => handleDelete(row) },
|
],
|
},
|
]);
|
|
const buildListParams = () => {
|
return {
|
current: page.current,
|
size: page.size,
|
keyword: searchForm.keyword || undefined,
|
orderCode: searchForm.keyword || undefined,
|
carrierName: searchForm.keyword || undefined,
|
vehicleNo: searchForm.keyword || undefined,
|
trackStatus:
|
searchForm.trackStatus === "" || searchForm.trackStatus === null || searchForm.trackStatus === undefined
|
? undefined
|
: searchForm.trackStatus,
|
startDate: searchForm.shipDateStart || undefined,
|
endDate: searchForm.shipDateEnd || undefined,
|
};
|
};
|
|
const getList = async () => {
|
tableLoading.value = true;
|
try {
|
const res = await getDeliveryTrackPage(buildListParams());
|
const data = res?.data ?? res;
|
const records = data?.records ?? data?.rows ?? data?.list ?? [];
|
const total = data?.total ?? data?.count ?? 0;
|
|
tableData.value = (records || []).map(normalizeRow);
|
page.total = total;
|
} catch (e) {
|
ElMessage.error(e?.message || e?.msg || "加载失败");
|
} finally {
|
tableLoading.value = false;
|
}
|
};
|
|
const handleQuery = () => {
|
page.current = 1;
|
getList();
|
};
|
|
const resetQuery = () => {
|
searchForm.keyword = "";
|
searchForm.trackStatus = "";
|
searchForm.shipDateRange = [];
|
searchForm.shipDateStart = undefined;
|
searchForm.shipDateEnd = undefined;
|
page.current = 1;
|
getList();
|
};
|
|
const pagination = (obj) => {
|
page.current = obj.page;
|
page.size = obj.limit;
|
getList();
|
};
|
|
const changeDateRange = (date) => {
|
if (date && date.length === 2) {
|
searchForm.shipDateStart = date[0];
|
searchForm.shipDateEnd = date[1];
|
} else {
|
searchForm.shipDateStart = undefined;
|
searchForm.shipDateEnd = undefined;
|
}
|
getList();
|
};
|
|
const clearRange = () => {
|
searchForm.shipDateRange = [];
|
searchForm.shipDateStart = undefined;
|
searchForm.shipDateEnd = undefined;
|
getList();
|
};
|
|
// ------------------ 轨迹弹窗(详情调用后端 /fakeWarehousing/deliveryTrack/{id} ) ------------------
|
const trackVisible = ref(false);
|
const trackLoading = ref(false);
|
const trackList = ref([]);
|
const currentRow = reactive({});
|
|
const openTrack = async (row) => {
|
Object.assign(currentRow, row);
|
trackVisible.value = true;
|
trackLoading.value = true;
|
|
try {
|
const res = await getDeliveryTrackDetail(row.id);
|
const detail = res?.data ?? res;
|
Object.assign(currentRow, normalizeRow(detail));
|
|
// 轨迹明细字段尚未确定:暂用模拟轨迹展示
|
trackList.value = buildMockTrack(currentRow);
|
} catch (e) {
|
ElMessage.error(e?.message || e?.msg || "轨迹加载失败");
|
trackList.value = buildMockTrack(currentRow);
|
} finally {
|
trackLoading.value = false;
|
}
|
};
|
|
// 删除(批量 ids 后端也支持:这里单条删除)
|
const handleDelete = async (row) => {
|
await ElMessageBox.confirm(`确认删除发货记录【${row.orderCode || "-"}】?`, "提示", {
|
confirmButtonText: "删除",
|
cancelButtonText: "取消",
|
type: "warning",
|
});
|
|
tableLoading.value = true;
|
try {
|
await deleteDeliveryTrack(row.id);
|
ElMessage.success("删除成功");
|
|
// 删除后如果当前页为空则回退
|
const res = await getDeliveryTrackPage(buildListParams());
|
const data = res?.data ?? res;
|
const records = data?.records ?? data?.rows ?? data?.list ?? [];
|
if ((records || []).length === 0 && page.current > 1) {
|
page.current -= 1;
|
}
|
|
await getList();
|
} catch (e) {
|
ElMessage.error(e?.message || e?.msg || "删除失败");
|
} finally {
|
tableLoading.value = false;
|
}
|
};
|
|
// 预留:新增/修改接口已接入(后续加弹窗即可)
|
// addDeliveryTrack / updateDeliveryTrack
|
|
onMounted(() => {
|
getList();
|
});
|
</script>
|
|
<style scoped lang="scss">
|
.table_list {
|
margin-top: unset;
|
}
|
</style>
|