<template>
|
<div class="app-container">
|
<!-- 统计概览 -->
|
<el-row :gutter="16" style="margin-bottom: 16px">
|
<el-col :span="6">
|
<el-card shadow="never">
|
<div>总任务数</div>
|
<div style="font-size: 22px; font-weight: 600; margin-top: 4px">
|
{{ totalTasks }}
|
</div>
|
</el-card>
|
</el-col>
|
<el-col :span="6">
|
<el-card shadow="never">
|
<div>进行中任务</div>
|
<div style="font-size: 22px; font-weight: 600; margin-top: 4px">
|
{{ runningTasks }}
|
</div>
|
</el-card>
|
</el-col>
|
<el-col :span="6">
|
<el-card shadow="never">
|
<div>已完成任务</div>
|
<div style="font-size: 22px; font-weight: 600; margin-top: 4px">
|
{{ finishedTasks }}
|
</div>
|
</el-card>
|
</el-col>
|
<el-col :span="6">
|
<el-card shadow="never">
|
<div>完成率</div>
|
<div style="font-size: 22px; font-weight: 600; margin-top: 4px">
|
{{ completionRate }}%
|
</div>
|
</el-card>
|
</el-col>
|
</el-row>
|
|
<!-- 查询条件 -->
|
<div class="search_form">
|
<div>
|
<span class="search_title">任务编号:</span>
|
<el-input
|
v-model="searchForm.taskNo"
|
style="width: 200px"
|
placeholder="请输入任务编号"
|
clearable
|
@keyup.enter.native="handleQuery"
|
/>
|
|
<span class="search_title ml10">车辆编号:</span>
|
<el-input
|
v-model="searchForm.vehicleCode"
|
style="width: 200px"
|
placeholder="请输入车辆编号"
|
clearable
|
@keyup.enter.native="handleQuery"
|
/>
|
|
<span class="search_title ml10">任务日期:</span>
|
<el-date-picker
|
v-model="searchForm.dateRange"
|
type="daterange"
|
range-separator="至"
|
start-placeholder="开始日期"
|
end-placeholder="结束日期"
|
value-format="YYYY-MM-DD"
|
format="YYYY-MM-DD"
|
clearable
|
@change="handleQuery"
|
/>
|
|
<span class="search_title ml10">状态:</span>
|
<el-select
|
v-model="searchForm.status"
|
style="width: 140px"
|
placeholder="请选择任务状态"
|
clearable
|
>
|
<el-option
|
v-for="item in statusOptions"
|
:key="item.value"
|
:label="item.label"
|
:value="item.value"
|
/>
|
</el-select>
|
|
<el-button type="primary" @click="handleQuery" style="margin-left: 10px">
|
搜索
|
</el-button>
|
<el-button @click="resetSearch">重置</el-button>
|
</div>
|
<div>
|
<el-button type="primary" icon="Plus" @click="openAdd">
|
新建运输任务
|
</el-button>
|
</div>
|
</div>
|
|
<!-- 表格 -->
|
<div class="table_list">
|
<el-table
|
:data="tableData"
|
border
|
style="width: 100%"
|
height="calc(100vh - 22em)"
|
:header-cell-style="{ background: '#F0F1F5', color: '#333333' }"
|
:row-class-name="tableRowClassName"
|
>
|
<el-table-column type="index" label="序号" width="60" align="center" />
|
<el-table-column
|
prop="taskNo"
|
label="任务编号"
|
width="150"
|
show-overflow-tooltip
|
/>
|
<el-table-column
|
prop="outboundOrderNo"
|
label="出库订单号"
|
width="180"
|
show-overflow-tooltip
|
/>
|
<el-table-column
|
prop="vehicleCode"
|
label="车辆编号"
|
width="130"
|
show-overflow-tooltip
|
/>
|
<el-table-column
|
prop="plateNumber"
|
label="车牌号码"
|
width="120"
|
show-overflow-tooltip
|
/>
|
<el-table-column
|
prop="driverName"
|
label="司机"
|
width="100"
|
show-overflow-tooltip
|
/>
|
<el-table-column
|
prop="loadAddress"
|
label="装货地点"
|
width="160"
|
show-overflow-tooltip
|
/>
|
<el-table-column
|
prop="deliveryAddress"
|
label="送货地点"
|
width="160"
|
show-overflow-tooltip
|
/>
|
<el-table-column
|
prop="loadTime"
|
label="装货时间"
|
width="160"
|
show-overflow-tooltip
|
/>
|
<el-table-column
|
prop="deliveryTime"
|
label="送货时间"
|
width="160"
|
show-overflow-tooltip
|
/>
|
<el-table-column
|
prop="signTime"
|
label="签收时间"
|
width="160"
|
show-overflow-tooltip
|
/>
|
<el-table-column label="状态" width="110" align="center">
|
<template #default="scope">
|
<el-tag :type="statusTagType(scope.row.status)">
|
{{ scope.row.status }}
|
</el-tag>
|
</template>
|
</el-table-column>
|
<el-table-column label="进度" width="150" align="center">
|
<template #default="scope">
|
<el-progress
|
:percentage="scope.row.progress"
|
:status="scope.row.status === '已完成' ? 'success' : undefined"
|
:stroke-width="12"
|
:show-text="false"
|
/>
|
<div style="font-size: 12px; margin-top: 4px">
|
{{ scope.row.progress }}%
|
</div>
|
</template>
|
</el-table-column>
|
<el-table-column label="操作" fixed="right" width="160" align="center">
|
<template #default="scope">
|
<el-button
|
type="primary"
|
link
|
size="small"
|
@click="openEdit(scope.row)"
|
>
|
编辑
|
</el-button>
|
<el-button
|
type="danger"
|
link
|
size="small"
|
@click="removeRow(scope.row)"
|
>
|
删除
|
</el-button>
|
</template>
|
</el-table-column>
|
</el-table>
|
</div>
|
|
<!-- 新增/编辑弹窗 -->
|
<el-dialog
|
v-model="dialogVisible"
|
:title="dialogTitle"
|
width="780px"
|
destroy-on-close
|
>
|
<el-form
|
ref="formRef"
|
:model="form"
|
:rules="rules"
|
label-width="110px"
|
label-position="right"
|
>
|
<el-row :gutter="20">
|
<el-col :span="12">
|
<el-form-item label="任务编号:" prop="taskNo">
|
<el-input
|
v-model="form.taskNo"
|
placeholder="请输入任务编号"
|
/>
|
</el-form-item>
|
</el-col>
|
<el-col :span="12">
|
<el-form-item label="出库订单号:" prop="outboundOrderNo">
|
<el-input
|
v-model="form.outboundOrderNo"
|
placeholder="请输入关联出库订单号"
|
/>
|
</el-form-item>
|
</el-col>
|
</el-row>
|
|
<el-row :gutter="20">
|
<el-col :span="12">
|
<el-form-item label="车辆编号:" prop="vehicleCode">
|
<el-input
|
v-model="form.vehicleCode"
|
placeholder="请输入车辆编号"
|
/>
|
</el-form-item>
|
</el-col>
|
<el-col :span="12">
|
<el-form-item label="车牌号码:" prop="plateNumber">
|
<el-input
|
v-model="form.plateNumber"
|
placeholder="请输入车牌号码"
|
/>
|
</el-form-item>
|
</el-col>
|
</el-row>
|
|
<el-row :gutter="20">
|
<el-col :span="12">
|
<el-form-item label="司机:" prop="driverName">
|
<el-input
|
v-model="form.driverName"
|
placeholder="请输入司机姓名"
|
/>
|
</el-form-item>
|
</el-col>
|
<el-col :span="12">
|
<el-form-item label="司机电话:" prop="driverPhone">
|
<el-input
|
v-model="form.driverPhone"
|
placeholder="请输入司机联系电话"
|
/>
|
</el-form-item>
|
</el-col>
|
</el-row>
|
|
<el-row :gutter="20">
|
<el-col :span="12">
|
<el-form-item label="装货地点:" prop="loadAddress">
|
<el-input
|
v-model="form.loadAddress"
|
placeholder="请输入装货地点"
|
/>
|
</el-form-item>
|
</el-col>
|
<el-col :span="12">
|
<el-form-item label="送货地点:" prop="deliveryAddress">
|
<el-input
|
v-model="form.deliveryAddress"
|
placeholder="请输入送货地点"
|
/>
|
</el-form-item>
|
</el-col>
|
</el-row>
|
|
<el-row :gutter="20">
|
<el-col :span="8">
|
<el-form-item label="装货时间:" prop="loadTime">
|
<el-date-picker
|
v-model="form.loadTime"
|
type="datetime"
|
value-format="YYYY-MM-DD HH:mm:ss"
|
format="YYYY-MM-DD HH:mm"
|
placeholder="请选择装货时间"
|
style="width: 100%"
|
/>
|
</el-form-item>
|
</el-col>
|
<el-col :span="8">
|
<el-form-item label="送货时间:" prop="deliveryTime">
|
<el-date-picker
|
v-model="form.deliveryTime"
|
type="datetime"
|
value-format="YYYY-MM-DD HH:mm:ss"
|
format="YYYY-MM-DD HH:mm"
|
placeholder="请选择送货时间"
|
style="width: 100%"
|
/>
|
</el-form-item>
|
</el-col>
|
<el-col :span="8">
|
<el-form-item label="签收时间:" prop="signTime">
|
<el-date-picker
|
v-model="form.signTime"
|
type="datetime"
|
value-format="YYYY-MM-DD HH:mm:ss"
|
format="YYYY-MM-DD HH:mm"
|
placeholder="请选择签收时间"
|
style="width: 100%"
|
/>
|
</el-form-item>
|
</el-col>
|
</el-row>
|
|
<el-row :gutter="20">
|
<el-col :span="12">
|
<el-form-item label="状态:" prop="status">
|
<el-select v-model="form.status" placeholder="请选择任务状态">
|
<el-option
|
v-for="item in statusOptions"
|
:key="item.value"
|
:label="item.label"
|
:value="item.value"
|
/>
|
</el-select>
|
</el-form-item>
|
</el-col>
|
<el-col :span="12">
|
<el-form-item label="计划日期:" prop="planDate">
|
<el-date-picker
|
v-model="form.planDate"
|
type="date"
|
value-format="YYYY-MM-DD"
|
format="YYYY-MM-DD"
|
placeholder="请选择计划日期"
|
style="width: 100%"
|
/>
|
</el-form-item>
|
</el-col>
|
</el-row>
|
</el-form>
|
|
<template #footer>
|
<div class="dialog-footer">
|
<el-button @click="handleCancel">取 消</el-button>
|
<el-button type="primary" @click="handleSubmit">保 存</el-button>
|
</div>
|
</template>
|
</el-dialog>
|
</div>
|
</template>
|
|
<script setup>
|
import { ref, reactive, computed, onMounted } from "vue";
|
import { ElMessage, ElMessageBox } from "element-plus";
|
|
// 模拟运输任务数据
|
const rawTasks = ref([
|
{
|
id: 1,
|
taskNo: "T2024-1201-001",
|
outboundOrderNo: "OUT-2024-1201-1001",
|
vehicleCode: "CL-202401",
|
plateNumber: "粤A12345",
|
driverName: "张师傅",
|
driverPhone: "13800000001",
|
loadAddress: "深圳仓库A区",
|
deliveryAddress: "广州客户一部",
|
planDate: "2024-12-01",
|
loadTime: "2024-12-01 09:00:00",
|
deliveryTime: "2024-12-01 14:30:00",
|
signTime: "2024-12-01 15:00:00",
|
status: "已完成",
|
},
|
{
|
id: 2,
|
taskNo: "T2024-1201-002",
|
outboundOrderNo: "OUT-2024-1201-1002",
|
vehicleCode: "CL-202402",
|
plateNumber: "粤B67890",
|
driverName: "李师傅",
|
driverPhone: "13800000002",
|
loadAddress: "深圳仓库B区",
|
deliveryAddress: "东莞客户二部",
|
planDate: "2024-12-01",
|
loadTime: "2024-12-01 10:00:00",
|
deliveryTime: "2024-12-01 13:00:00",
|
signTime: "",
|
status: "运输中",
|
},
|
{
|
id: 3,
|
taskNo: "T2024-1202-001",
|
outboundOrderNo: "OUT-2024-1202-1003",
|
vehicleCode: "CL-202401",
|
plateNumber: "粤A12345",
|
driverName: "张师傅",
|
driverPhone: "13800000001",
|
loadAddress: "深圳仓库A区",
|
deliveryAddress: "佛山客户三部",
|
planDate: "2024-12-02",
|
loadTime: "2024-12-02 08:30:00",
|
deliveryTime: "",
|
signTime: "",
|
status: "待发车",
|
},
|
{
|
id: 4,
|
taskNo: "T2024-1203-001",
|
outboundOrderNo: "OUT-2024-1203-1004",
|
vehicleCode: "CL-202403",
|
plateNumber: "粤C11223",
|
driverName: "王师傅",
|
driverPhone: "13800000003",
|
loadAddress: "深圳仓库C区",
|
deliveryAddress: "惠州客户四部",
|
planDate: "2024-12-03",
|
loadTime: "",
|
deliveryTime: "",
|
signTime: "",
|
status: "未开始",
|
},
|
]);
|
|
// 状态枚举
|
const statusOptions = [
|
{ label: "未开始", value: "未开始" },
|
{ label: "待发车", value: "待发车" },
|
{ label: "运输中", value: "运输中" },
|
{ label: "待签收", value: "待签收" },
|
{ label: "已完成", value: "已完成" },
|
];
|
|
// 查询表单
|
const searchForm = reactive({
|
taskNo: "",
|
vehicleCode: "",
|
dateRange: [],
|
status: "",
|
});
|
|
// 表格数据(带进度等计算字段)
|
const tableData = ref([]);
|
|
// 统计
|
const totalTasks = computed(() => rawTasks.value.length);
|
const finishedTasks = computed(
|
() => rawTasks.value.filter((t) => t.status === "已完成").length
|
);
|
const runningTasks = computed(
|
() =>
|
rawTasks.value.filter((t) =>
|
["待发车", "运输中", "待签收"].includes(t.status)
|
).length
|
);
|
const completionRate = computed(() => {
|
if (!totalTasks.value) return 0;
|
return Math.round((finishedTasks.value / totalTasks.value) * 100);
|
});
|
|
// 计算单条任务进度
|
const computeProgress = (task) => {
|
if (task.status === "已完成" || task.signTime) return 100;
|
if (task.status === "待签收" || task.deliveryTime) return 80;
|
if (task.status === "运输中") return 60;
|
if (task.status === "待发车" || task.loadTime) return 30;
|
if (task.status === "未开始") return 0;
|
return 0;
|
};
|
|
// 状态 tag 样式
|
const statusTagType = (status) => {
|
if (status === "已完成") return "success";
|
if (status === "运输中") return "warning";
|
if (status === "待签收" || status === "待发车") return "info";
|
return "default";
|
};
|
|
// 重算表格数据
|
const recomputeTable = () => {
|
const filtered = rawTasks.value
|
.filter((t) => {
|
if (searchForm.taskNo && !t.taskNo.includes(searchForm.taskNo.trim())) {
|
return false;
|
}
|
if (
|
searchForm.vehicleCode &&
|
!t.vehicleCode.includes(searchForm.vehicleCode.trim())
|
) {
|
return false;
|
}
|
if (searchForm.status && t.status !== searchForm.status) {
|
return false;
|
}
|
if (Array.isArray(searchForm.dateRange) && searchForm.dateRange.length === 2) {
|
const [start, end] = searchForm.dateRange;
|
if (!t.planDate || t.planDate < start || t.planDate > end) {
|
return false;
|
}
|
}
|
return true;
|
})
|
.map((t) => ({
|
...t,
|
progress: computeProgress(t),
|
}));
|
|
tableData.value = filtered;
|
};
|
|
// 查询
|
const handleQuery = () => {
|
recomputeTable();
|
};
|
|
const resetSearch = () => {
|
searchForm.taskNo = "";
|
searchForm.vehicleCode = "";
|
searchForm.dateRange = [];
|
searchForm.status = "";
|
recomputeTable();
|
};
|
|
// 行样式
|
const tableRowClassName = ({ row }) => {
|
if (row.status === "已完成") {
|
return "row-finished";
|
}
|
if (row.status === "运输中") {
|
return "row-running";
|
}
|
return "";
|
};
|
|
// 弹窗 & 表单
|
const dialogVisible = ref(false);
|
const dialogTitle = ref("新建运输任务");
|
const isEdit = ref(false);
|
const formRef = ref(null);
|
const form = reactive({
|
id: null,
|
taskNo: "",
|
outboundOrderNo: "",
|
vehicleCode: "",
|
plateNumber: "",
|
driverName: "",
|
driverPhone: "",
|
loadAddress: "",
|
deliveryAddress: "",
|
planDate: "",
|
loadTime: "",
|
deliveryTime: "",
|
signTime: "",
|
status: "未开始",
|
});
|
|
const rules = {
|
taskNo: [{ required: true, message: "请输入任务编号", trigger: "blur" }],
|
outboundOrderNo: [
|
{ required: true, message: "请输入出库订单号", trigger: "blur" },
|
],
|
vehicleCode: [{ required: true, message: "请输入车辆编号", trigger: "blur" }],
|
plateNumber: [{ required: true, message: "请输入车牌号码", trigger: "blur" }],
|
driverName: [{ required: true, message: "请输入司机姓名", trigger: "blur" }],
|
loadAddress: [{ required: true, message: "请输入装货地点", trigger: "blur" }],
|
deliveryAddress: [
|
{ required: true, message: "请输入送货地点", trigger: "blur" },
|
],
|
planDate: [{ required: true, message: "请选择计划日期", trigger: "change" }],
|
};
|
|
// 新建
|
const openAdd = () => {
|
dialogTitle.value = "新建运输任务";
|
isEdit.value = false;
|
Object.assign(form, {
|
id: null,
|
taskNo: "",
|
outboundOrderNo: "",
|
vehicleCode: "",
|
plateNumber: "",
|
driverName: "",
|
driverPhone: "",
|
loadAddress: "",
|
deliveryAddress: "",
|
planDate: "",
|
loadTime: "",
|
deliveryTime: "",
|
signTime: "",
|
status: "未开始",
|
});
|
dialogVisible.value = true;
|
};
|
|
// 编辑
|
const openEdit = (row) => {
|
dialogTitle.value = "编辑运输任务";
|
isEdit.value = true;
|
Object.assign(form, row);
|
dialogVisible.value = true;
|
};
|
|
// 保存
|
const handleSubmit = () => {
|
if (!formRef.value) return;
|
formRef.value.validate((valid) => {
|
if (!valid) return;
|
|
if (isEdit.value) {
|
const index = rawTasks.value.findIndex((t) => t.id === form.id);
|
if (index !== -1) {
|
rawTasks.value[index] = { ...form };
|
}
|
ElMessage.success("运输任务已更新");
|
} else {
|
const newId = rawTasks.value.length
|
? Math.max(...rawTasks.value.map((t) => t.id)) + 1
|
: 1;
|
rawTasks.value.push({ ...form, id: newId });
|
ElMessage.success("运输任务已新增");
|
}
|
dialogVisible.value = false;
|
recomputeTable();
|
});
|
};
|
|
const handleCancel = () => {
|
dialogVisible.value = false;
|
};
|
|
// 删除
|
const removeRow = (row) => {
|
ElMessageBox.confirm("是否确认删除该运输任务?", "删除提示", {
|
confirmButtonText: "确认",
|
cancelButtonText: "取消",
|
type: "warning",
|
})
|
.then(() => {
|
rawTasks.value = rawTasks.value.filter((t) => t.id !== row.id);
|
recomputeTable();
|
ElMessage.success("删除成功");
|
})
|
.catch(() => {});
|
};
|
|
onMounted(() => {
|
recomputeTable();
|
});
|
</script>
|
|
<style scoped lang="scss">
|
.dialog-footer {
|
text-align: right;
|
}
|
|
::v-deep(.row-finished) {
|
background-color: #f6ffed;
|
}
|
|
::v-deep(.row-running) {
|
background-color: #fffbe6;
|
}
|
</style>
|