Files
tms-erp-web/src/views/transportCapacity/vehicle.vue
T
2026-09-10 22:50:18 +08:00

1787 lines
62 KiB
Vue
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<basic-container class="transport-vehicle-page">
<avue-crud
:option="option"
:table-loading="loading"
:data="data"
v-model:page="page"
v-model="form"
ref="crud"
:permission="permissionList"
@search-change="searchChange"
@search-reset="searchReset"
@selection-change="selectionChange"
@current-change="currentChange"
@size-change="sizeChange"
@refresh-change="refreshChange"
@on-load="onLoad"
>
<template #menu-left>
<el-button
type="primary"
icon="el-icon-plus"
v-if="hasPermission('transport_vehicle_add')"
@click="openVehicle()"
>新增
</el-button>
<el-button
type="primary"
icon="el-icon-download"
plain
v-if="hasPermission('transport_vehicle_export')"
@click="handleExport"
>导出
</el-button>
<el-button
type="danger"
icon="el-icon-delete"
plain
v-if="hasPermission('transport_vehicle_delete')"
@click="handleDelete"
>批量删除
</el-button>
</template>
<template #header>
<div class="transport-vehicle-page__expiry-tags">
<el-check-tag
v-for="item in expiryTagOptions"
:key="item.value"
:checked="query.expireStatus === item.value"
@change="handleExpireChange(item.value)"
>
{{ item.label }}{{ expiryStat[item.statKey] || 0 }}
</el-check-tag>
</div>
</template>
<template #plateNo="{ row }">
<el-button type="primary" link @click="openVehicle(row, true)">
{{ row.plateNo }}
</el-button>
</template>
<template #organizationName="{ row }">
{{ String(row.organizationName || '').replace(/^[\s\u3000]+/, '') }}
</template>
<template #status="{ row }">
<el-tag :type="row.status === 1 ? 'primary' : 'info'" class="status-text">
{{ row.status === 1 ? '启用' : '停用' }}
</el-tag>
</template>
<template #boundDriver="{ row }">
<span>{{ row.boundDriver || '-' }}</span>
</template>
<template #certificationStatus="{ row }">
<el-tag :type="getCertificationStatus(row).type" class="status-text">
{{ getCertificationStatus(row).label }}
</el-tag>
</template>
<template #drivingLicenseEndDate="{ row }">
<span :class="getDateClass(row, 'drivingLicenseEndDate', 'drivingLicenseLongTerm')">
{{ formatEndDate(row.drivingLicenseEndDate, row.drivingLicenseLongTerm) }}
</span>
</template>
<template #roadTransportCertEndDate="{ row }">
<span :class="getDateClass(row, 'roadTransportCertEndDate', 'roadTransportCertLongTerm')">
{{ formatEndDate(row.roadTransportCertEndDate, row.roadTransportCertLongTerm) }}
</span>
</template>
<template #annualReviewEndDate="{ row }">
<span :class="getDateClass(row, 'annualReviewEndDate', 'annualReviewLongTerm')">
{{ formatEndDate(row.annualReviewEndDate, row.annualReviewLongTerm) }}
</span>
</template>
<template #menu="{ row }">
<el-link
type="primary"
v-if="hasPermission('transport_vehicle_view')"
@click="openVehicle(row, true)"
>
查看
</el-link>
<el-link
type="primary"
v-if="hasPermission('transport_vehicle_edit')"
@click="openVehicle(row)"
>
修改
</el-link>
<el-link type="primary" @click="openVehicleLedger(row)">综合台账</el-link>
<el-link type="primary" v-if="row.certificationStatus === 0" @click="openCertificationAudit(row)">
审核
</el-link>
<el-link
type="primary"
v-if="hasPermission('transport_vehicle_status')"
@click="handleStatus(row)"
>
{{ row.status === 1 ? '停用' : '启用' }}
</el-link>
</template>
</avue-crud>
<vehicle-ledger v-model="vehicleLedgerBox" :vehicle="ledgerVehicle" />
<el-dialog
v-model="certificationAuditBox"
title="车辆认证审核"
width="92%"
top="4vh"
class="certification-audit-dialog"
>
<el-form label-position="right" label-width="auto" class="certification-audit archive-form">
<section-card title="基础信息">
<el-row :gutter="18">
<el-col :span="6">
<el-form-item label="车牌号">
<span class="certification-audit__value">{{ auditValue('plateNo') }}</span>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="车辆类型">
<span class="certification-audit__value">{{ auditValue('vehicleType') }}</span>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="外廓尺寸(毫米)">
<div class="dimension-group">
<div class="dimension-group__item">
<span class="dimension-group__label"></span>
<span class="certification-audit__value">{{ auditValue('outerLength') }}</span>
</div>
<div class="dimension-group__item">
<span class="dimension-group__label"></span>
<span class="certification-audit__value">{{ auditValue('outerWidth') }}</span>
</div>
<div class="dimension-group__item">
<span class="dimension-group__label"></span>
<span class="certification-audit__value">{{ auditValue('outerHeight') }}</span>
</div>
</div>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="18">
<el-col :span="6">
<el-form-item label="核定载质量(KG">
<span class="certification-audit__value">{{ auditValue('approvedLoadKg') }}</span>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="准牵引总质量(KG">
<span class="certification-audit__value">{{ auditValue('tractionMassKg') }}</span>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="业务关系">
<span class="certification-audit__value">{{ auditValue('businessRelation') }}</span>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="能源类型">
<span class="certification-audit__value">{{ auditValue('energyType') }}</span>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="18">
<el-col :span="6">
<el-form-item label="强制报废日期">
<span class="certification-audit__value">
{{ auditValue('compulsoryScrapDate') }}
</span>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="海关备案号">
<span class="certification-audit__value">{{ auditValue('customsRecordNo') }}</span>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="所有组织">
<span class="certification-audit__value">
{{ auditValue('organizationName') }}
</span>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="使用部门">
<span class="certification-audit__value">{{ auditValue('useDepartment') }}</span>
</el-form-item>
</el-col>
</el-row>
</section-card>
<section-card title="证件信息">
<el-row :gutter="18">
<el-col :span="6">
<el-form-item label="行驶证有效期止">
<span class="certification-audit__value">
{{ auditValue('drivingLicenseEndDate') }}
</span>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="道路运输证号">
<span class="certification-audit__value">
{{ auditValue('roadTransportCertNo') }}
</span>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="道路运输证有效期止">
<span class="certification-audit__value">
{{ auditValue('roadTransportCertEndDate') }}
</span>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="道路运输年审有效期">
<span class="certification-audit__value">
{{ auditValue('annualReviewEndDate') }}
</span>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="18">
<el-col :span="6">
<el-form-item label="机动车登记编号">
<span class="certification-audit__value">{{ auditValue('registrationNo') }}</span>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="机动车登记日期">
<span class="certification-audit__value">{{ auditValue('registrationDate') }}</span>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="行驶证档案编号">
<span class="certification-audit__value">{{ auditValue('drivingLicenseNo') }}</span>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="行驶证有效期起">
<span class="certification-audit__value">
{{ auditValue('drivingLicenseStartDate') }}
</span>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="18">
<el-col :span="6">
<el-form-item label="道路运输证有效期起">
<span class="certification-audit__value">
{{ auditValue('roadTransportCertStartDate') }}
</span>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="18">
<el-col :span="24">
<el-form-item label="备注">
<span class="certification-audit__value">{{ auditValue('remark') }}</span>
</el-form-item>
</el-col>
</el-row>
</section-card>
<section-card title="证件图片">
<el-row :gutter="18">
<el-col :span="6">
<image-upload-field
label="行驶证主页正面"
prop="drivingLicenseImage"
:value="certificationAuditForm.drivingLicenseImage"
readonly
large
class-prefix="vehicle"
/>
</el-col>
<el-col :span="6">
<image-upload-field
label="行驶证主页反面"
prop="drivingLicenseMainBack"
:value="certificationAuditForm.drivingLicenseMainBack"
readonly
large
class-prefix="vehicle"
/>
</el-col>
<el-col :span="6">
<image-upload-field
label="行驶证副页正面"
prop="drivingLicenseViceFront"
:value="certificationAuditForm.drivingLicenseViceFront"
readonly
large
class-prefix="vehicle"
/>
</el-col>
</el-row>
<el-row :gutter="18">
<el-col :span="6">
<image-upload-field
label="行驶证副页反面"
prop="drivingLicenseViceBack"
:value="certificationAuditForm.drivingLicenseViceBack"
readonly
large
class-prefix="vehicle"
/>
</el-col>
<el-col :span="6">
<image-upload-field
label="道路运输证图片"
prop="roadTransportCertImage"
:value="certificationAuditForm.roadTransportCertImage"
readonly
large
class-prefix="vehicle"
/>
</el-col>
<el-col :span="6">
<image-upload-field
label="机动车登记本"
prop="registrationImage"
:value="certificationAuditForm.registrationImage"
readonly
large
class-prefix="vehicle"
/>
</el-col>
</el-row>
</section-card>
</el-form>
<el-form
v-if="showRejectReason"
class="certification-audit__reason"
label-position="right"
label-width="auto"
>
<el-form-item label="驳回原因" required>
<el-input
v-model="certificationRejectReason"
type="textarea"
maxlength="200"
show-word-limit
/>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="certificationAuditBox = false">取消</el-button>
<el-button type="danger" @click="handleCertificationReject">认证驳回</el-button>
<el-button type="primary" @click="handleCertificationApprove">认证通过</el-button>
</template>
</el-dialog>
<el-dialog
:title="dialogTitle"
append-to-body
v-model="vehicleBox"
width="92%"
top="4vh"
class="vehicle-dialog"
@closed="resetVehicle"
>
<el-form
ref="vehicleForm"
:model="vehicleForm"
:rules="formRules"
label-position="right"
:disabled="readonly"
class="vehicle-form archive-form"
>
<section-card title="基础车辆信息">
<el-row :gutter="18">
<el-col :span="6">
<el-form-item label="车牌号" prop="plateNo">
<el-input
v-model="vehicleForm.plateNo"
maxlength="8"
show-word-limit
placeholder="如:京A12345"
@input="handlePlateNoInput"
@blur="validatePlateFormat"
/>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="车辆类型" prop="vehicleType">
<el-select
v-model="vehicleForm.vehicleType"
filterable
allow-create
default-first-option
>
<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="外廓尺寸(毫米)">
<div class="dimension-group">
<div class="dimension-group__item">
<span class="dimension-group__label"></span>
<el-input
v-model="vehicleForm.outerLength"
inputmode="numeric"
@input="vehicleForm.outerLength = digitsOnly($event)"
/>
</div>
<div class="dimension-group__item">
<span class="dimension-group__label"></span>
<el-input
v-model="vehicleForm.outerWidth"
inputmode="numeric"
@input="vehicleForm.outerWidth = digitsOnly($event)"
/>
</div>
<div class="dimension-group__item">
<span class="dimension-group__label"></span>
<el-input
v-model="vehicleForm.outerHeight"
inputmode="numeric"
@input="vehicleForm.outerHeight = digitsOnly($event)"
/>
</div>
</div>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="18">
<el-col :span="6">
<el-form-item label="核定载质量(KG" prop="approvedLoadKg">
<el-input
v-model="vehicleForm.approvedLoadKg"
inputmode="numeric"
placeholder="请输入"
@input="vehicleForm.approvedLoadKg = digitsOnly($event)"
/>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="准牵引总质量(KG" prop="tractionMassKg">
<el-input
v-model="vehicleForm.tractionMassKg"
inputmode="numeric"
placeholder="请输入"
@input="vehicleForm.tractionMassKg = digitsOnly($event)"
/>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="业务关系" prop="businessRelation">
<el-select v-model="vehicleForm.businessRelation">
<el-option
v-for="item in businessRelationOptions"
:key="item"
:label="item"
:value="item"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="能源类型" prop="energyType">
<el-select
v-model="vehicleForm.energyType"
filterable
allow-create
default-first-option
>
<el-option
v-for="item in energyTypeOptions"
:key="item"
:label="item"
:value="item"
/>
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="18">
<el-col :span="6">
<el-form-item label="强制报废日期" prop="compulsoryScrapDate">
<div class="date-with-flag">
<el-date-picker
v-model="vehicleForm.compulsoryScrapDate"
type="date"
value-format="YYYY-MM-DD"
:disabled="vehicleForm.compulsoryScrapLongTerm === 1"
/>
<el-checkbox
v-model="vehicleForm.compulsoryScrapLongTerm"
:true-label="1"
:false-label="0"
@change="handleLongTermChange('compulsoryScrapDate', 'compulsoryScrapLongTerm')"
>
长期有效
</el-checkbox>
</div>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="海关备案号" prop="customsRecordNo">
<el-input v-model="vehicleForm.customsRecordNo" maxlength="50" />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="所有组织" prop="organizationName">
<el-cascader
v-model="organizationCascaderValue"
:options="organizationTreeOptions"
:props="organizationCascaderProps"
filterable
clearable
placeholder="请选择"
/>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="使用部门" prop="useDepartment">
<el-cascader
v-model="useDepartmentCascaderValue"
:options="organizationTreeOptions"
:props="organizationCascaderProps"
filterable
clearable
placeholder="请选择"
/>
</el-form-item>
</el-col>
</el-row>
</section-card>
<section-card title="车辆资质图片">
<el-row :gutter="18">
<el-col :span="6">
<el-form-item label="行驶证有效期" prop="drivingLicenseEndDate">
<div class="date-with-flag">
<el-date-picker
v-model="vehicleForm.drivingLicenseEndDate"
type="date"
value-format="YYYY-MM-DD"
:disabled="vehicleForm.drivingLicenseLongTerm === 1"
/>
<el-checkbox
v-model="vehicleForm.drivingLicenseLongTerm"
:true-label="1"
:false-label="0"
@change="handleLongTermChange('drivingLicenseEndDate', 'drivingLicenseLongTerm')"
>
长期有效
</el-checkbox>
</div>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="道路运输证号" prop="roadTransportCertNo">
<el-input v-model="vehicleForm.roadTransportCertNo" maxlength="50" />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="道路运输证有效期" prop="roadTransportCertEndDate">
<div class="date-with-flag">
<el-date-picker
v-model="vehicleForm.roadTransportCertEndDate"
type="date"
value-format="YYYY-MM-DD"
:disabled="vehicleForm.roadTransportCertLongTerm === 1"
/>
<el-checkbox
v-model="vehicleForm.roadTransportCertLongTerm"
:true-label="1"
:false-label="0"
@change="handleLongTermChange('roadTransportCertEndDate', 'roadTransportCertLongTerm')"
>
长期有效
</el-checkbox>
</div>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="道路运输年审有效期" prop="annualReviewEndDate">
<div class="date-with-flag">
<el-date-picker
v-model="vehicleForm.annualReviewEndDate"
type="date"
value-format="YYYY-MM-DD"
:disabled="vehicleForm.annualReviewLongTerm === 1"
/>
<el-checkbox
v-model="vehicleForm.annualReviewLongTerm"
:true-label="1"
:false-label="0"
@change="handleLongTermChange('annualReviewEndDate', 'annualReviewLongTerm')"
>
长期有效
</el-checkbox>
</div>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="18">
<el-col :span="6">
<el-form-item label="机动车登记编号" prop="registrationNo">
<el-input v-model="vehicleForm.registrationNo" maxlength="50" />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="机动车登记日期" prop="registrationDate">
<el-date-picker
v-model="vehicleForm.registrationDate"
type="date"
value-format="YYYY-MM-DD"
/>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="行驶证档案编号" prop="drivingLicenseNo">
<el-input v-model="vehicleForm.drivingLicenseNo" maxlength="50" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="18">
<el-col :span="6">
<image-upload-field
label="行驶证主页正面"
prop="drivingLicenseImage"
:value="vehicleForm.drivingLicenseImage"
:readonly="readonly"
:headers="uploadHeaders"
large
class-prefix="vehicle"
@success="
(url, file) =>
handleVehicleCertificateUploadSuccess('drivingLicenseImage', url, file)
"
/>
</el-col>
<el-col :span="6">
<image-upload-field
label="行驶证主页反面"
prop="drivingLicenseMainBack"
:value="vehicleForm.drivingLicenseMainBack"
:readonly="readonly"
:headers="uploadHeaders"
large
class-prefix="vehicle"
@success="
(url, file) =>
handleVehicleCertificateUploadSuccess('drivingLicenseMainBack', url, file)
"
/>
</el-col>
<el-col :span="6">
<image-upload-field
label="行驶证副页正面"
prop="drivingLicenseViceFront"
:value="vehicleForm.drivingLicenseViceFront"
:readonly="readonly"
:headers="uploadHeaders"
large
class-prefix="vehicle"
@success="
(url, file) =>
handleVehicleCertificateUploadSuccess('drivingLicenseViceFront', url, file)
"
/>
</el-col>
</el-row>
<el-row :gutter="18">
<el-col :span="6">
<image-upload-field
label="行驶证副页反面"
prop="drivingLicenseViceBack"
:value="vehicleForm.drivingLicenseViceBack"
:readonly="readonly"
:headers="uploadHeaders"
large
class-prefix="vehicle"
@success="
(url, file) =>
handleVehicleCertificateUploadSuccess('drivingLicenseViceBack', url, file)
"
/>
</el-col>
<el-col :span="6">
<image-upload-field
label="道路运输证图片"
prop="roadTransportCertImage"
:value="vehicleForm.roadTransportCertImage"
:readonly="readonly"
:headers="uploadHeaders"
large
class-prefix="vehicle"
@success="url => handleVehicleCertificateUploadSuccess('roadTransportCertImage', url)"
/>
</el-col>
<el-col :span="6">
<image-upload-field
label="机动车登记本"
prop="registrationImage"
:value="vehicleForm.registrationImage"
:readonly="readonly"
:headers="uploadHeaders"
large
class-prefix="vehicle"
@success="
(url, file) =>
handleVehicleCertificateUploadSuccess('registrationImage', url, file)
"
/>
</el-col>
</el-row>
</section-card>
</el-form>
<template #footer>
<el-button @click="vehicleBox = false">取消</el-button>
<el-button type="primary" v-if="!readonly" :loading="submitLoading" @click="handleSubmit">
确定
</el-button>
</template>
</el-dialog>
</basic-container>
</template>
<script>
import { mapGetters } from 'vuex';
import NProgress from 'nprogress';
import { ElLoading } from 'element-plus';
import { option } from '@/option/transportCapacity/transport-vehicle';
import {
getList,
getDetail,
submit,
remove,
changeStatus,
auditCertification,
getExpiryStat,
recognitionTransportCertificates,
recognizeBaiduOcr,
} from '@/api/transportCapacity/transport-vehicle';
import { getDeptTree } from '@/api/system/dept';
import organizationSearch from '@/mixins/organization-search';
import { exportBlob } from '@/api/common';
import { getToken } from '@/utils/auth';
import { getUploadHeaders } from '@/utils/upload';
import { downloadXls } from '@/utils/util';
import ImageUploadField from '@/components/image-upload-field/main.vue';
import VehicleLedger from './components/vehicle-ledger.vue';
const emptyForm = () => ({
id: '',
organizationName: '',
useDepartment: '',
plateNo: '',
plateColor: '',
vehicleType: '',
outerLength: undefined,
outerWidth: undefined,
outerHeight: undefined,
approvedLoadKg: undefined,
tractionMassKg: undefined,
businessRelation: '自有',
energyType: '',
compulsoryScrapDate: '',
compulsoryScrapLongTerm: 0,
customsRecordNo: '',
drivingLicenseNo: '',
drivingLicenseStartDate: '',
drivingLicenseEndDate: '',
drivingLicenseLongTerm: 0,
drivingLicenseImage: '',
drivingLicenseMainBack: '',
drivingLicenseViceFront: '',
drivingLicenseViceBack: '',
roadTransportCertNo: '',
roadTransportCertStartDate: '',
roadTransportCertEndDate: '',
roadTransportCertLongTerm: 0,
roadTransportCertImage: '',
annualReviewEndDate: '',
annualReviewLongTerm: 0,
registrationNo: '',
registrationDate: '',
registrationImage: '',
status: 1,
});
export default {
components: {
ImageUploadField,
VehicleLedger,
},
mixins: [organizationSearch],
data() {
return {
option,
form: {},
query: {
expireStatus: '',
},
searchForm: {
businessRelation: '',
},
loading: true,
submitLoading: false,
data: [],
ids: '',
selectionList: [],
page: {
pageSize: 10,
currentPage: 1,
total: 0,
},
expiryStat: {
total: 0,
within30: 0,
expired: 0,
},
vehicleBox: false,
vehicleLedgerBox: false,
ledgerVehicle: {},
certificationAuditBox: false,
certificationAuditForm: {},
certificationRejectReason: '',
showRejectReason: false,
readonly: false,
vehicleForm: emptyForm(),
vehicleCertificateUploads: {},
uploadHeaders: getUploadHeaders(),
organizationOptions: [],
vehicleTypeOptions: [
{ label: '高栏车', value: '高栏' },
{ label: '低栏车', value: '低栏' },
{ label: '平板车', value: '平板' },
{ label: '厢式车', value: '厢式' },
{ label: '拖挂车', value: '拖挂' },
{ label: '半挂车', value: '半挂' },
],
businessRelationOptions: ['自有', '管理挂靠', '业务合作'],
energyTypeOptions: ['柴油', '汽油', '新能源', '天然气'],
baseRules: {
organizationName: [{ required: true, message: '请选择所属组织', trigger: 'change' }],
plateNo: [
{ required: true, message: '请输入车牌号', trigger: 'change' },
{ validator: this.validatePlateNo, trigger: 'change' },
],
vehicleType: [{ required: true, message: '请选择车辆类型', trigger: 'change' }],
approvedLoadKg: [{ required: true, message: '请输入核定载质量', trigger: 'blur' }],
businessRelation: [{ required: true, message: '请选择业务关系', trigger: 'change' }],
energyType: [{ required: true, message: '请选择能源类型', trigger: 'change' }],
drivingLicenseNo: [{ required: true, message: '请输入行驶证档案编号', trigger: 'blur' }],
roadTransportCertNo: [{ required: true, message: '请输入道路运输证号', trigger: 'blur' }],
roadTransportCertEndDate: [
{ validator: this.validateRoadTransportCertEndDate, trigger: 'change' },
],
annualReviewEndDate: [{ validator: this.validateAnnualReviewEndDate, trigger: 'change' }],
registrationNo: [{ required: true, message: '请输入机动车登记编号', trigger: 'blur' }],
},
};
},
computed: {
...mapGetters(['permission', 'userInfo']),
// 行驶证有效期:用 required 规则渲染必填星号;勾选「长期有效」后免填,星号同步消失
formRules() {
return {
...this.baseRules,
drivingLicenseEndDate:
this.vehicleForm.drivingLicenseLongTerm === 1
? []
: [{ required: true, message: '请选择行驶证有效期', trigger: 'change' }],
};
},
// 所属组织级联:表单存组织名称,级联控件用 id 路径,两者在此双向转换
organizationCascaderValue: {
get() {
return this.resolveOrgPath(this.vehicleForm.organizationName);
},
set(path) {
this.vehicleForm.organizationName = this.resolveOrgName(path);
},
},
// 使用部门级联:与所属组织同一套部门树
useDepartmentCascaderValue: {
get() {
return this.resolveOrgPath(this.vehicleForm.useDepartment);
},
set(path) {
this.vehicleForm.useDepartment = this.resolveOrgName(path);
},
},
isAdmin() {
const authority = this.userInfo.authority || '';
return authority.includes('admin');
},
permissionList() {
return {
addBtn: this.hasPermission('transport_vehicle_add'),
viewBtn: this.hasPermission('transport_vehicle_view'),
delBtn: this.hasPermission('transport_vehicle_delete'),
editBtn: this.hasPermission('transport_vehicle_edit'),
};
},
dialogTitle() {
if (this.readonly) {
return '查看车辆';
}
return this.vehicleForm.id ? '修改车辆' : '新增车辆';
},
expiryTagOptions() {
return [
{ label: '全部', value: '', statKey: 'total' },
{ label: '30天内到期', value: 'within30', statKey: 'within30' },
{ label: '已到期', value: 'expired', statKey: 'expired' },
];
},
},
created() {
this.initDeptTree();
this.loadOrganizationOptions();
},
methods: {
hasPermission(code) {
return this.isAdmin || this.permission?.[code] === true;
},
initDeptTree() {
getDeptTree(this.userInfo.tenantId).then(res => {
this.organizationOptions = this.flattenDeptOptions(res.data.data || []);
const column = this.findColumn(this.option.column, 'organizationName');
column.dicData = this.organizationOptions;
if (this.vehicleBox && !this.vehicleForm.id && !this.vehicleForm.organizationName) {
this.vehicleForm.organizationName = this.getCurrentOrganizationName();
}
});
},
flattenDeptOptions(tree = [], level = 0) {
const result = [];
tree.forEach(item => {
const rawLabel = item.title || item.deptName || item.name;
result.push({
label: `${' '.repeat(level)}${rawLabel}`,
value: rawLabel,
rawLabel,
id: item.id || item.value,
});
if (item.children && item.children.length) {
result.push(...this.flattenDeptOptions(item.children, level + 1));
}
});
return result;
},
getCurrentOrganizationName() {
const currentDeptId = this.userInfo.deptId || this.userInfo.dept_id;
const currentDept = this.organizationOptions.find(
item => String(item.id) === String(currentDeptId)
);
return currentDept?.rawLabel || this.userInfo.deptName || this.userInfo.dept_name || '';
},
digitsOnly(value = '') {
return String(value).replace(/\D/g, '');
},
validatePlateNo(rule, value, callback) {
const plateNo = String(value || '')
.trim()
.toUpperCase();
if (!plateNo) {
callback(new Error('请输入车牌号'));
return;
}
if (!/^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼港澳]{1}[A-Z][A-Z0-9挂学警港澳]{5,6}$/.test(plateNo)) {
callback(new Error('请输入标准车牌号(如:京A12345)'));
return;
}
if (this.vehicleForm.plateNo !== plateNo) {
this.vehicleForm.plateNo = plateNo;
}
callback();
},
validateAnnualReviewEndDate(rule, value, callback) {
if (this.vehicleForm.annualReviewLongTerm !== 1 && !value) {
callback(new Error('请选择道路运输年审有效期'));
return;
}
callback();
},
// 长期有效勾选:勾选时清空对应日期(不可编辑由 date-picker :disabled 控制),取消勾选后可重新填报
handleLongTermChange(dateProp, longTermProp) {
if (this.vehicleForm[longTermProp] === 1) {
this.vehicleForm[dateProp] = '';
}
this.$refs.vehicleForm?.validateField(dateProp);
},
validateRoadTransportCertEndDate(rule, value, callback) {
if (this.vehicleForm.roadTransportCertLongTerm === 1 && !value) {
callback();
return;
}
if (!value) {
callback(new Error('请选择道路运输证有效期'));
return;
}
callback();
},
openVehicle(row, readonly = false) {
this.readonly = readonly;
if (!row?.id) {
this.vehicleForm = emptyForm();
this.vehicleForm.organizationName = this.getCurrentOrganizationName();
this.syncPlateNo(false);
this.vehicleBox = true;
return;
}
getDetail(row.id).then(res => {
this.vehicleForm = {
...emptyForm(),
...(res.data.data || {}),
};
this.splitPlateNo();
this.syncPlateNo(false);
this.vehicleBox = true;
});
},
openVehicleLedger(row) {
this.ledgerVehicle = { ...row };
this.vehicleLedgerBox = true;
},
openCertificationAudit(row) {
getDetail(row.id).then(res => {
this.certificationAuditForm = { ...(res.data.data || {}) };
this.certificationRejectReason = '';
this.showRejectReason = false;
this.certificationAuditBox = true;
});
},
auditValue(prop) {
// 长期有效字段:勾选后显示「长期有效」,避免与未填写的「-」混淆
const longTermMap = {
drivingLicenseEndDate: 'drivingLicenseLongTerm',
roadTransportCertEndDate: 'roadTransportCertLongTerm',
annualReviewEndDate: 'annualReviewLongTerm',
compulsoryScrapDate: 'compulsoryScrapLongTerm',
};
const longTermProp = longTermMap[prop];
if (longTermProp && this.certificationAuditForm[longTermProp] === 1) {
return '长期有效';
}
const value = this.certificationAuditForm[prop];
return value === undefined || value === null || value === '' ? '-' : value;
},
handleCertificationApprove() {
auditCertification(this.certificationAuditForm.id, 1).then(() => {
this.$message.success('认证已通过');
this.certificationAuditBox = false;
this.onLoad(this.page);
});
},
handleCertificationReject() {
if (!this.showRejectReason) { this.showRejectReason = true; return; }
if (!this.certificationRejectReason.trim()) { this.$message.warning('请输入认证驳回原因'); return; }
auditCertification(this.certificationAuditForm.id, 2, this.certificationRejectReason).then(() => {
this.$message.success('认证已驳回');
this.certificationAuditBox = false;
this.onLoad(this.page);
});
},
resetVehicle() {
this.vehicleForm = emptyForm();
this.vehicleCertificateUploads = {};
this.readonly = false;
this.submitLoading = false;
this.$refs.vehicleForm?.clearValidate();
},
setImage(prop, url) {
this.vehicleForm[prop] = url;
this.$refs.vehicleForm?.validateField(prop);
},
handleVehicleCertificateUploadSuccess(prop, url) {
this.setImage(prop, url);
this.vehicleCertificateUploads[prop] = url;
if (prop.startsWith('drivingLicense')) {
if (prop.endsWith('Back')) {
this.recognizeBaiduVehicleOcr(url, 'general', '', '行驶证', data => {
this.applyBaiduVehicleLicenseGeneralRecognition(data);
});
return;
}
const side = prop === 'drivingLicenseViceFront' ? 'back' : 'front';
this.recognizeBaiduVehicleOcr(url, 'vehicle_license', side, '行驶证', data => {
this.applyBaiduVehicleLicenseRecognition(data);
});
return;
}
if (prop === 'roadTransportCertImage') {
this.recognizeBaiduVehicleOcr(
url,
'road_transport_certificate',
'',
'道路运输证',
data => {
this.applyBaiduRoadTransportCertificateRecognition(data);
}
);
return;
}
this.recognizeVehicleCertificates();
},
recognizeBaiduVehicleOcr(url, type, side, documentName, applyRecognition) {
if (!url) {
this.$message.warning(`${documentName}图片上传成功,未获取到图片地址,无法自动识别`);
return;
}
const loading = ElLoading.service({
lock: true,
text: `${documentName}识别中`,
background: 'rgba(255, 255, 255, 0.7)',
});
recognizeBaiduOcr(url, type, side)
.then(res => {
applyRecognition(res.data.data?.result || {});
this.$message.success(`${documentName}识别完成`);
})
.catch(() => {
this.$message.warning(`${documentName}图片上传成功,自动识别失败,请手动填写车辆资质信息`);
})
.finally(() => {
loading.close();
});
},
recognizeVehicleCertificates() {
const objectKeys = [
this.vehicleCertificateUploads.registrationImage || this.vehicleForm.registrationImage,
].filter(Boolean);
if (!objectKeys.length) {
this.$message.warning('车辆资质图片上传成功,未获取到图片地址,无法自动识别');
return;
}
const loading = ElLoading.service({
lock: true,
text: '车辆资质识别中',
background: 'rgba(255, 255, 255, 0.7)',
});
recognitionTransportCertificates(objectKeys)
.then(res => {
this.applyVehicleCertificateRecognition(res.data.data || {});
this.$message.success('车辆资质识别完成');
})
.catch(() => {
this.$message.warning('车辆资质图片上传成功,自动识别失败,请手动填写车辆资质信息');
})
.finally(() => {
loading.close();
});
},
applyBaiduVehicleLicenseRecognition(data = {}) {
const plateNo = this.getBaiduOcrValue(data, ['号牌号码', '车牌号码']);
const vehicleType = this.getBaiduOcrValue(data, ['车辆类型']);
const drivingLicenseNo = this.getBaiduOcrValue(data, ['档案编号']);
const inspectionValidity = this.getBaiduOcrValue(data, ['检验有效期至', '检验有效期', '检验记录']);
const registrationDate = this.getBaiduOcrValue(data, ['注册日期']);
const registrationNo = this.getBaiduOcrValue(data, ['车辆识别代号', '车架号']);
const approvedLoadKg = this.getBaiduOcrValue(data, ['核定载质量']);
const outerDimensions = this.getBaiduOcrValue(data, ['外廓尺寸']);
if (plateNo) {
this.vehicleForm.plateNo = String(plateNo).replace(/\s/g, '').toUpperCase();
this.splitPlateNo();
this.syncPlateNo(false);
}
if (vehicleType) {
this.vehicleForm.vehicleType = this.normalizeVehicleType(vehicleType);
}
if (drivingLicenseNo) {
this.vehicleForm.drivingLicenseNo = String(drivingLicenseNo).replace(/\s/g, '');
}
this.applyDrivingLicenseValidity(inspectionValidity);
if (registrationDate) {
this.vehicleForm.registrationDate = this.normalizeDate(registrationDate);
}
if (registrationNo) {
this.vehicleForm.registrationNo = String(registrationNo).replace(/\s/g, '');
}
if (approvedLoadKg) {
this.vehicleForm.approvedLoadKg = this.normalizeVehicleNumber(approvedLoadKg);
}
this.applyOuterDimensions(outerDimensions);
this.$nextTick(() => {
[
'plateNo',
'vehicleType',
'drivingLicenseNo',
'drivingLicenseEndDate',
'registrationNo',
'approvedLoadKg',
'outerLength',
'outerWidth',
'outerHeight',
].forEach(prop => {
this.$refs.vehicleForm?.validateField(prop);
});
});
},
applyBaiduVehicleLicenseGeneralRecognition(data = {}) {
const words = this.getBaiduOcrWords(data);
const getValue = labels => this.getBaiduGeneralOcrValue(words, labels);
this.applyBaiduVehicleLicenseRecognition({
words_result: {
号牌号码: { words: getValue(['号牌号码', '车牌号码']) },
车辆类型: { words: getValue(['车辆类型']) },
档案编号: { words: getValue(['档案编号']) },
检验有效期至: { words: getValue(['检验有效期至', '检验有效期', '检验记录']) },
注册日期: { words: getValue(['注册日期']) },
车辆识别代号: { words: getValue(['车辆识别代号', '车架号']) },
核定载质量: { words: getValue(['核定载质量']) },
外廓尺寸: { words: getValue(['外廓尺寸']) },
},
});
},
normalizeVehicleNumber(value = '') {
const number = String(value || '').replace(/,/g, '').match(/-?\d+(?:\.\d+)?/);
return number ? number[0] : '';
},
applyOuterDimensions(value = '') {
const dimensions = String(value || '').match(/\d+(?:\.\d+)?/g) || [];
if (dimensions[0]) this.vehicleForm.outerLength = dimensions[0];
if (dimensions[1]) this.vehicleForm.outerWidth = dimensions[1];
if (dimensions[2]) this.vehicleForm.outerHeight = dimensions[2];
},
applyBaiduRoadTransportCertificateRecognition(data = {}) {
const certificateNo = this.getBaiduOcrValue(data, ['道路运输证号', '证号']);
const validity = this.getBaiduOcrValue(data, ['有效期至', '有效期限', '有效期']);
if (certificateNo) {
this.vehicleForm.roadTransportCertNo = String(certificateNo).replace(/\s/g, '');
}
this.applyRoadTransportCertificateValidity(validity);
this.$nextTick(() => {
['roadTransportCertNo', 'roadTransportCertEndDate'].forEach(prop => {
this.$refs.vehicleForm?.validateField(prop);
});
});
},
applyRoadTransportCertificateValidity(validity = '') {
const value = String(validity || '');
if (!value) return;
if (/(长期|永久)/.test(value)) {
this.vehicleForm.roadTransportCertLongTerm = 1;
this.vehicleForm.roadTransportCertEndDate = '';
return;
}
const dateList = value.match(/\d{4}[-/.年]\d{1,2}[-/.月]\d{1,2}日?/g) || [];
if (dateList.length) {
this.vehicleForm.roadTransportCertEndDate = this.normalizeDate(dateList[dateList.length - 1]);
this.vehicleForm.roadTransportCertLongTerm = 0;
}
},
applyDrivingLicenseValidity(validity = '') {
const value = String(validity || '');
if (!value) return;
if (/(长期|永久)/.test(value)) {
this.vehicleForm.drivingLicenseLongTerm = 1;
this.vehicleForm.drivingLicenseEndDate = '';
return;
}
const dateList = value.match(/\d{4}[-/.年]\d{1,2}[-/.月]\d{1,2}日?/g) || [];
if (dateList.length) {
this.vehicleForm.drivingLicenseEndDate = this.normalizeDate(dateList[dateList.length - 1]);
this.vehicleForm.drivingLicenseLongTerm = 0;
}
},
getBaiduOcrValue(data = {}, keys = []) {
const wordsResult = data.words_result || data.wordsResult || {};
const normalizedKeys = keys.map(key => String(key).toLowerCase());
if (Array.isArray(wordsResult)) {
const wordItem = wordsResult.find(item =>
normalizedKeys.includes(String(item.key || item.name || '').toLowerCase())
);
return wordItem?.words || wordItem?.word || wordItem?.value || '';
}
const wordEntry = Object.entries(wordsResult).find(([key]) =>
normalizedKeys.includes(String(key).toLowerCase())
);
if (!wordEntry) return '';
const value = wordEntry[1];
if (Array.isArray(value)) {
const item = value.find(entry => entry?.words || entry?.word || entry?.value);
return item?.words || item?.word || item?.value || '';
}
return typeof value === 'object' ? value.words || value.word || value.value || '' : value || '';
},
getBaiduOcrWords(data = {}) {
const wordsResult = data.words_result || data.wordsResult || {};
if (Array.isArray(wordsResult)) {
return wordsResult.map(item => item.words || item.word || item.value || '').filter(Boolean);
}
return Object.values(wordsResult)
.map(item => {
if (Array.isArray(item)) {
const entry = item.find(value => value?.words || value?.word || value?.value);
return entry?.words || entry?.word || entry?.value || '';
}
return typeof item === 'object' ? item.words || item.word || item.value || '' : item || '';
})
.filter(Boolean);
},
getBaiduGeneralOcrValue(words = [], labels = []) {
const normalizedWords = words.map(word => String(word || '').trim()).filter(Boolean);
for (const label of labels) {
const labelIndex = normalizedWords.findIndex(word => word.includes(label));
if (labelIndex < 0) continue;
const inlineValue = normalizedWords[labelIndex]
.replace(new RegExp(`^.*${label}\\s*[:]?`), '')
.trim();
if (inlineValue) return inlineValue;
if (normalizedWords[labelIndex + 1]) return normalizedWords[labelIndex + 1];
}
return '';
},
applyVehicleCertificateRecognition(data = {}) {
const plateNo = data.drivingPlateNo || data.vehicleLicensePlateNo || '';
const vehicleType = data.vehicleLicenseVehicleType || data.vehicleType || '';
const drivingLicenseNo =
data.vehicleLicenseFileNo || data.drivingLicenseNo || data.fileNo || '';
const drivingLicenseEndDate =
data.vehicleLicenseInspectionValidDate ||
data.drivingLicenseEndDate ||
data.vehicleLicenseValidDateEnd ||
'';
const registrationDate =
data.vehicleLicenseRegisterDate || data.registrationDate || data.dateRegister || '';
const registrationNo = data.registrationNo || data.vehicleLicenseNo || '';
if (plateNo) {
this.vehicleForm.plateNo = plateNo;
this.splitPlateNo();
this.syncPlateNo(false);
}
if (vehicleType) {
this.vehicleForm.vehicleType = this.normalizeVehicleType(vehicleType);
}
if (drivingLicenseNo) {
this.vehicleForm.drivingLicenseNo = drivingLicenseNo;
}
if (drivingLicenseEndDate) {
this.vehicleForm.drivingLicenseEndDate = this.normalizeDate(drivingLicenseEndDate);
this.vehicleForm.drivingLicenseLongTerm = 0;
}
if (registrationDate) {
this.vehicleForm.registrationDate = this.normalizeDate(registrationDate);
}
if (registrationNo) {
this.vehicleForm.registrationNo = registrationNo;
}
this.$nextTick(() => {
[
'plateNo',
'vehicleType',
'drivingLicenseNo',
'drivingLicenseEndDate',
'registrationNo',
].forEach(prop => {
this.$refs.vehicleForm?.validateField(prop);
});
});
},
normalizeDate(value = '') {
const dateValue = String(value || '').trim();
const match = dateValue.match(/^(\d{4})[-/.年](\d{1,2})[-/.月](\d{1,2})日?$/);
if (!match) return dateValue;
const [, year, month, day] = match;
return `${year}-${month.padStart(2, '0')}-${day.padStart(2, '0')}`;
},
normalizeVehicleType(value = '') {
const vehicleType = String(value || '').trim();
const option = this.vehicleTypeOptions.find(
item => item.value === vehicleType || item.label === vehicleType
);
return option ? option.value : vehicleType;
},
handlePlateNoInput(value) {
this.vehicleForm.plateNo = String(value || '')
.replace(/\s/g, '')
.toUpperCase();
},
validatePlateFormat() {
this.$refs.vehicleForm?.validateField('plateNo');
},
syncPlateNo(validate = true) {
const plateNo = String(this.vehicleForm.plateNo || '')
.replace(/\s/g, '')
.toUpperCase();
this.vehicleForm.plateNo = plateNo;
if (validate) {
this.$refs.vehicleForm?.validateField('plateNo');
}
},
splitPlateNo() {
// 车牌号已统一为单一 input,无需拆分
},
handleSubmit() {
this.syncPlateNo();
this.$refs.vehicleForm.validate(valid => {
if (!valid) {
return;
}
this.submitLoading = true;
submit(this.vehicleForm)
.then(() => {
this.$message.success('操作成功');
this.vehicleBox = false;
this.onLoad(this.page);
})
.finally(() => {
this.submitLoading = false;
});
});
},
handleStatus(row) {
const status = row.status === 1 ? 2 : 1;
const text = status === 1 ? '启用' : '停用';
this.$confirm(`是否${text}该车辆?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}).then(() => {
changeStatus(row.id, status).then(() => {
this.$message.success('操作成功');
this.onLoad(this.page);
});
});
},
handleDelete() {
if (this.selectionList.length === 0) {
this.$message.warning('请选择至少一条数据');
return;
}
this.$confirm('是否删除选中的车辆数据?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}).then(() => {
remove(this.ids).then(() => {
this.$message.success('操作成功');
this.onLoad(this.page);
});
});
},
handleExpireChange(expireStatus) {
this.query.expireStatus = expireStatus;
this.page.currentPage = 1;
this.onLoad(this.page);
},
refreshStat() {
getExpiryStat(this.buildQuery(false)).then(res => {
this.expiryStat = {
total: res.data.data?.total || 0,
within30: res.data.data?.within30 || 0,
expired: res.data.data?.expired || 0,
};
});
},
searchReset() {
this.searchForm = {
businessRelation: '',
};
this.query = {
expireStatus: this.query.expireStatus,
};
this.onLoad(this.page);
},
searchChange(params, done) {
// 所属组织级联返回的是 id 路径,转成组织名称传给后端
this.normalizeOrganizationSearch(params);
this.searchForm = params;
this.page.currentPage = 1;
this.onLoad(this.page, params);
done();
},
selectionChange(list) {
this.selectionList = list;
this.ids = list.map(item => item.id).join(',');
},
selectionClear() {
this.selectionList = [];
this.ids = '';
this.$refs.crud?.toggleSelection();
},
currentChange(currentPage) {
this.page.currentPage = currentPage;
},
sizeChange(pageSize) {
this.page.pageSize = pageSize;
},
refreshChange() {
this.onLoad(this.page, this.searchForm);
},
onLoad(page, params = {}) {
this.loading = true;
getList(page.currentPage, page.pageSize, this.buildQuery(true, params))
.then(res => {
const data = res.data.data;
this.page.total = data.total;
this.data = data.records;
this.selectionClear();
this.refreshStat();
})
.finally(() => {
this.loading = false;
});
},
buildQuery(includeExpire = true, params = {}) {
const query = {
...params,
...this.searchForm,
};
if (includeExpire && this.query.expireStatus) {
query.expireStatus = this.query.expireStatus;
}
return query;
},
handleExport() {
this.$confirm('是否导出车辆数据?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}).then(() => {
NProgress.start();
exportBlob(
'/blade-transport/transport-vehicle/export-transport-vehicle',
this.buildExportParams(),
{ feedback: true }
)
.then(res => {
downloadXls(res.data, `车辆管理${this.$dayjs().format('YYYY-MM-DD HH:mm:ss')}.xlsx`);
})
.finally(() => {
NProgress.done();
});
});
},
buildExportParams() {
return {
...this.buildQuery(true),
ids: this.ids,
[this.website.tokenHeader]: getToken(),
};
},
formatEndDate(date, longTerm) {
if (longTerm === 1) {
return '长期有效';
}
return date || '-';
},
getDateClass(row, dateProp, longTermProp) {
if (row[longTermProp] === 1 || !row[dateProp]) {
return '';
}
const today = this.$dayjs();
const endDate = this.$dayjs(row[dateProp]);
if (endDate.isBefore(today, 'day')) {
return 'date-danger';
}
if (endDate.diff(today, 'day') <= 30) {
return 'date-warning';
}
return '';
},
getCertificationStatus(row) {
if (row.certificationStatus === 0) return { label: '认证中', type: 'warning' };
if (row.certificationStatus === 1) return { label: '已认证', type: 'success' };
if (row.certificationStatus === 2) return { label: '已驳回', type: 'danger' };
const today = this.$dayjs().startOf('day');
const certificateDates = [
[row.drivingLicenseEndDate, row.drivingLicenseLongTerm],
[row.roadTransportCertEndDate, row.roadTransportCertLongTerm],
];
if (certificateDates.some(([date, longTerm]) => longTerm !== 1 && !date)) {
return { label: '未认证', type: 'info' };
}
if (
certificateDates.some(
([date, longTerm]) => longTerm !== 1 && this.$dayjs(date).isBefore(today, 'day')
)
) {
return { label: '已过期', type: 'danger' };
}
return { label: '已认证', type: 'success' };
},
},
};
</script>
<style lang="scss" scoped>
.transport-vehicle-page {
&__expiry-tags {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 8px;
margin-top: 8px;
margin-bottom: 16px;
:deep(.el-check-tag) {
margin: 0;
white-space: nowrap;
}
}
:deep(.el-table th .cell),
:deep(.el-table td .cell) {
white-space: nowrap;
}
}
// 车辆认证审核弹窗:与编辑弹窗一致,灰底 + section-card 白卡分组
.certification-audit {
&__value {
display: block;
color: #303133;
word-break: break-all;
}
// 外廓尺寸长/宽/高组合,排版复用编辑弹窗的 dimension-group
.dimension-group {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 8px;
align-items: center;
}
.dimension-group__item {
display: flex;
align-items: center;
gap: 8px;
min-width: 0;
}
.dimension-group__label {
flex-shrink: 0;
color: #303133;
}
&__reason {
margin-top: 12px;
}
}
.vehicle-form {
:deep(.el-date-editor.el-input),
:deep(.el-date-editor.el-input__wrapper),
:deep(.el-select),
:deep(.el-input),
:deep(.el-input-number),
:deep(.el-cascader) {
width: 100%;
}
.dimension-group {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 8px;
align-items: center;
}
.dimension-group__item {
display: flex;
align-items: center;
gap: 8px;
min-width: 0;
}
.dimension-group__label {
flex-shrink: 0;
color: #303133;
}
.date-with-flag {
display: flex;
align-items: center;
gap: 8px;
}
.date-with-flag :deep(.el-date-editor) {
flex: 1;
min-width: 0;
}
.date-with-flag :deep(.el-checkbox) {
flex-shrink: 0;
margin-right: 0;
}
.date-with-flag :deep(.el-checkbox__label) {
padding-left: 4px;
}
.date-range {
display: grid;
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
gap: 8px;
}
:deep(.el-form-item__label) {
white-space: nowrap;
}
}
// 大尺寸证件照:按身份证 1.586:1 比例缩放(约真实尺寸 75%,240×151),左对齐显示
:deep(.vehicle-uploader) {
display: block;
}
:deep(.vehicle-uploader .el-upload),
:deep(.vehicle-uploader.el-upload) {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 170px;
border: 1px dashed #c0c4cc;
background: #fafafa;
overflow: hidden;
}
// 大尺寸模式:缩成 1.586:1 比例,左对齐(不再 100% 占栏)
:deep(.vehicle-uploader--large) {
display: block;
width: 240px;
margin: 0;
}
:deep(.vehicle-uploader--large .el-upload),
:deep(.vehicle-uploader--large.el-upload) {
width: 100%;
height: 151px;
}
:deep(.vehicle-uploader__image) {
width: 100%;
height: 100%;
object-fit: contain;
display: block;
}
:deep(.vehicle-uploader__empty) {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
color: #606266;
}
.date-warning {
color: #d40000;
font-weight: 600;
}
.date-danger {
color: #d40000;
font-weight: 600;
}
.el-form-item {
margin-bottom: 14px !important;
}
</style>