gaoluyang
2026-08-06 377c0a70c094d024aeb0bb94f4b791c6d5530c1e
src/views/inventoryManagement/transportTaskManagement/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,692 @@
<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)"
             v-hasPermi="['inventoryManagement:transportTaskManagement:remove']">
              åˆ é™¤
            </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;
}
:deep(.row-finished) {
  background-color: #f6ffed;
}
:deep(.row-running) {
  background-color: #fffbe6;
}
</style>