spring
2 天以前 c344439875168ddcae23a0070e735e85717daa06
fix: 完成车辆管理、车辆油耗管理、运输任务管理页面编辑
已添加3个文件
1829 ■■■■■ 文件已修改
src/views/inventoryManagement/transportTaskManagement/index.vue 692 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/inventoryManagement/vehicleFuelManagement/index.vue 556 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/inventoryManagement/vehicleManagement/index.vue 581 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
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)"
            >
              åˆ é™¤
            </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>
src/views/inventoryManagement/vehicleFuelManagement/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,556 @@
<template>
  <div class="app-container">
    <!-- æŸ¥è¯¢æ¡ä»¶ -->
    <div class="search_form">
      <div>
        <span class="search_title">车辆编号:</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"
        />
        <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 - 18.5em)"
        :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="vehicleCode"
          label="车辆编号"
          width="130"
          show-overflow-tooltip
        />
        <el-table-column
          prop="plateNumber"
          label="车牌号码"
          width="120"
          show-overflow-tooltip
        />
        <el-table-column
          prop="fuelDate"
          label="加油日期"
          width="120"
          show-overflow-tooltip
        />
        <el-table-column
          prop="gunNo"
          label="油枪号"
          width="90"
          align="center"
        />
        <el-table-column
          prop="amount"
          label="金额(元)"
          width="100"
          align="right"
        >
          <template #default="scope">
            <span>{{ scope.row.amount?.toFixed(2) }}</span>
          </template>
        </el-table-column>
        <el-table-column
          prop="liters"
          label="升数(L)"
          width="90"
          align="right"
        >
          <template #default="scope">
            <span>{{ scope.row.liters?.toFixed(2) }}</span>
          </template>
        </el-table-column>
        <el-table-column
          prop="startMileage"
          label="起始里程(km)"
          width="120"
          align="right"
        />
        <el-table-column
          prop="endMileage"
          label="结束里程(km)"
          width="120"
          align="right"
        />
        <el-table-column
          prop="distance"
          label="行驶里程(km)"
          width="120"
          align="right"
        />
        <el-table-column
          prop="fuelConsumption"
          label="油耗(L/100km)"
          width="130"
          align="center"
        >
          <template #default="scope">
            <span
              :style="scope.row.isAbnormal ? 'color:#F56C6C;font-weight:600;' : ''"
            >
              {{ scope.row.fuelConsumption != null ? scope.row.fuelConsumption.toFixed(2) : '-' }}
            </span>
          </template>
        </el-table-column>
        <el-table-column
          prop="avgConsumption"
          label="车辆平均油耗"
          width="130"
          align="center"
        >
          <template #default="scope">
            <span>
              {{ scope.row.avgConsumption != null ? scope.row.avgConsumption.toFixed(2) : '-' }}
            </span>
          </template>
        </el-table-column>
        <el-table-column label="异常预警" width="100" align="center">
          <template #default="scope">
            <el-tag v-if="scope.row.isAbnormal" type="danger" size="small">
              å¼‚常
            </el-tag>
            <span v-else>-</span>
          </template>
        </el-table-column>
        <el-table-column label="操作" fixed="right" width="140" 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="640px"
      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="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="fuelDate">
              <el-date-picker
                v-model="form.fuelDate"
                type="date"
                value-format="YYYY-MM-DD"
                format="YYYY-MM-DD"
                placeholder="请选择加油日期"
                style="width: 100%"
              />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="油枪号:" prop="gunNo">
              <el-input
                v-model="form.gunNo"
                placeholder="请输入油枪号"
              />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="金额(元):" prop="amount">
              <el-input-number
                v-model="form.amount"
                :min="0"
                :step="0.01"
                :precision="2"
                placeholder="请输入金额"
                style="width: 100%"
              />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="升数(L):" prop="liters">
              <el-input-number
                v-model="form.liters"
                :min="0"
                :step="0.01"
                :precision="2"
                placeholder="请输入升数"
                style="width: 100%"
              />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="起始里程(km):" prop="startMileage">
              <el-input-number
                v-model="form.startMileage"
                :min="0"
                :step="1"
                placeholder="请输入起始里程"
                style="width: 100%"
              />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="结束里程(km):" prop="endMileage">
              <el-input-number
                v-model="form.endMileage"
                :min="0"
                :step="1"
                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, onMounted } from "vue";
import { ElMessage, ElMessageBox } from "element-plus";
// æ¨¡æ‹ŸåŠ æ²¹è®°å½•æ•°æ®
const rawRecords = ref([
  {
    id: 1,
    vehicleCode: "CL-202401",
    plateNumber: "粤A12345",
    fuelDate: "2024-12-01",
    gunNo: "01",
    amount: 500,
    liters: 70,
    startMileage: 12000,
    endMileage: 12600,
  },
  {
    id: 2,
    vehicleCode: "CL-202401",
    plateNumber: "粤A12345",
    fuelDate: "2024-12-15",
    gunNo: "02",
    amount: 520,
    liters: 72,
    startMileage: 12600,
    endMileage: 13250,
  },
  {
    id: 3,
    vehicleCode: "CL-202402",
    plateNumber: "粤B67890",
    fuelDate: "2024-12-05",
    gunNo: "03",
    amount: 430,
    liters: 60,
    startMileage: 8000,
    endMileage: 8520,
  },
  {
    id: 4,
    vehicleCode: "CL-202402",
    plateNumber: "粤B67890",
    fuelDate: "2024-12-20",
    gunNo: "01",
    amount: 450,
    liters: 63,
    startMileage: 8520,
    endMileage: 9000,
  },
  {
    id: 5,
    vehicleCode: "CL-202401",
    plateNumber: "粤A12345",
    fuelDate: "2025-01-05",
    gunNo: "01",
    amount: 700,
    liters: 90,
    startMileage: 13250,
    endMileage: 13600, // æ˜Žæ˜¾å¼‚常油耗
  },
]);
// æŸ¥è¯¢è¡¨å•
const searchForm = reactive({
  vehicleCode: "",
  dateRange: [],
});
// è¡¨æ ¼æ•°æ®ï¼ˆåŒ…含计算字段)
const tableData = ref([]);
// å¼¹çª— & è¡¨å•
const dialogVisible = ref(false);
const dialogTitle = ref("新增加油记录");
const isEdit = ref(false);
const formRef = ref(null);
const form = reactive({
  id: null,
  vehicleCode: "",
  plateNumber: "",
  fuelDate: "",
  gunNo: "",
  amount: null,
  liters: null,
  startMileage: null,
  endMileage: null,
});
const rules = {
  vehicleCode: [{ required: true, message: "请输入车辆编号", trigger: "blur" }],
  plateNumber: [{ required: true, message: "请输入车牌号码", trigger: "blur" }],
  fuelDate: [{ required: true, message: "请选择加油日期", trigger: "change" }],
  gunNo: [{ required: true, message: "请输入油枪号", trigger: "blur" }],
  amount: [{ required: true, message: "请输入金额", trigger: "blur" }],
  liters: [{ required: true, message: "请输入升数", trigger: "blur" }],
  startMileage: [{ required: true, message: "请输入起始里程", trigger: "blur" }],
  endMileage: [{ required: true, message: "请输入结束里程", trigger: "blur" }],
};
// é‡æ–°è®¡ç®—油耗、平均油耗和异常预警
const recomputeTable = () => {
  const records = rawRecords.value;
  // 1. å…ˆæŒ‰è½¦è¾†ç»Ÿè®¡å¹³å‡æ²¹è€—
  const stats = {};
  records.forEach((r) => {
    const distance = r.endMileage - r.startMileage;
    if (distance <= 0 || !r.liters) return;
    const cons = (r.liters / distance) * 100; // L/100km
    if (!stats[r.vehicleCode]) {
      stats[r.vehicleCode] = { totalCons: 0, count: 0 };
    }
    stats[r.vehicleCode].totalCons += cons;
    stats[r.vehicleCode].count += 1;
  });
  const avgMap = {};
  Object.keys(stats).forEach((key) => {
    avgMap[key] = stats[key].totalCons / stats[key].count;
  });
  // 2. æŒ‰ç­›é€‰æ¡ä»¶è¿‡æ»¤å¹¶è¡¥å……计算字段
  const filtered = records
    .filter((r) => {
      if (
        searchForm.vehicleCode &&
        !r.vehicleCode.includes(searchForm.vehicleCode.trim())
      ) {
        return false;
      }
      if (Array.isArray(searchForm.dateRange) && searchForm.dateRange.length === 2) {
        const [start, end] = searchForm.dateRange;
        if (r.fuelDate < start || r.fuelDate > end) {
          return false;
        }
      }
      return true;
    })
    .map((r) => {
      const distance = r.endMileage - r.startMileage;
      const fuelConsumption =
        distance > 0 && r.liters
          ? (r.liters / distance) * 100
          : null;
      const avgConsumption =
        avgMap[r.vehicleCode] != null ? avgMap[r.vehicleCode] : null;
      const isAbnormal =
        avgConsumption != null &&
        fuelConsumption != null &&
        fuelConsumption > avgConsumption * 1.2;
      return {
        ...r,
        distance,
        fuelConsumption,
        avgConsumption,
        isAbnormal,
      };
    });
  tableData.value = filtered;
};
// æŸ¥è¯¢
const handleQuery = () => {
  recomputeTable();
};
const resetSearch = () => {
  searchForm.vehicleCode = "";
  searchForm.dateRange = [];
  recomputeTable();
};
// è¡Œæ ·å¼ï¼ˆå¼‚常高亮)
const tableRowClassName = ({ row }) => {
  if (row.isAbnormal) {
    return "row-abnormal";
  }
  return "";
};
// æ–°å¢ž
const openAdd = () => {
  dialogTitle.value = "新增加油记录";
  isEdit.value = false;
  Object.assign(form, {
    id: null,
    vehicleCode: "",
    plateNumber: "",
    fuelDate: "",
    gunNo: "",
    amount: null,
    liters: null,
    startMileage: null,
    endMileage: null,
  });
  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 (form.endMileage <= form.startMileage) {
      ElMessage.warning("结束里程必须大于起始里程");
      return;
    }
    if (isEdit.value) {
      const index = rawRecords.value.findIndex((r) => r.id === form.id);
      if (index !== -1) {
        rawRecords.value[index] = { ...form };
      }
      ElMessage.success("加油记录已更新");
    } else {
      const newId = rawRecords.value.length
        ? Math.max(...rawRecords.value.map((r) => r.id)) + 1
        : 1;
      rawRecords.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(() => {
      rawRecords.value = rawRecords.value.filter((r) => r.id !== row.id);
      recomputeTable();
      ElMessage.success("删除成功");
    })
    .catch(() => {});
};
onMounted(() => {
  recomputeTable();
});
</script>
<style scoped lang="scss">
.dialog-footer {
  text-align: right;
}
::v-deep(.row-abnormal) {
  background-color: #fff5f5;
}
</style>
src/views/inventoryManagement/vehicleManagement/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,581 @@
<template>
  <div class="app-container">
    <!-- æŸ¥è¯¢æ¡ä»¶ -->
    <div class="search_form">
      <div>
        <span class="search_title">车牌号码:</span>
        <el-input
          v-model="searchForm.plateNumber"
          style="width: 180px"
          placeholder="请输入车牌号码"
          clearable
          @keyup.enter.native="handleQuery"
        />
        <span class="search_title ml10">车辆类型:</span>
        <el-select
          v-model="searchForm.vehicleType"
          style="width: 160px"
          placeholder="请选择车辆类型"
          clearable
        >
          <el-option
            v-for="item in vehicleTypeOptions"
            :key="item.value"
            :label="item.label"
            :value="item.value"
          />
        </el-select>
        <span class="search_title ml10">所属部门:</span>
        <el-select
          v-model="searchForm.department"
          style="width: 160px"
          placeholder="请选择所属部门"
          clearable
        >
          <el-option
            v-for="item in departmentOptions"
            :key="item.value"
            :label="item.label"
            :value="item.value"
          />
        </el-select>
        <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>
        <span class="search_title ml10">归档状态:</span>
        <el-select
          v-model="searchForm.archived"
          style="width: 140px"
          placeholder="请选择归档状态"
          clearable
        >
          <el-option label="未归档" value="false" />
          <el-option label="已归档" value="true" />
        </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 - 18.5em)"
        :header-cell-style="{ background: '#F0F1F5', color: '#333333' }"
      >
        <el-table-column type="index" label="序号" width="60" align="center" />
        <el-table-column
          prop="vehicleCode"
          label="车辆编号"
          width="140"
          show-overflow-tooltip
        />
        <el-table-column
          prop="plateNumber"
          label="车牌号码"
          width="120"
          show-overflow-tooltip
        />
        <el-table-column
          prop="vehicleType"
          label="车辆类型"
          width="120"
          show-overflow-tooltip
        />
        <el-table-column
          prop="department"
          label="所属部门"
          width="140"
          show-overflow-tooltip
        />
        <el-table-column
          prop="purchaseDate"
          label="购置日期"
          width="120"
          show-overflow-tooltip
        />
        <el-table-column
          prop="licenseNumber"
          label="行驶证编号"
          width="160"
          show-overflow-tooltip
        />
        <el-table-column
          prop="licenseIssueDate"
          label="发证日期"
          width="120"
          show-overflow-tooltip
        />
        <el-table-column
          prop="licenseExpireDate"
          label="到期日期"
          width="120"
          show-overflow-tooltip
        />
        <el-table-column label="状态" width="100" 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="100" align="center">
          <template #default="scope">
            <el-tag :type="scope.row.archived ? 'info' : 'success'">
              {{ scope.row.archived ? '已归档' : '未归档' }}
            </el-tag>
          </template>
        </el-table-column>
        <el-table-column label="操作" fixed="right" width="220" align="center">
          <template #default="scope">
            <el-button
              type="primary"
              link
              size="small"
              @click="openEdit(scope.row)"
            >
              ç¼–辑
            </el-button>
            <el-button
              type="warning"
              link
              size="small"
              :disabled="scope.row.archived"
              @click="archiveRow(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="600px"
      destroy-on-close
    >
      <el-form
        ref="formRef"
        :model="form"
        :rules="rules"
        label-width="100px"
        label-position="right"
      >
        <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="vehicleType">
              <el-select
                v-model="form.vehicleType"
                placeholder="请选择车辆类型"
                clearable
              >
                <el-option
                  v-for="item in vehicleTypeOptions"
                  :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="department">
              <el-select
                v-model="form.department"
                placeholder="请选择所属部门"
                clearable
              >
                <el-option
                  v-for="item in departmentOptions"
                  :key="item.value"
                  :label="item.label"
                  :value="item.value"
                />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="购置日期:" prop="purchaseDate">
              <el-date-picker
                v-model="form.purchaseDate"
                type="date"
                value-format="YYYY-MM-DD"
                format="YYYY-MM-DD"
                placeholder="请选择购置日期"
                style="width: 100%"
              />
            </el-form-item>
          </el-col>
          <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-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="行驶证编号:" prop="licenseNumber">
              <el-input
                v-model="form.licenseNumber"
                placeholder="请输入行驶证编号"
              />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="发证日期:" prop="licenseIssueDate">
              <el-date-picker
                v-model="form.licenseIssueDate"
                type="date"
                value-format="YYYY-MM-DD"
                format="YYYY-MM-DD"
                placeholder="请选择发证日期"
                style="width: 100%"
              />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="到期日期:" prop="licenseExpireDate">
              <el-date-picker
                v-model="form.licenseExpireDate"
                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 } from "vue";
import { ElMessage, ElMessageBox } from "element-plus";
// æ¨¡æ‹Ÿè½¦è¾†åŸºç¡€æ•°æ®
const allVehicles = ref([
  {
    id: 1,
    vehicleCode: "CL-202401",
    plateNumber: "粤A12345",
    vehicleType: "厢式货车",
    department: "物流一部",
    purchaseDate: "2022-03-15",
    licenseNumber: "4401-2022-0001",
    licenseIssueDate: "2022-03-10",
    licenseExpireDate: "2026-03-10",
    status: "在用",
    archived: false,
  },
  {
    id: 2,
    vehicleCode: "CL-202402",
    plateNumber: "粤B67890",
    vehicleType: "冷藏车",
    department: "物流二部",
    purchaseDate: "2021-08-01",
    licenseNumber: "4401-2021-0123",
    licenseIssueDate: "2021-07-28",
    licenseExpireDate: "2025-07-28",
    status: "ç»´ä¿®",
    archived: false,
  },
  {
    id: 3,
    vehicleCode: "CL-202403",
    plateNumber: "粤C11223",
    vehicleType: "牵引车",
    department: "项目运输部",
    purchaseDate: "2020-05-20",
    licenseNumber: "4401-2020-0456",
    licenseIssueDate: "2020-05-18",
    licenseExpireDate: "2024-05-18",
    status: "闲置",
    archived: false,
  },
  {
    id: 4,
    vehicleCode: "CL-202404",
    plateNumber: "粤D33445",
    vehicleType: "厢式货车",
    department: "资产管理部",
    purchaseDate: "2019-11-11",
    licenseNumber: "4401-2019-0789",
    licenseIssueDate: "2019-11-08",
    licenseExpireDate: "2023-11-08",
    status: "在用",
    archived: true,
  },
]);
// ä¸‹æ‹‰æžšä¸¾
const vehicleTypeOptions = [
  { label: "厢式货车", value: "厢式货车" },
  { label: "冷藏车", value: "冷藏车" },
  { label: "牵引车", value: "牵引车" },
  { label: "其他", value: "其他" },
];
const departmentOptions = [
  { label: "物流一部", value: "物流一部" },
  { label: "物流二部", value: "物流二部" },
  { label: "项目运输部", value: "项目运输部" },
  { label: "资产管理部", value: "资产管理部" },
];
const statusOptions = [
  { label: "在用", value: "在用" },
  { label: "闲置", value: "闲置" },
  { label: "ç»´ä¿®", value: "ç»´ä¿®" },
];
// æŸ¥è¯¢è¡¨å•
const searchForm = reactive({
  plateNumber: "",
  vehicleType: "",
  department: "",
  status: "",
  archived: "",
});
// è¡¨æ ¼æ•°æ®
const tableData = ref([...allVehicles.value]);
// å¼¹çª— & è¡¨å•
const dialogVisible = ref(false);
const dialogTitle = ref("新增车辆");
const isEdit = ref(false);
const formRef = ref(null);
const form = reactive({
  id: null,
  vehicleCode: "",
  plateNumber: "",
  vehicleType: "",
  department: "",
  purchaseDate: "",
  licenseNumber: "",
  licenseIssueDate: "",
  licenseExpireDate: "",
  status: "在用",
  archived: false,
});
const rules = {
  vehicleCode: [{ required: true, message: "请输入车辆编号", trigger: "blur" }],
  plateNumber: [{ required: true, message: "请输入车牌号码", trigger: "blur" }],
  vehicleType: [{ required: true, message: "请选择车辆类型", trigger: "change" }],
  department: [{ required: true, message: "请选择所属部门", trigger: "change" }],
  purchaseDate: [{ required: true, message: "请选择购置日期", trigger: "change" }],
  status: [{ required: true, message: "请选择状态", trigger: "change" }],
  licenseNumber: [{ required: true, message: "请输入行驶证编号", trigger: "blur" }],
  licenseIssueDate: [{ required: true, message: "请选择发证日期", trigger: "change" }],
};
// æŸ¥è¯¢
const handleQuery = () => {
  tableData.value = allVehicles.value.filter((item) => {
    if (
      searchForm.plateNumber &&
      !item.plateNumber.includes(searchForm.plateNumber.trim())
    ) {
      return false;
    }
    if (searchForm.vehicleType && item.vehicleType !== searchForm.vehicleType) {
      return false;
    }
    if (searchForm.department && item.department !== searchForm.department) {
      return false;
    }
    if (searchForm.status && item.status !== searchForm.status) {
      return false;
    }
    if (searchForm.archived !== "") {
      const targetArchived = searchForm.archived === "true";
      if (item.archived !== targetArchived) return false;
    }
    return true;
  });
};
const resetSearch = () => {
  searchForm.plateNumber = "";
  searchForm.vehicleType = "";
  searchForm.department = "";
  searchForm.status = "";
  searchForm.archived = "";
  handleQuery();
};
// æ–°å¢ž
const openAdd = () => {
  dialogTitle.value = "新增车辆";
  isEdit.value = false;
  Object.assign(form, {
    id: null,
    vehicleCode: "",
    plateNumber: "",
    vehicleType: "",
    department: "",
    purchaseDate: "",
    licenseInfo: "",
    status: "在用",
    archived: false,
  });
  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 = allVehicles.value.findIndex((v) => v.id === form.id);
      if (index !== -1) {
        allVehicles.value[index] = { ...form };
      }
      ElMessage.success("车辆信息已更新");
    } else {
      const newId = allVehicles.value.length
        ? Math.max(...allVehicles.value.map((v) => v.id)) + 1
        : 1;
      allVehicles.value.push({ ...form, id: newId });
      ElMessage.success("车辆信息已新增");
    }
    dialogVisible.value = false;
    handleQuery();
  });
};
const handleCancel = () => {
  dialogVisible.value = false;
};
// å½’æ¡£
const archiveRow = (row) => {
  if (row.archived) return;
  ElMessageBox.confirm(
    "是否确认将该车辆归档?归档后仅保留查询,不再参与运输任务分配。",
    "归档提示",
    {
      confirmButtonText: "确认",
      cancelButtonText: "取消",
      type: "warning",
    }
  )
    .then(() => {
      row.archived = true;
      if (row.status === "在用") {
        row.status = "闲置";
      }
      ElMessage.success("车辆已归档");
      handleQuery();
    })
    .catch(() => {});
};
// åˆ é™¤
const removeRow = (row) => {
  ElMessageBox.confirm("是否确认删除该车辆基础信息?", "删除提示", {
    confirmButtonText: "确认",
    cancelButtonText: "取消",
    type: "warning",
  })
    .then(() => {
      allVehicles.value = allVehicles.value.filter((v) => v.id !== row.id);
      handleQuery();
      ElMessage.success("删除成功");
    })
    .catch(() => {});
};
// çŠ¶æ€æ ·å¼
const statusTagType = (status) => {
  if (status === "在用") return "success";
  if (status === "闲置") return "info";
  if (status === "ç»´ä¿®") return "warning";
  return "default";
};
</script>
<style scoped lang="scss">
.dialog-footer {
  text-align: right;
}
</style>