<!--OA模块:车辆审批管理-->
|
<template>
|
<div class="app-container">
|
<!-- 车辆管理区域 -->
|
<el-card class="vehicle-manage-card mb20">
|
<template #header>
|
<div class="card-header">
|
<span>车辆管理</span>
|
<el-button type="primary" size="small" @click="openVehicleDialog()">新增车辆</el-button>
|
</div>
|
</template>
|
<el-table :data="vehicleList" border size="small" style="width: 100%" v-loading="vehicleLoading">
|
<el-table-column type="index" label="序号" width="60" align="center" />
|
<el-table-column prop="plateNumber" label="车牌号" min-width="120" align="center" />
|
<el-table-column label="使用状态" width="100" align="center">
|
<template #default="scope">
|
<el-tag :type="vehicleStatusTagType(scope.row.status)" size="small">
|
{{ vehicleStatusLabel(scope.row.status) }}
|
</el-tag>
|
</template>
|
</el-table-column>
|
<el-table-column prop="currentMileage" label="当前公里数" width="120" align="center" />
|
<el-table-column label="操作" width="150" align="center" fixed="right">
|
<template #default="scope">
|
<el-button link type="primary" size="small" @click="openVehicleDialog(scope.row)">编辑</el-button>
|
<el-button link type="danger" size="small" @click="handleDeleteVehicle(scope.row)">删除</el-button>
|
</template>
|
</el-table-column>
|
</el-table>
|
<div class="pagination-container" style="margin-top: 10px;">
|
<el-pagination
|
v-model:current-page="vehiclePage.current"
|
v-model:page-size="vehiclePage.size"
|
:page-sizes="[10, 20, 50]"
|
layout="total, sizes, prev, pager, next"
|
:total="vehiclePage.total"
|
@size-change="fetchVehicleList"
|
@current-change="fetchVehicleList"
|
size="small"
|
/>
|
</div>
|
</el-card>
|
|
<!-- 借出记录列表 -->
|
<div class="search_form mb20">
|
<div>
|
<span class="search_title">审批单号:</span>
|
<el-input
|
v-model="searchForm.instanceNo"
|
style="width: 220px"
|
placeholder="请输入审批单号"
|
clearable
|
@keyup.enter="onSearch"
|
/>
|
<span class="search_title" style="margin-left: 12px">申请人:</span>
|
<el-input
|
v-model="searchForm.applicantKeyword"
|
style="width: 220px"
|
placeholder="姓名或编号"
|
clearable
|
:prefix-icon="Search"
|
@keyup.enter="onSearch"
|
/>
|
<span class="search_title" style="margin-left: 12px">车牌号:</span>
|
<el-select
|
v-model="searchForm.vehiclePlateNumber"
|
style="width: 180px"
|
placeholder="请选择车牌号"
|
clearable
|
filterable
|
>
|
<el-option
|
v-for="item in vehicleList"
|
:key="item.id"
|
:label="item.plateNumber"
|
:value="item.plateNumber"
|
/>
|
</el-select>
|
<el-button type="primary" style="margin-left: 10px" @click="onSearch">搜索</el-button>
|
<el-button @click="resetSearch">重置</el-button>
|
</div>
|
<div>
|
<el-button type="danger" @click="batchDeleteBorrow" :disabled="!selectedBorrowRows?.length">批量删除</el-button>
|
<el-button type="primary" @click="openAddWithTemplate">新增借出申请</el-button>
|
</div>
|
</div>
|
<div class="table_list">
|
<PIMTable
|
rowKey="id"
|
:column="borrowTableColumn"
|
:tableData="borrowTableData"
|
:page="borrowPage"
|
:isSelection="true"
|
:tableLoading="borrowTableLoading"
|
@pagination="onBorrowPagination"
|
@selection-change="(rows) => selectedBorrowRows = rows"
|
:total="borrowPage.total"
|
/>
|
</div>
|
|
<!-- 借出申请提交对话框 -->
|
<ApprovalInstanceSubmitDialog
|
v-model="submitDialog.visible"
|
:title="submitDialogTitle"
|
:form="submitForm"
|
:rules="submitFormRules"
|
:fields="submitFormFields"
|
:active-template="activeTemplate"
|
:user-options="flowUserOptions"
|
:is-edit="isSubmitEdit"
|
:saving="submitSaving"
|
:form-ref="submitFormRef"
|
flow-attachments-only
|
@submit="onSubmit"
|
>
|
<template #before="{ form, fields }">
|
<FormPayloadFields :fields="displayTemplateFields(fields)" :form-payload="form.formPayload" />
|
<!-- 车辆使用信息展示 -->
|
<el-row :gutter="24" v-if="vehicleUseInfoDisplay(form)">
|
<el-col :span="24">
|
<el-form-item label="车辆状态">
|
<el-tag :type="vehicleStatusType(form.formPayload?.vehicleNo)">
|
{{ vehicleStatusText(form.formPayload?.vehicleNo) }}
|
</el-tag>
|
</el-form-item>
|
</el-col>
|
</el-row>
|
</template>
|
</ApprovalInstanceSubmitDialog>
|
|
<ApprovalTemplateBindDialog
|
v-model:visible="templateBindVisible"
|
:module-key="APPROVAL_MODULE_KEYS.VEHICLE"
|
skip-form-confirm
|
@confirm="onTemplateBound"
|
@closed="onTemplateBindClosed"
|
/>
|
|
<ApprovalInstanceDetailDialog
|
v-model="detailDialog.visible"
|
title="车辆审批详情"
|
:row="detailRow"
|
@edit="openEditFromDetail"
|
/>
|
|
<!-- 车辆管理对话框 -->
|
<el-dialog
|
v-model="vehicleDialog.visible"
|
:title="vehicleDialog.isEdit ? '编辑车辆' : '新增车辆'"
|
width="500px"
|
:close-on-click-modal="false"
|
>
|
<el-form :model="vehicleForm" :rules="vehicleRules" ref="vehicleFormRef" label-width="100px">
|
<el-form-item label="车牌号" prop="plateNumber">
|
<el-input v-model="vehicleForm.plateNumber" placeholder="请输入车牌号" />
|
</el-form-item>
|
<el-form-item label="使用状态" prop="status">
|
<el-select v-model="vehicleForm.status" placeholder="请选择使用状态" style="width: 100%">
|
<el-option label="闲置" value="idle" />
|
<el-option label="使用中" value="in_use" />
|
<el-option label="维修中" value="repair" />
|
<el-option label="保养中" value="maintenance" />
|
</el-select>
|
</el-form-item>
|
<el-form-item label="当前公里数" prop="currentMileage">
|
<el-input-number
|
v-model="vehicleForm.currentMileage"
|
:min="0"
|
:precision="1"
|
:step="0.1"
|
controls-position="right"
|
style="width: 100%"
|
placeholder="请输入当前公里数"
|
/>
|
</el-form-item>
|
</el-form>
|
<template #footer>
|
<el-button @click="vehicleDialog.visible = false">取消</el-button>
|
<el-button type="primary" :loading="vehicleSaveLoading" @click="saveVehicle">保存</el-button>
|
</template>
|
</el-dialog>
|
|
<!-- 延期申请对话框(使用审批模板) -->
|
<ApprovalInstanceSubmitDialog
|
v-model="extendSubmitDialog.visible"
|
:title="extendSubmitDialogTitle"
|
:form="extendSubmitForm"
|
:rules="extendSubmitFormRules"
|
:fields="extendSubmitFormFields"
|
:active-template="extendActiveTemplate"
|
:user-options="flowUserOptions"
|
:is-edit="false"
|
:saving="extendSubmitSaving"
|
:form-ref="extendSubmitFormRef"
|
flow-attachments-only
|
@submit="onExtendSubmit"
|
>
|
<template #before="{ form, fields }">
|
<FormPayloadFields :fields="fields" :form-payload="form.formPayload" />
|
<!-- 延期信息展示 -->
|
<el-row :gutter="24">
|
<el-col :span="12">
|
<el-form-item label="原审批单号">
|
<el-input v-model="extendSourceRow.instanceNo" disabled />
|
</el-form-item>
|
</el-col>
|
<el-col :span="12">
|
<el-form-item label="车牌号">
|
<el-input v-model="extendSourceRow.vehiclePlateNumber" disabled />
|
</el-form-item>
|
</el-col>
|
</el-row>
|
</template>
|
</ApprovalInstanceSubmitDialog>
|
|
<ApprovalTemplateBindDialog
|
v-model:visible="extendTemplateBindVisible"
|
:module-key="APPROVAL_MODULE_KEYS.VEHICLE_DELAY"
|
skip-form-confirm
|
@confirm="onExtendTemplateBound"
|
@closed="onExtendTemplateBindClosed"
|
/>
|
|
<!-- 归还车辆对话框 -->
|
<el-dialog
|
v-model="returnDialog.visible"
|
title="归还车辆"
|
width="600px"
|
:close-on-click-modal="false"
|
>
|
<el-form :model="returnForm" :rules="returnRules" ref="returnFormRef" label-width="120px">
|
<el-form-item label="借出单号">
|
<el-input v-model="returnForm.instanceNo" disabled />
|
</el-form-item>
|
<el-form-item label="车牌号">
|
<el-input v-model="returnForm.vehiclePlateNumber" disabled />
|
</el-form-item>
|
<el-form-item label="实际归还时间" prop="actualReturnTime" required>
|
<el-date-picker
|
v-model="returnForm.actualReturnTime"
|
type="datetime"
|
placeholder="请选择实际归还时间"
|
format="YYYY-MM-DD HH:mm:ss"
|
value-format="YYYY-MM-DD HH:mm:ss"
|
style="width: 100%"
|
/>
|
</el-form-item>
|
<el-form-item label="附件">
|
<AttachmentUploadImage
|
v-model:fileList="returnForm.returnStorageBlobDTOs"
|
:limit="5"
|
button-text="上传归还附件"
|
/>
|
</el-form-item>
|
</el-form>
|
<template #footer>
|
<el-button type="primary" :loading="returnLoading" @click="submitReturn">确认归还</el-button>
|
<el-button @click="returnDialog.visible = false">取消</el-button>
|
</template>
|
</el-dialog>
|
</div>
|
</template>
|
|
<script setup>
|
import { Search } from "@element-plus/icons-vue";
|
import dayjs from "dayjs";
|
import { ElMessage, ElMessageBox } from "element-plus";
|
import { computed, onMounted, reactive, ref } from "vue";
|
import AttachmentUploadImage from "@/components/AttachmentUpload/image/index.vue";
|
import {
|
deleteVehicle,
|
listVehiclePage,
|
saveVehicle as saveVehicleApi,
|
updateVehicle,
|
saveBorrow,
|
listBorrowPage,
|
deleteBorrow,
|
} from "@/api/officeProcessAutomation/vehicle.js";
|
import FormPayloadFields from "../approve-list/components/FormPayloadFields.vue";
|
import ApprovalInstanceDetailDialog from "../approve-shared/components/ApprovalInstanceDetailDialog.vue";
|
import ApprovalInstanceSubmitDialog from "../approve-shared/components/ApprovalInstanceSubmitDialog.vue";
|
import ApprovalTemplateBindDialog from "../approve-shared/components/ApprovalTemplateBindDialog.vue";
|
import { buildInstanceTableColumns } from "../approve-shared/approvalInstanceFormConfigTable.js";
|
import { APPROVAL_MODULE_KEYS } from "../approve-shared/approvalModuleRegistry.js";
|
import { useApprovalInstanceModule } from "../approve-shared/useApprovalInstanceModule.js";
|
import { useFlowUserOptions } from "../approve-shared/useFlowUserOptions.js";
|
|
// ==================== 车辆管理 ====================
|
const vehicleList = ref([]);
|
const vehicleLoading = ref(false);
|
const vehicleSaveLoading = ref(false);
|
const vehiclePage = reactive({
|
current: 1,
|
size: 10,
|
total: 0,
|
});
|
|
const vehicleDialog = reactive({
|
visible: false,
|
isEdit: false,
|
});
|
const vehicleFormRef = ref(null);
|
const vehicleForm = reactive({
|
id: null,
|
plateNumber: "",
|
status: "idle",
|
currentMileage: 0,
|
});
|
const vehicleRules = {
|
plateNumber: [{ required: true, message: "请输入车牌号", trigger: "blur" }],
|
status: [{ required: true, message: "请选择使用状态", trigger: "change" }],
|
};
|
|
// 获取车辆列表
|
async function fetchVehicleList() {
|
vehicleLoading.value = true;
|
try {
|
const res = await listVehiclePage({
|
current: vehiclePage.current,
|
size: vehiclePage.size,
|
});
|
if (res.code === 200) {
|
vehicleList.value = (res.data?.records || []).map((item) => ({
|
id: item.id,
|
plateNumber: item.plateNumber,
|
status: mapVehicleStatusFromApi(item.status),
|
currentMileage: item.mileage || 0,
|
}));
|
vehiclePage.total = res.data?.total || 0;
|
}
|
} catch (error) {
|
ElMessage.error("获取车辆列表失败");
|
} finally {
|
vehicleLoading.value = false;
|
}
|
}
|
|
// 状态映射
|
function mapVehicleStatusFromApi(apiStatus) {
|
const statusMap = {
|
IDLE: "idle",
|
IN_USE: "in_use",
|
MAINTENANCE: "repair",
|
SCRAPPED: "archived",
|
};
|
return statusMap[apiStatus] || "idle";
|
}
|
|
function mapVehicleStatusToApi(frontendStatus) {
|
const statusMap = {
|
idle: "IDLE",
|
in_use: "IN_USE",
|
repair: "MAINTENANCE",
|
archived: "SCRAPPED",
|
};
|
return statusMap[frontendStatus] || "IDLE";
|
}
|
|
function vehicleStatusTagType(status) {
|
const typeMap = {
|
idle: "success",
|
in_use: "warning",
|
repair: "danger",
|
maintenance: "info",
|
};
|
return typeMap[status] || "info";
|
}
|
|
function vehicleStatusLabel(status) {
|
const labelMap = {
|
idle: "闲置",
|
in_use: "使用中",
|
repair: "维修中",
|
maintenance: "保养中",
|
};
|
return labelMap[status] || status;
|
}
|
|
// 车辆管理对话框
|
function openVehicleDialog(row = null) {
|
vehicleDialog.isEdit = !!row;
|
if (row) {
|
vehicleForm.id = row.id;
|
vehicleForm.plateNumber = row.plateNumber;
|
vehicleForm.status = row.status;
|
vehicleForm.currentMileage = row.currentMileage;
|
} else {
|
vehicleForm.id = null;
|
vehicleForm.plateNumber = "";
|
vehicleForm.status = "idle";
|
vehicleForm.currentMileage = 0;
|
}
|
vehicleDialog.visible = true;
|
}
|
|
// 保存车辆
|
async function saveVehicle() {
|
if (!vehicleFormRef.value) return;
|
await vehicleFormRef.value.validate(async (valid) => {
|
if (!valid) return;
|
vehicleSaveLoading.value = true;
|
try {
|
const apiData = {
|
plateNumber: vehicleForm.plateNumber,
|
mileage: vehicleForm.currentMileage,
|
status: mapVehicleStatusToApi(vehicleForm.status),
|
};
|
|
if (vehicleDialog.isEdit) {
|
await updateVehicle({ ...apiData, id: vehicleForm.id });
|
ElMessage.success("车辆信息已更新");
|
} else {
|
await saveVehicleApi(apiData);
|
ElMessage.success("车辆已添加");
|
}
|
vehicleDialog.visible = false;
|
fetchVehicleList();
|
} catch (error) {
|
ElMessage.error(error?.message || "保存失败");
|
} finally {
|
vehicleSaveLoading.value = false;
|
}
|
});
|
}
|
|
// 删除车辆
|
async function handleDeleteVehicle(row) {
|
try {
|
await ElMessageBox.confirm(`确定要删除车辆 ${row.plateNumber} 吗?`, "提示", {
|
type: "warning",
|
});
|
await deleteVehicle([row.id]);
|
ElMessage.success("车辆已删除");
|
fetchVehicleList();
|
} catch {
|
// 用户取消
|
}
|
}
|
|
// ==================== 借出申请(使用审批模板) ====================
|
|
// 查找车辆使用时间字段(支持 datetimerange 和 datetime 类型)
|
function findVehicleUseTimeField(fields = []) {
|
return (
|
fields.find((f) => (f?.type === "datetimerange" || f?.type === "datetime") && String(f?.label || "").includes("车辆使用时间")) ||
|
fields.find((f) => f?.type === "datetimerange" && f?.key === "useDateRange") ||
|
fields.find((f) => f?.type === "datetimerange") ||
|
null
|
);
|
}
|
|
// 查找车牌号字段
|
function findVehicleNoField(fields = []) {
|
return (
|
fields.find((f) => String(f?.label || "").includes("车牌号")) ||
|
fields.find((f) => f?.key === "vehicleNo") ||
|
null
|
);
|
}
|
|
// 解析时间范围
|
function resolveTimeRange(payload, timeField) {
|
if (!timeField?.key) return { start: "", end: "" };
|
const val = payload?.[timeField.key];
|
if (!Array.isArray(val) || val.length < 2) return { start: "", end: "" };
|
return { start: val[0] || "", end: val[1] || "" };
|
}
|
|
// 计算天数
|
function computeDays(startStr, endStr) {
|
if (!startStr || !endStr) return null;
|
const t0 = dayjs(startStr);
|
const t1 = dayjs(endStr);
|
if (!t0.isValid() || !t1.isValid() || !t1.isAfter(t0)) return null;
|
const days = t1.diff(t0, "millisecond") / (24 * 60 * 60 * 1000);
|
return Math.round(days * 100) / 100;
|
}
|
|
// 显示模板字段(过滤掉已在自定义区域显示的字段)
|
function displayTemplateFields(fields = []) {
|
return (fields || []).filter((f) => {
|
const label = String(f?.label || "");
|
return !label.includes("预计使用天数") && !label.includes("车辆状态");
|
});
|
}
|
|
// 车辆使用信息显示
|
function vehicleUseInfoDisplay(form) {
|
const vehicleNoField = findVehicleNoField(form.formFieldDefs);
|
return !!vehicleNoField?.key && !!form.formPayload?.[vehicleNoField?.key];
|
}
|
|
// 车辆使用时长显示
|
function vehicleDurationDisplay(form) {
|
const useTimeField = findVehicleUseTimeField(form.formFieldDefs);
|
const { start, end } = resolveTimeRange(form.formPayload, useTimeField);
|
const d = computeDays(start, end);
|
return d == null ? "" : String(d);
|
}
|
|
// 车辆状态标签类型
|
function vehicleStatusType(vehicleNo) {
|
const vehicle = vehicleList.value.find((v) => v.plateNumber === vehicleNo);
|
if (!vehicle) return "info";
|
const typeMap = {
|
idle: "success",
|
in_use: "warning",
|
repair: "danger",
|
maintenance: "info",
|
};
|
return typeMap[vehicle.status] || "info";
|
}
|
|
// 车辆状态标签文字
|
function vehicleStatusText(vehicleNo) {
|
const vehicle = vehicleList.value.find((v) => v.plateNumber === vehicleNo);
|
if (!vehicle) return "未知";
|
const labelMap = {
|
idle: "闲置",
|
in_use: "使用中",
|
repair: "维修中",
|
maintenance: "保养中",
|
};
|
return labelMap[vehicle.status] || vehicle.status;
|
}
|
|
// 验证车辆使用时间(目前不做时间先后校验,由后端处理)
|
function validateVehicleUseTime() {
|
// 时间校验已移除
|
}
|
|
const searchForm = reactive({
|
instanceNo: "",
|
applicantKeyword: "",
|
vehiclePlateNumber: "",
|
});
|
|
// ==================== 借出记录列表查询 ====================
|
const borrowTableData = ref([]);
|
const borrowTableLoading = ref(false);
|
const borrowPage = reactive({
|
current: 1,
|
size: 10,
|
total: 0,
|
});
|
const selectedBorrowRows = ref([]);
|
|
// 查询借出记录列表
|
async function fetchBorrowList() {
|
borrowTableLoading.value = true;
|
try {
|
const res = await listBorrowPage({
|
current: borrowPage.current,
|
size: borrowPage.size,
|
borrowNo: searchForm.instanceNo,
|
applicantName: searchForm.applicantKeyword,
|
vehiclePlateNumber: searchForm.vehiclePlateNumber,
|
});
|
if (res.code === 200) {
|
borrowTableData.value = res.data?.records || [];
|
borrowPage.total = res.data?.total || 0;
|
}
|
} catch (error) {
|
ElMessage.error(error?.message || "查询失败");
|
} finally {
|
borrowTableLoading.value = false;
|
}
|
}
|
|
// 借出记录分页
|
function onBorrowPagination(obj) {
|
borrowPage.current = obj.page;
|
borrowPage.size = obj.limit;
|
fetchBorrowList();
|
}
|
|
// 批量删除借出记录
|
async function batchDeleteBorrow() {
|
if (!selectedBorrowRows.value?.length) {
|
ElMessage.warning("请选择要删除的记录");
|
return;
|
}
|
try {
|
await ElMessageBox.confirm("确定删除选中的借出记录吗?", "提示", {
|
type: "warning",
|
});
|
const ids = selectedBorrowRows.value.map(row => row.id);
|
const res = await deleteBorrow(ids);
|
if (res.code === 200) {
|
ElMessage.success("删除成功");
|
selectedBorrowRows.value = [];
|
fetchBorrowList();
|
} else {
|
ElMessage.error(res.msg || "删除失败");
|
}
|
} catch (error) {
|
if (error !== "cancel") {
|
ElMessage.error(error?.message || "删除失败");
|
}
|
}
|
}
|
|
const mod = useApprovalInstanceModule({
|
moduleKey: APPROVAL_MODULE_KEYS.VEHICLE,
|
beforeSave: validateVehicleUseTime,
|
});
|
|
const {
|
tableData,
|
tableLoading,
|
page,
|
detailDialog,
|
detailRow,
|
submitDialog,
|
submitForm,
|
submitFormRef,
|
submitSaving,
|
isSubmitEdit,
|
activeTemplate,
|
submitFormFields,
|
submitFormRules,
|
submitDialogTitle,
|
templateBindVisible,
|
handleQuery,
|
initModuleList,
|
pagination,
|
openAddWithTemplate,
|
onTemplateBound,
|
onTemplateBindClosed,
|
openEditFromDetail,
|
submitInstanceForm,
|
buildTableActions,
|
} = mod;
|
|
const { flowUserOptions } = useFlowUserOptions();
|
|
// 判断是否过了计划归还时间(是否可以延期)
|
function canExtend(row) {
|
if (!["pending", "approved"].includes(row?.approvalStatus) || row?.businessType !== 19) {
|
return false;
|
}
|
// 从车辆使用时间字段获取计划归还时间
|
const useTimeField = findVehicleUseTimeField(row.formFieldDefs || []);
|
if (useTimeField?.key && row.formPayload?.[useTimeField.key]) {
|
const timeRange = row.formPayload[useTimeField.key];
|
if (Array.isArray(timeRange) && timeRange.length >= 2) {
|
const plannedReturnTime = dayjs(timeRange[1]);
|
// 过了计划归还时间才能延期
|
return dayjs().isAfter(plannedReturnTime);
|
}
|
}
|
return false;
|
}
|
|
// 借出状态标签类型
|
function borrowStatusTagType(status) {
|
const typeMap = {
|
DRAFT: "info",
|
IN_APPROVAL: "warning",
|
BORROWING: "success",
|
RETURNED: "",
|
REJECTED: "danger",
|
};
|
return typeMap[status] || "info";
|
}
|
|
// 借出状态文本
|
function borrowStatusLabel(status) {
|
const labelMap = {
|
DRAFT: "草稿",
|
IN_APPROVAL: "审批中",
|
BORROWING: "借出中",
|
RETURNED: "已归还",
|
REJECTED: "已驳回",
|
};
|
return labelMap[status] || status;
|
}
|
|
// 延期状态标签类型
|
function extendStatusTagType(status) {
|
const typeMap = {
|
NONE: "info",
|
PENDING: "warning",
|
APPROVED: "success",
|
REJECTED: "danger",
|
};
|
return typeMap[status] || "info";
|
}
|
|
// 延期状态文本
|
function extendStatusLabel(status) {
|
const labelMap = {
|
NONE: "未申请",
|
PENDING: "审批中",
|
APPROVED: "已通过",
|
REJECTED: "已驳回",
|
};
|
return labelMap[status] || status;
|
}
|
|
// 判断是否可以延期(借出中且未申请延期或延期已驳回)
|
function canExtendBorrow(row) {
|
if (row.borrowStatus !== "BORROWING") return false;
|
if (row.extendStatus === "PENDING" || row.extendStatus === "APPROVED") return false;
|
// 过了计划归还时间才能延期
|
if (row.plannedReturnTime) {
|
return dayjs().isAfter(dayjs(row.plannedReturnTime));
|
}
|
return false;
|
}
|
|
// 判断是否可以归还(借出中)
|
function canReturnBorrow(row) {
|
return row.borrowStatus === "BORROWING";
|
}
|
|
// 构建借出记录表格列
|
const borrowTableColumn = computed(() => [
|
{ label: "借出单号", prop: "borrowNo", minWidth: 160, align: "center" },
|
{ label: "车牌号", prop: "vehiclePlateNumber", minWidth: 120, align: "center" },
|
{ label: "申请人", prop: "applicantName", minWidth: 100, align: "center" },
|
{ label: "借出原因", prop: "borrowReason", minWidth: 150, align: "center", showOverflowTooltip: true },
|
{ label: "借出时间", prop: "borrowStartTime", width: 160, align: "center" },
|
{ label: "计划归还", prop: "plannedReturnTime", width: 160, align: "center" },
|
{ label: "实际归还", prop: "actualReturnTime", width: 160, align: "center" },
|
{
|
label: "借出状态",
|
prop: "borrowStatus",
|
width: 100,
|
align: "center",
|
dataType: "tag",
|
formatData: borrowStatusLabel,
|
formatType: borrowStatusTagType,
|
},
|
{
|
label: "延期状态",
|
prop: "extendStatus",
|
width: 100,
|
align: "center",
|
dataType: "tag",
|
formatData: extendStatusLabel,
|
formatType: extendStatusTagType,
|
},
|
{ label: "创建时间", prop: "createTime", width: 160, align: "center" },
|
{
|
dataType: "action",
|
label: "操作",
|
align: "center",
|
fixed: "right",
|
width: 150,
|
operation: [
|
{
|
name: "延期",
|
type: "text",
|
disabled: (row) => !canExtendBorrow(row),
|
clickFun: (row) => openExtendDialog(row),
|
},
|
{
|
name: "归还",
|
type: "text",
|
disabled: (row) => !canReturnBorrow(row),
|
clickFun: (row) => openReturnDialog(row),
|
},
|
],
|
},
|
]);
|
|
async function onSearch() {
|
// 同时刷新车辆列表(用于车牌号下拉选择)
|
await fetchVehicleList();
|
// 使用借出记录查询接口
|
fetchBorrowList();
|
}
|
|
function resetSearch() {
|
searchForm.instanceNo = "";
|
searchForm.applicantKeyword = "";
|
searchForm.vehiclePlateNumber = "";
|
onSearch();
|
}
|
|
function onPagination(obj) {
|
pagination(obj, searchForm);
|
}
|
|
async function onSubmit() {
|
// 新增借出申请,只调用 saveBorrow 接口
|
if (!isSubmitEdit.value) {
|
try {
|
// 从表单数据中获取借出信息
|
const formPayload = submitForm.formPayload || {};
|
const fields = submitForm.formFieldDefs || [];
|
|
// 查找车辆选择字段(先尝试使用项目中已有的查找方式,再兼容直接取值)
|
const vehicleField = findVehicleNoField(fields);
|
|
// 车辆选择字段的值是车牌号,需要从vehicleList中根据车牌号查找对应的车辆ID
|
// 优先从 formPayload.vehiclePlateNumber 获取,如果不存在则通过字段key获取
|
const selectedPlateNumber = formPayload.vehiclePlateNumber ||
|
(vehicleField?.key ? formPayload[vehicleField.key] : null);
|
|
let vehicleId = null;
|
if (selectedPlateNumber) {
|
const selectedVehicle = vehicleList.value.find(v => v.plateNumber === selectedPlateNumber);
|
vehicleId = selectedVehicle?.id;
|
}
|
|
// 直接使用表单中的时间字段
|
const borrowStartTime = formPayload.borrowStartTime;
|
const plannedReturnTime = formPayload.plannedReturnTime;
|
|
if (!vehicleId) {
|
ElMessage.warning("请选择车辆");
|
return;
|
}
|
if (!borrowStartTime || !plannedReturnTime) {
|
ElMessage.warning("请选择车辆使用时间");
|
return;
|
}
|
|
// 只调用 saveBorrow 接口,不再调用审批实例提交
|
// formConfig 是模板详情接口返回的原始 JSON 字符串
|
console.log('=== submitForm debug ===', {
|
templateId: submitForm.templateId,
|
templateKey: submitForm.templateKey,
|
templateSnapshotTemplateId: submitForm.templateSnapshot?.templateId,
|
formConfig: submitForm.formConfig || submitForm.templateSnapshot?.formConfig || activeTemplate.value?.formConfig,
|
'formConfig type': typeof submitForm.formConfig,
|
});
|
const submitData = {
|
vehicleId: vehicleId,
|
borrowReason: formPayload.borrowReason || formPayload.reason || '',
|
borrowStartTime: borrowStartTime,
|
plannedReturnTime: plannedReturnTime,
|
borrowStatus: 'IN_APPROVAL',
|
approvalTemplateId: submitForm.templateId || submitForm.templateSnapshot?.templateId || submitForm.templateKey,
|
borrowStorageBlobDTOs: submitForm.storageBlobDTOs || [],
|
formConfig: submitForm.formConfig,
|
};
|
console.log('=== saveBorrow submitData ===', JSON.parse(JSON.stringify(submitData)));
|
const borrowRes = await saveBorrow(submitData);
|
|
if (borrowRes.code !== 200) {
|
ElMessage.error(borrowRes.msg || "保存借出记录失败");
|
return;
|
}
|
|
ElMessage.success("提交成功");
|
submitDialog.visible = false;
|
fetchBorrowList();
|
} catch (error) {
|
ElMessage.error(error?.message || "保存借出记录失败");
|
}
|
} else {
|
// 编辑模式,调用审批实例提交
|
const ok = await submitInstanceForm({ skipValidate: true });
|
if (ok) ElMessage.success("修改成功");
|
}
|
}
|
|
// ==================== 延期申请(使用审批模板) ====================
|
const extendTemplateBindVisible = ref(false);
|
const extendSubmitDialog = reactive({
|
visible: false,
|
});
|
const extendSubmitFormRef = ref(null);
|
const extendSubmitSaving = ref(false);
|
const extendSubmitForm = reactive({
|
templateId: null,
|
templateName: "",
|
formPayload: {},
|
flowNodes: [],
|
storageBlobDTOs: [],
|
formFieldDefs: [],
|
});
|
const extendSubmitFormRules = reactive({});
|
const extendSubmitFormFields = ref([]);
|
const extendActiveTemplate = ref({});
|
const extendSubmitDialogTitle = computed(() => "车辆延期申请");
|
const extendSourceRow = reactive({
|
id: null,
|
instanceNo: "",
|
vehiclePlateNumber: "",
|
originalEndDate: "",
|
});
|
|
// 打开延期申请对话框(先选择模板)
|
function openExtendDialog(row) {
|
// 保存原审批信息
|
extendSourceRow.id = row.id;
|
extendSourceRow.instanceNo = row.instanceNo || "";
|
// 尝试从多个可能的字段名中获取车牌号
|
const payload = row.formPayload || {};
|
extendSourceRow.vehiclePlateNumber = payload.vehicleNo || payload.plateNumber || payload.vehiclePlateNumber || payload.carNo || "";
|
// 从车辆使用时间计算原到期日期
|
const useTimeField = findVehicleUseTimeField(row.formFieldDefs || []);
|
if (useTimeField?.key && row.formPayload?.[useTimeField.key]) {
|
const timeRange = row.formPayload[useTimeField.key];
|
if (Array.isArray(timeRange) && timeRange.length >= 2) {
|
extendSourceRow.originalEndDate = timeRange[1];
|
}
|
}
|
// 打开模板选择对话框
|
extendTemplateBindVisible.value = true;
|
}
|
|
// 延期模板绑定确认
|
function onExtendTemplateBound(payload) {
|
const { templateId, templateName, formFieldDefs, formPayload, flowNodes, templateAttachments, storageBlobDTOs } = payload;
|
extendActiveTemplate.value = { id: templateId, name: templateName, fields: formFieldDefs };
|
extendSubmitForm.templateId = templateId;
|
extendSubmitForm.templateName = templateName;
|
extendSubmitForm.formFieldDefs = formFieldDefs || [];
|
extendSubmitFormFields.value = formFieldDefs || [];
|
// 初始化表单数据
|
extendSubmitForm.formPayload = formPayload || {};
|
extendSubmitForm.flowNodes = flowNodes || [];
|
extendSubmitForm.templateAttachments = templateAttachments || [];
|
extendSubmitForm.storageBlobDTOs = storageBlobDTOs || [];
|
// 打开提交对话框
|
extendSubmitDialog.visible = true;
|
}
|
|
// 延期模板绑定关闭
|
function onExtendTemplateBindClosed() {
|
// 清理状态
|
}
|
|
// 提交延期申请
|
async function onExtendSubmit() {
|
if (!extendSubmitFormRef.value) return;
|
|
extendSubmitSaving.value = true;
|
try {
|
// 构建提交数据
|
// formConfig 是模板详情接口返回的原始 JSON 字符串
|
const payload = {
|
...extendSubmitForm.formPayload,
|
originalInstanceNo: extendSourceRow.instanceNo,
|
vehiclePlateNumber: extendSourceRow.vehiclePlateNumber,
|
originalEndDate: extendSourceRow.originalEndDate,
|
formConfig: extendSubmitForm.formConfig,
|
};
|
|
// 这里调用创建延期审批实例的API
|
// await createVehicleDelayApproval({
|
// templateId: extendSubmitForm.templateId,
|
// formPayload: payload,
|
// flowNodes: extendSubmitForm.flowNodes,
|
// storageBlobDTOs: extendSubmitForm.storageBlobDTOs,
|
// formConfig: extendSubmitForm.formFieldDefs || [],
|
// });
|
|
ElMessage.success("延期申请已提交");
|
extendSubmitDialog.visible = false;
|
onSearch();
|
} catch (error) {
|
ElMessage.error(error?.message || "提交失败");
|
} finally {
|
extendSubmitSaving.value = false;
|
}
|
}
|
|
// ==================== 归还车辆 ====================
|
const returnDialog = reactive({
|
visible: false,
|
});
|
const returnLoading = ref(false);
|
const returnFormRef = ref(null);
|
const returnForm = reactive({
|
id: null,
|
instanceNo: "",
|
vehiclePlateNumber: "",
|
actualReturnTime: "",
|
returnStorageBlobDTOs: [],
|
});
|
const returnRules = {
|
actualReturnTime: [{ required: true, message: "请选择实际归还时间", trigger: "change" }],
|
};
|
|
// 打开归还车辆对话框
|
function openReturnDialog(row) {
|
returnForm.id = row.id;
|
returnForm.instanceNo = row.instanceNo || "";
|
// 尝试从多个可能的字段名中获取车牌号
|
const payload = row.formPayload || {};
|
returnForm.vehiclePlateNumber = payload.vehicleNo || payload.plateNumber || payload.vehiclePlateNumber || payload.carNo || "";
|
returnForm.actualReturnTime = dayjs().format("YYYY-MM-DD HH:mm:ss");
|
returnForm.returnStorageBlobDTOs = [];
|
returnDialog.visible = true;
|
}
|
|
// 提交归还
|
async function submitReturn() {
|
if (!returnFormRef.value) return;
|
await returnFormRef.value.validate(async (valid) => {
|
if (!valid) return;
|
|
returnLoading.value = true;
|
try {
|
// 这里调用归还车辆的API
|
// 1. 更新车辆公里数
|
const vehicle = vehicleList.value.find((v) => v.plateNumber === returnForm.vehiclePlateNumber);
|
if (vehicle) {
|
vehicle.currentMileage += returnForm.mileage;
|
vehicle.status = returnForm.vehicleStatus === "good" ? "idle" : "repair";
|
}
|
|
// 2. 创建归还审批记录(实际项目中调用API)
|
// await createVehicleReturnApproval({...});
|
|
ElMessage.success("车辆归还成功");
|
returnDialog.visible = false;
|
onSearch();
|
} catch (error) {
|
ElMessage.error(error?.message || "归还失败");
|
} finally {
|
returnLoading.value = false;
|
}
|
});
|
}
|
|
onMounted(() => {
|
fetchVehicleList();
|
// 使用借出记录查询接口
|
fetchBorrowList();
|
});
|
</script>
|
|
<style scoped>
|
.search_form {
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
flex-wrap: wrap;
|
gap: 12px;
|
}
|
|
.search_title {
|
font-size: 14px;
|
color: #606266;
|
}
|
|
.mb20 {
|
margin-bottom: 20px;
|
}
|
|
.table_list {
|
margin-top: 10px;
|
}
|
|
.vehicle-manage-card {
|
margin-bottom: 20px;
|
}
|
|
.card-header {
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
}
|
|
.pagination-container {
|
display: flex;
|
justify-content: flex-end;
|
}
|
</style>
|