1143 lines
36 KiB
Vue
1143 lines
36 KiB
Vue
<template>
|
||
<basic-container class="transport-ship-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"
|
||
plain
|
||
v-if="hasPermission('transport_ship_add')"
|
||
@click="openShip()"
|
||
>
|
||
新增
|
||
</el-button>
|
||
<el-button
|
||
type="primary"
|
||
icon="el-icon-download"
|
||
plain
|
||
v-if="hasPermission('transport_ship_export')"
|
||
@click="handleExport"
|
||
>
|
||
导出
|
||
</el-button>
|
||
<el-button
|
||
type="danger"
|
||
icon="el-icon-delete"
|
||
plain
|
||
v-if="hasPermission('transport_ship_delete')"
|
||
@click="handleDelete"
|
||
>
|
||
批量删除
|
||
</el-button>
|
||
</template>
|
||
<template #menu-right>
|
||
<el-radio-group v-model="query.expireStatus" class="ship-stat" @change="handleExpireChange">
|
||
<el-radio-button label="">全部({{ expiryStat.total }})</el-radio-button>
|
||
<el-radio-button label="within30">30天内到期({{ expiryStat.within30 }})</el-radio-button>
|
||
<el-radio-button label="expired">已到期({{ expiryStat.expired }})</el-radio-button>
|
||
</el-radio-group>
|
||
</template>
|
||
<template #shipName="{ row }">
|
||
<el-button type="primary" link @click="openShip(row, true)">
|
||
{{ row.shipName }}
|
||
</el-button>
|
||
</template>
|
||
<template #status="{ row }">
|
||
<el-tag :type="row.status === 1 ? 'primary' : 'info'">
|
||
{{ row.status === 1 ? '启用' : '停用' }}
|
||
</el-tag>
|
||
</template>
|
||
<template #nationalityCertEndDate="{ row }">
|
||
<span :class="getDateClass(row, 'nationalityCertEndDate', 'nationalityCertLongTerm')">
|
||
{{ formatEndDate(row.nationalityCertEndDate, row.nationalityCertLongTerm) }}
|
||
</span>
|
||
</template>
|
||
<template #safeManningCertEndDate="{ row }">
|
||
<span :class="getDateClass(row, 'safeManningCertEndDate', 'safeManningCertLongTerm')">
|
||
{{ formatEndDate(row.safeManningCertEndDate, row.safeManningCertLongTerm) }}
|
||
</span>
|
||
</template>
|
||
<template #businessTransportCertEndDate="{ row }">
|
||
<span
|
||
:class="
|
||
getDateClass(row, 'businessTransportCertEndDate', 'businessTransportCertLongTerm')
|
||
"
|
||
>
|
||
{{ formatEndDate(row.businessTransportCertEndDate, row.businessTransportCertLongTerm) }}
|
||
</span>
|
||
</template>
|
||
<template #leaseEndDate="{ row }">
|
||
<span :class="getDateClass(row, 'leaseEndDate', 'leaseLongTerm')">
|
||
{{ formatEndDate(row.leaseEndDate, row.leaseLongTerm) }}
|
||
</span>
|
||
</template>
|
||
<template #menu="{ row }">
|
||
<el-link
|
||
type="primary"
|
||
v-if="hasPermission('transport_ship_edit')"
|
||
@click="openShip(row)"
|
||
>
|
||
修改
|
||
</el-link>
|
||
<el-link
|
||
type="primary"
|
||
v-if="hasPermission('transport_ship_view')"
|
||
@click="openShip(row, true)"
|
||
>
|
||
综合台账
|
||
</el-link>
|
||
<el-link
|
||
type="primary"
|
||
v-if="hasPermission('transport_ship_status')"
|
||
@click="handleStatus(row)"
|
||
>
|
||
{{ row.status === 1 ? '停用' : '启用' }}
|
||
</el-link>
|
||
</template>
|
||
</avue-crud>
|
||
|
||
<el-dialog
|
||
:title="dialogTitle"
|
||
append-to-body
|
||
v-model="shipBox"
|
||
width="92%"
|
||
top="4vh"
|
||
@closed="resetShip"
|
||
>
|
||
<el-form
|
||
ref="shipForm"
|
||
:model="shipForm"
|
||
:rules="formRules"
|
||
label-position="right"
|
||
label-width="auto"
|
||
:disabled="readonly"
|
||
class="ship-form"
|
||
>
|
||
<div class="section-title">基础船舶信息</div>
|
||
<el-row :gutter="18">
|
||
<el-col :span="8">
|
||
<el-form-item label="船舶所属组织" prop="organizationName">
|
||
<el-select v-model="shipForm.organizationName" filterable clearable placeholder="请选择">
|
||
<el-option
|
||
v-for="item in organizationOptions"
|
||
:key="item.value"
|
||
:label="item.label"
|
||
:value="item.value"
|
||
/>
|
||
</el-select>
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="8">
|
||
<el-form-item label="船名" prop="shipName">
|
||
<el-input v-model="shipForm.shipName" maxlength="50" show-word-limit />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="8">
|
||
<el-form-item label="安放龙骨日期/建造完工日期" prop="keelLayingDate">
|
||
<div class="date-range-inline">
|
||
<el-date-picker
|
||
v-model="shipForm.keelLayingDate"
|
||
type="date"
|
||
value-format="YYYY-MM-DD"
|
||
@change="validateField('keelLayingDate')"
|
||
/>
|
||
<span>至</span>
|
||
<el-date-picker
|
||
v-model="shipForm.buildCompletionDate"
|
||
type="date"
|
||
value-format="YYYY-MM-DD"
|
||
@change="validateField('keelLayingDate')"
|
||
/>
|
||
</div>
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
<el-row :gutter="18">
|
||
<el-col :span="8">
|
||
<el-form-item label="总长" prop="totalLength">
|
||
<el-input-number
|
||
v-model="shipForm.totalLength"
|
||
:min="0"
|
||
:precision="2"
|
||
:controls="false"
|
||
/>
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="8">
|
||
<el-form-item label="船宽" prop="shipWidth">
|
||
<el-input-number
|
||
v-model="shipForm.shipWidth"
|
||
:min="0"
|
||
:precision="2"
|
||
:controls="false"
|
||
/>
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="8">
|
||
<el-form-item label="型深" prop="moldedDepth">
|
||
<el-input-number
|
||
v-model="shipForm.moldedDepth"
|
||
:min="0"
|
||
:precision="2"
|
||
:controls="false"
|
||
/>
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
<el-row :gutter="18">
|
||
<el-col :span="8">
|
||
<el-form-item label="最大船高" prop="maxShipHeight">
|
||
<el-input-number
|
||
v-model="shipForm.maxShipHeight"
|
||
:min="0"
|
||
:precision="2"
|
||
:controls="false"
|
||
/>
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="8">
|
||
<el-form-item label="空载吃水" prop="lightDraft">
|
||
<el-input-number
|
||
v-model="shipForm.lightDraft"
|
||
:min="0"
|
||
:precision="2"
|
||
:controls="false"
|
||
/>
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="8">
|
||
<el-form-item label="满载吃水" prop="fullLoadDraft">
|
||
<el-input-number
|
||
v-model="shipForm.fullLoadDraft"
|
||
:min="0"
|
||
:precision="2"
|
||
:controls="false"
|
||
/>
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
<el-row :gutter="18">
|
||
<el-col :span="8">
|
||
<el-form-item label="航区" prop="navigationArea">
|
||
<div class="navigation-area-field">
|
||
<el-select v-model="shipForm.navigationArea" placeholder="请选择">
|
||
<el-option label="A级" value="A级" />
|
||
<el-option label="B级" value="B级" />
|
||
</el-select>
|
||
<el-tooltip effect="light" placement="top" popper-class="ship-navigation-tooltip">
|
||
<template #content>
|
||
<table class="navigation-cargo-table">
|
||
<tbody>
|
||
<tr>
|
||
<th rowspan="2">参考<br />载货量</th>
|
||
<th>航区</th>
|
||
<th>A级</th>
|
||
<th>B级</th>
|
||
<th>FA</th>
|
||
<th>FB</th>
|
||
</tr>
|
||
<tr>
|
||
<th>载货量(t)</th>
|
||
<td>864.00</td>
|
||
<td>864.00</td>
|
||
<td>965.00</td>
|
||
<td>1015.00</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</template>
|
||
<el-icon class="navigation-info-icon"><InfoFilled /></el-icon>
|
||
</el-tooltip>
|
||
</div>
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
|
||
<div class="section-title section-title--wide">船舶证书信息</div>
|
||
<div class="cert-block">
|
||
<div class="cert-block__title">船舶所有权证书</div>
|
||
<el-row :gutter="18">
|
||
<el-col :span="8">
|
||
<el-form-item label="登记号码" prop="ownershipRegistrationNo">
|
||
<el-input v-model="shipForm.ownershipRegistrationNo" maxlength="50" />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="8">
|
||
<el-form-item label="初次登记号码" prop="initialRegistrationNo">
|
||
<el-input v-model="shipForm.initialRegistrationNo" maxlength="50" />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="8">
|
||
<el-form-item label="船舶所有人" prop="shipOwner">
|
||
<el-input v-model="shipForm.shipOwner" maxlength="50" />
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
<el-row :gutter="18">
|
||
<el-col :span="8">
|
||
<el-form-item label="取得所有权日期" prop="ownershipAcquisitionDate">
|
||
<el-date-picker
|
||
v-model="shipForm.ownershipAcquisitionDate"
|
||
type="date"
|
||
value-format="YYYY-MM-DD"
|
||
/>
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="8">
|
||
<el-form-item label="船舶识别号" prop="shipIdentifierNo">
|
||
<el-input
|
||
v-model="shipForm.shipIdentifierNo"
|
||
maxlength="50"
|
||
@input="
|
||
shipForm.shipIdentifierNo = String(shipForm.shipIdentifierNo || '').toUpperCase()
|
||
"
|
||
/>
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
</div>
|
||
|
||
<div class="cert-block">
|
||
<div class="cert-block__title">内河船舶安全与环保证书</div>
|
||
<el-row :gutter="18">
|
||
<el-col :span="8">
|
||
<el-form-item label="总吨" prop="grossTonnage">
|
||
<el-input-number
|
||
v-model="shipForm.grossTonnage"
|
||
:min="0"
|
||
:precision="2"
|
||
:controls="false"
|
||
/>
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="8">
|
||
<el-form-item label="净吨" prop="netTonnage">
|
||
<el-input-number
|
||
v-model="shipForm.netTonnage"
|
||
:min="0"
|
||
:precision="2"
|
||
:controls="false"
|
||
/>
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="8">
|
||
<el-form-item label="船检登记号" prop="shipInspectionNo">
|
||
<el-input v-model="shipForm.shipInspectionNo" maxlength="50" />
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
<el-row :gutter="18">
|
||
<el-col :span="8">
|
||
<el-form-item label="船舶类型" prop="shipType">
|
||
<el-input v-model="shipForm.shipType" maxlength="50" />
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
</div>
|
||
|
||
<div class="cert-block">
|
||
<div class="cert-block__title">船舶国籍证书</div>
|
||
<el-row :gutter="18">
|
||
<el-col :span="16">
|
||
<el-form-item label="证书有效期自" prop="nationalityCertStartDate">
|
||
<div class="date-range-inline">
|
||
<el-date-picker
|
||
v-model="shipForm.nationalityCertStartDate"
|
||
type="date"
|
||
value-format="YYYY-MM-DD"
|
||
/>
|
||
<span>至</span>
|
||
<el-date-picker
|
||
v-model="shipForm.nationalityCertEndDate"
|
||
type="date"
|
||
value-format="YYYY-MM-DD"
|
||
@change="validateField('nationalityCertStartDate')"
|
||
/>
|
||
</div>
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
</div>
|
||
|
||
<div class="cert-block">
|
||
<div class="cert-block__title">内河船舶最低安全配员证书</div>
|
||
<el-row :gutter="18">
|
||
<el-col :span="16">
|
||
<el-form-item label="证书有效期自" prop="safeManningCertStartDate">
|
||
<div class="date-range-inline">
|
||
<el-date-picker
|
||
v-model="shipForm.safeManningCertStartDate"
|
||
type="date"
|
||
value-format="YYYY-MM-DD"
|
||
/>
|
||
<span>至</span>
|
||
<el-date-picker
|
||
v-model="shipForm.safeManningCertEndDate"
|
||
type="date"
|
||
value-format="YYYY-MM-DD"
|
||
@change="validateField('safeManningCertStartDate')"
|
||
/>
|
||
</div>
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
</div>
|
||
|
||
<div class="cert-block">
|
||
<div class="cert-block__title">光船租赁登记证明书</div>
|
||
<el-row :gutter="18">
|
||
<el-col :span="8">
|
||
<el-form-item label="起租日期" prop="leaseStartDate">
|
||
<el-date-picker
|
||
v-model="shipForm.leaseStartDate"
|
||
type="date"
|
||
value-format="YYYY-MM-DD"
|
||
/>
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="8">
|
||
<el-form-item label="终止日期" prop="leaseEndDate">
|
||
<el-date-picker
|
||
v-model="shipForm.leaseEndDate"
|
||
type="date"
|
||
value-format="YYYY-MM-DD"
|
||
@change="validateField('leaseStartDate')"
|
||
/>
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="8">
|
||
<el-form-item label="船舶承租人" prop="shipLessee">
|
||
<el-input v-model="shipForm.shipLessee" maxlength="50" />
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
</div>
|
||
|
||
<div class="cert-block">
|
||
<div class="cert-block__title">船舶营业运输证书</div>
|
||
<el-row :gutter="18">
|
||
<el-col :span="8">
|
||
<el-form-item label="证书编号" prop="businessTransportCertNo">
|
||
<el-input v-model="shipForm.businessTransportCertNo" maxlength="50" />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="8">
|
||
<el-form-item label="发证日期" prop="businessTransportCertIssueDate">
|
||
<el-date-picker
|
||
v-model="shipForm.businessTransportCertIssueDate"
|
||
type="date"
|
||
value-format="YYYY-MM-DD"
|
||
/>
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="8">
|
||
<el-form-item label="有效期至" prop="businessTransportCertEndDate">
|
||
<el-date-picker
|
||
v-model="shipForm.businessTransportCertEndDate"
|
||
type="date"
|
||
value-format="YYYY-MM-DD"
|
||
/>
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
<el-row :gutter="18">
|
||
<el-col :span="8">
|
||
<el-form-item label="船舶经营人" prop="shipOperator">
|
||
<el-input v-model="shipForm.shipOperator" maxlength="50" />
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
</div>
|
||
|
||
<el-row class="cert-upload-grid" :gutter="18">
|
||
<el-col :span="8">
|
||
<image-upload-field
|
||
label="船舶所有权证书"
|
||
prop="ownershipCertImage"
|
||
:value="shipForm.ownershipCertImage"
|
||
:readonly="readonly"
|
||
:headers="uploadHeaders"
|
||
large
|
||
class-prefix="ship"
|
||
@success="url => setImage('ownershipCertImage', url)"
|
||
/>
|
||
</el-col>
|
||
<el-col :span="8">
|
||
<image-upload-field
|
||
label="内河船舶安全与环保证书"
|
||
prop="safetyCertImage"
|
||
:value="shipForm.safetyCertImage"
|
||
:readonly="readonly"
|
||
:headers="uploadHeaders"
|
||
large
|
||
class-prefix="ship"
|
||
@success="url => setImage('safetyCertImage', url)"
|
||
/>
|
||
</el-col>
|
||
<el-col :span="8">
|
||
<image-upload-field
|
||
label="船舶国籍证书"
|
||
prop="nationalityCertImage"
|
||
:value="shipForm.nationalityCertImage"
|
||
:readonly="readonly"
|
||
:headers="uploadHeaders"
|
||
large
|
||
class-prefix="ship"
|
||
@success="url => setImage('nationalityCertImage', url)"
|
||
/>
|
||
</el-col>
|
||
<el-col :span="8">
|
||
<image-upload-field
|
||
label="内河船舶最低安全配员证书"
|
||
prop="safeManningCertImage"
|
||
:value="shipForm.safeManningCertImage"
|
||
:readonly="readonly"
|
||
:headers="uploadHeaders"
|
||
large
|
||
class-prefix="ship"
|
||
@success="url => setImage('safeManningCertImage', url)"
|
||
/>
|
||
</el-col>
|
||
<el-col :span="8">
|
||
<div class="cert-upload-spacer"></div>
|
||
</el-col>
|
||
<el-col :span="8">
|
||
<image-upload-field
|
||
label="光船租赁登记证书"
|
||
prop="leaseContractImage"
|
||
:value="shipForm.leaseContractImage"
|
||
:readonly="readonly"
|
||
:headers="uploadHeaders"
|
||
large
|
||
class-prefix="ship"
|
||
@success="url => setImage('leaseContractImage', url)"
|
||
/>
|
||
</el-col>
|
||
<el-col :span="8">
|
||
<image-upload-field
|
||
label="船舶营业运输证"
|
||
prop="businessTransportCertImage"
|
||
:value="shipForm.businessTransportCertImage"
|
||
:readonly="readonly"
|
||
:headers="uploadHeaders"
|
||
large
|
||
class-prefix="ship"
|
||
@success="url => setImage('businessTransportCertImage', url)"
|
||
/>
|
||
</el-col>
|
||
</el-row>
|
||
</el-form>
|
||
<template #footer>
|
||
<el-button @click="shipBox = 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 { InfoFilled } from '@element-plus/icons-vue';
|
||
import { option } from '@/option/transportCapacity/transport-ship';
|
||
import { getDeptTree } from '@/api/system/dept';
|
||
import {
|
||
getList,
|
||
getDetail,
|
||
submit,
|
||
remove,
|
||
changeStatus,
|
||
getExpiryStat,
|
||
} from '@/api/transportCapacity/transport-ship';
|
||
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';
|
||
|
||
const emptyForm = () => ({
|
||
id: '',
|
||
organizationName: '',
|
||
shipName: '',
|
||
shipIdentifierNo: '',
|
||
keelLayingDate: '',
|
||
buildCompletionDate: '',
|
||
totalLength: undefined,
|
||
shipWidth: undefined,
|
||
moldedDepth: undefined,
|
||
maxShipHeight: undefined,
|
||
lightDraft: undefined,
|
||
fullLoadDraft: undefined,
|
||
navigationArea: 'A级',
|
||
ownershipRegistrationNo: '',
|
||
initialRegistrationNo: '',
|
||
shipOwner: '',
|
||
ownershipAcquisitionDate: '',
|
||
shipInspectionNo: '',
|
||
shipType: '',
|
||
grossTonnage: undefined,
|
||
netTonnage: undefined,
|
||
nationalityCertStartDate: '',
|
||
nationalityCertEndDate: '',
|
||
nationalityCertLongTerm: 0,
|
||
nationalityCertImage: '',
|
||
ownershipCertImage: '',
|
||
safetyCertImage: '',
|
||
safeManningCertStartDate: '',
|
||
safeManningCertEndDate: '',
|
||
safeManningCertLongTerm: 0,
|
||
safeManningCertImage: '',
|
||
leaseStartDate: '',
|
||
shipLessee: '',
|
||
businessTransportCertEndDate: '',
|
||
businessTransportCertLongTerm: 0,
|
||
businessTransportCertImage: '',
|
||
businessTransportCertNo: '',
|
||
businessTransportCertIssueDate: '',
|
||
shipOperator: '',
|
||
leaseEndDate: '',
|
||
leaseLongTerm: 0,
|
||
leaseContractImage: '',
|
||
remark: '',
|
||
status: 1,
|
||
});
|
||
|
||
export default {
|
||
components: { ImageUploadField, InfoFilled },
|
||
data() {
|
||
return {
|
||
option,
|
||
form: {},
|
||
query: { expireStatus: '' },
|
||
searchForm: {},
|
||
loading: true,
|
||
submitLoading: false,
|
||
data: [],
|
||
ids: '',
|
||
selectionList: [],
|
||
page: { pageSize: 10, currentPage: 1, total: 0 },
|
||
expiryStat: { total: 0, within30: 0, expired: 0 },
|
||
shipBox: false,
|
||
readonly: false,
|
||
shipForm: emptyForm(),
|
||
uploadHeaders: getUploadHeaders(),
|
||
organizationOptions: [],
|
||
formRules: {
|
||
organizationName: [{ required: true, message: '请选择船舶所属组织', trigger: 'change' }],
|
||
shipName: [{ required: true, message: '请输入船名', trigger: 'blur' }],
|
||
keelLayingDate: [{ validator: this.validateKeelCompletionDate, trigger: 'change' }],
|
||
shipIdentifierNo: [{ required: true, message: '请输入船舶识别号', trigger: 'blur' }],
|
||
totalLength: [{ validator: this.validateNonNegativeField, trigger: 'change' }],
|
||
shipWidth: [{ validator: this.validateNonNegativeField, trigger: 'change' }],
|
||
moldedDepth: [{ validator: this.validateNonNegativeField, trigger: 'change' }],
|
||
maxShipHeight: [{ validator: this.validateNonNegativeField, trigger: 'change' }],
|
||
lightDraft: [{ validator: this.validateNonNegativeField, trigger: 'change' }],
|
||
fullLoadDraft: [{ validator: this.validateNonNegativeField, trigger: 'change' }],
|
||
navigationArea: [{ required: true, message: '请选择航区', trigger: 'change' }],
|
||
ownershipRegistrationNo: [{ required: true, message: '请输入登记号码', trigger: 'blur' }],
|
||
initialRegistrationNo: [{ required: true, message: '请输入初次登记号码', trigger: 'blur' }],
|
||
shipOwner: [{ max: 50, message: '船舶所有人不能超过50字', trigger: 'blur' }],
|
||
grossTonnage: [{ required: true, message: '请输入总吨', trigger: 'change' }],
|
||
netTonnage: [{ required: true, message: '请输入净吨', trigger: 'change' }],
|
||
shipInspectionNo: [{ required: true, message: '请输入船检登记号', trigger: 'blur' }],
|
||
shipType: [{ required: true, message: '请输入船舶类型', trigger: 'blur' }],
|
||
nationalityCertStartDate: [{ validator: this.validateNationalityCertDate, trigger: 'change' }],
|
||
safeManningCertStartDate: [{ validator: this.validateSafeManningCertDate, trigger: 'change' }],
|
||
leaseStartDate: [{ validator: this.validateLeaseDate, trigger: 'change' }],
|
||
businessTransportCertNo: [{ required: true, message: '请输入证书编号', trigger: 'blur' }],
|
||
businessTransportCertIssueDate: [
|
||
{ required: true, message: '请选择发证日期', trigger: 'change' },
|
||
{ validator: this.validateBusinessTransportCertDate, trigger: 'change' },
|
||
],
|
||
businessTransportCertEndDate: [
|
||
{ required: true, message: '请选择有效期至', trigger: 'change' },
|
||
{ validator: this.validateBusinessTransportCertDate, trigger: 'change' },
|
||
],
|
||
shipLessee: [{ max: 50, message: '船舶承租人不能超过50字', trigger: 'blur' }],
|
||
shipOperator: [{ max: 50, message: '船舶经营人不能超过50字', trigger: 'blur' }],
|
||
remark: [{ max: 200, message: '备注最多200字', trigger: 'blur' }],
|
||
},
|
||
};
|
||
},
|
||
computed: {
|
||
...mapGetters(['permission', 'userInfo']),
|
||
isAdmin() {
|
||
const authority = this.userInfo.authority || '';
|
||
return authority.includes('admin');
|
||
},
|
||
permissionList() {
|
||
return {
|
||
addBtn: this.hasPermission('transport_ship_add'),
|
||
viewBtn: this.hasPermission('transport_ship_view'),
|
||
delBtn: this.hasPermission('transport_ship_delete'),
|
||
editBtn: this.hasPermission('transport_ship_edit'),
|
||
};
|
||
},
|
||
dialogTitle() {
|
||
if (this.readonly) return '船舶综合台账';
|
||
return this.shipForm.id ? '修改船舶' : '新增船舶';
|
||
},
|
||
},
|
||
created() {
|
||
this.initDeptTree();
|
||
},
|
||
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;
|
||
});
|
||
},
|
||
flattenDeptOptions(tree = [], level = 0) {
|
||
const result = [];
|
||
tree.forEach(item => {
|
||
const label = item.title || item.deptName || item.name;
|
||
result.push({
|
||
label: `${' '.repeat(level)}${label}`,
|
||
value: label,
|
||
});
|
||
if (item.children && item.children.length) {
|
||
result.push(...this.flattenDeptOptions(item.children, level + 1));
|
||
}
|
||
});
|
||
return result;
|
||
},
|
||
validateField(prop) {
|
||
this.$refs.shipForm?.validateField(prop);
|
||
},
|
||
validateNonNegativeField(rule, value, callback) {
|
||
if (value !== undefined && value !== null && value < 0) {
|
||
callback(new Error('数值不能小于0'));
|
||
return;
|
||
}
|
||
callback();
|
||
},
|
||
validateKeelCompletionDate(rule, value, callback) {
|
||
if (
|
||
this.shipForm.keelLayingDate &&
|
||
this.shipForm.buildCompletionDate &&
|
||
this.$dayjs(this.shipForm.buildCompletionDate).isBefore(this.shipForm.keelLayingDate, 'day')
|
||
) {
|
||
callback(new Error('建造完工日期不能早于安放龙骨日期'));
|
||
return;
|
||
}
|
||
callback();
|
||
},
|
||
validateNationalityCertDate(rule, value, callback) {
|
||
this.validateDateRangeByProp(
|
||
'nationalityCertStartDate',
|
||
'nationalityCertEndDate',
|
||
'nationalityCertLongTerm',
|
||
'请选择船舶国籍证书有效期',
|
||
callback
|
||
);
|
||
},
|
||
validateSafeManningCertDate(rule, value, callback) {
|
||
this.validateDateRangeByProp(
|
||
'safeManningCertStartDate',
|
||
'safeManningCertEndDate',
|
||
'safeManningCertLongTerm',
|
||
'请选择最低安全配员证书有效期',
|
||
callback
|
||
);
|
||
},
|
||
validateLeaseDate(rule, value, callback) {
|
||
this.validateOptionalDateRange(
|
||
'leaseStartDate',
|
||
'leaseEndDate',
|
||
'终止日期不能早于起租日期',
|
||
callback
|
||
);
|
||
},
|
||
validateBusinessTransportCertDate(rule, value, callback) {
|
||
this.validateOptionalDateRange(
|
||
'businessTransportCertIssueDate',
|
||
'businessTransportCertEndDate',
|
||
'有效期至不能早于发证日期',
|
||
callback
|
||
);
|
||
},
|
||
validateDateRangeByProp(startProp, endProp, longTermProp, emptyMessage, callback) {
|
||
if (this.shipForm[longTermProp] === 1 && !this.shipForm[startProp] && !this.shipForm[endProp]) {
|
||
callback();
|
||
return;
|
||
}
|
||
if (!this.shipForm[startProp] || !this.shipForm[endProp]) {
|
||
callback(new Error(emptyMessage));
|
||
return;
|
||
}
|
||
this.validateOptionalDateRange(startProp, endProp, '结束日期不能早于开始日期', callback);
|
||
},
|
||
validateOptionalDateRange(startProp, endProp, message, callback) {
|
||
if (
|
||
this.shipForm[startProp] &&
|
||
this.shipForm[endProp] &&
|
||
this.$dayjs(this.shipForm[endProp]).isBefore(this.shipForm[startProp], 'day')
|
||
) {
|
||
callback(new Error(message));
|
||
return;
|
||
}
|
||
callback();
|
||
},
|
||
openShip(row, readonly = false) {
|
||
this.readonly = readonly;
|
||
if (!row?.id) {
|
||
this.shipForm = emptyForm();
|
||
this.shipBox = true;
|
||
return;
|
||
}
|
||
getDetail(row.id).then(res => {
|
||
this.shipForm = { ...emptyForm(), ...(res.data.data || {}) };
|
||
this.shipBox = true;
|
||
});
|
||
},
|
||
resetShip() {
|
||
this.shipForm = emptyForm();
|
||
this.readonly = false;
|
||
this.submitLoading = false;
|
||
this.$refs.shipForm?.clearValidate();
|
||
},
|
||
setImage(prop, url) {
|
||
this.shipForm[prop] = url;
|
||
this.$refs.shipForm?.validateField(prop);
|
||
},
|
||
handleSubmit() {
|
||
this.$refs.shipForm.validate(valid => {
|
||
if (!valid) return;
|
||
this.submitLoading = true;
|
||
submit(this.shipForm)
|
||
.then(() => {
|
||
this.$message.success('操作成功');
|
||
this.shipBox = 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() {
|
||
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 = {};
|
||
this.query = { expireStatus: this.query.expireStatus };
|
||
this.onLoad(this.page);
|
||
},
|
||
searchChange(params, done) {
|
||
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-ship/export-transport-ship',
|
||
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 '';
|
||
},
|
||
},
|
||
};
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.transport-ship-page {
|
||
.ship-stat {
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
:deep(.el-table th .cell),
|
||
:deep(.el-table td .cell) {
|
||
white-space: nowrap;
|
||
}
|
||
}
|
||
|
||
.ship-form {
|
||
.section-title {
|
||
margin: 18px 0 14px;
|
||
padding-left: 10px;
|
||
border-left: 3px solid #e74b5f;
|
||
color: #303133;
|
||
font-weight: 600;
|
||
line-height: 18px;
|
||
}
|
||
|
||
.section-title:first-child {
|
||
margin-top: 0;
|
||
}
|
||
|
||
.section-title--wide {
|
||
margin-top: 24px;
|
||
}
|
||
|
||
:deep(.el-date-editor.el-input),
|
||
:deep(.el-date-editor.el-input__wrapper),
|
||
:deep(.el-select),
|
||
:deep(.el-input),
|
||
:deep(.el-input-number) {
|
||
width: 100%;
|
||
}
|
||
}
|
||
|
||
.date-range-inline {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
width: 100%;
|
||
}
|
||
|
||
.date-range-inline :deep(.el-date-editor) {
|
||
flex: 1;
|
||
min-width: 0;
|
||
}
|
||
|
||
.navigation-area-field {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
width: 100%;
|
||
}
|
||
|
||
.navigation-area-field :deep(.el-select) {
|
||
flex: 1;
|
||
min-width: 0;
|
||
}
|
||
|
||
.navigation-info-icon {
|
||
color: #909399;
|
||
font-size: 18px;
|
||
cursor: help;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.cert-block {
|
||
padding: 16px 0 8px;
|
||
border-top: 1px solid #e5e6eb;
|
||
}
|
||
|
||
.cert-block__title {
|
||
margin-bottom: 12px;
|
||
color: #303133;
|
||
font-weight: 600;
|
||
line-height: 20px;
|
||
}
|
||
|
||
.cert-upload-grid {
|
||
padding-top: 8px;
|
||
}
|
||
|
||
.cert-upload-spacer {
|
||
height: 1px;
|
||
}
|
||
|
||
.ship-upload-item {
|
||
margin-bottom: 14px;
|
||
}
|
||
|
||
.ocr-tip {
|
||
min-height: 114px;
|
||
padding: 18px 20px;
|
||
background: #ffd84d;
|
||
color: #333;
|
||
font-size: 16px;
|
||
line-height: 1.45;
|
||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);
|
||
}
|
||
|
||
:deep(.ship-uploader) {
|
||
display: block;
|
||
width: 100%;
|
||
}
|
||
|
||
:deep(.ship-uploader .el-upload),
|
||
:deep(.ship-uploader.el-upload) {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
width: 100%;
|
||
height: 170px;
|
||
border: 1px dashed #c0c4cc;
|
||
background: #fafafa;
|
||
overflow: hidden;
|
||
}
|
||
|
||
:deep(.ship-uploader--large .el-upload),
|
||
:deep(.ship-uploader--large.el-upload) {
|
||
height: 260px;
|
||
}
|
||
|
||
:deep(.ship-uploader__image) {
|
||
width: 100%;
|
||
height: 100%;
|
||
object-fit: contain;
|
||
display: block;
|
||
}
|
||
|
||
:deep(.ship-uploader__empty) {
|
||
width: 100%;
|
||
height: 100%;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
color: #606266;
|
||
}
|
||
|
||
:deep(.ship-navigation-tooltip) {
|
||
padding: 8px 10px;
|
||
}
|
||
|
||
.navigation-cargo-table {
|
||
border-collapse: collapse;
|
||
font-size: 12px;
|
||
}
|
||
|
||
.navigation-cargo-table th,
|
||
.navigation-cargo-table td {
|
||
border: 1px solid #909399;
|
||
padding: 6px 10px;
|
||
text-align: center;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.navigation-cargo-table th {
|
||
font-weight: 600;
|
||
}
|
||
|
||
.date-warning {
|
||
color: #f56c6c;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.date-danger {
|
||
color: #d40000;
|
||
font-weight: 600;
|
||
}
|
||
</style>
|