Files
tms-erp-web/src/views/transportCapacity/ship.vue
b2894lxlx 433c611195 1、完善发货模板
2、完善运输计划
3、完善运单管理
2026-08-05 15:52:24 +08:00

1143 lines
36 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-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>