6de87ed4e2
- 调整合同管理备注输入框行数和最大长度 - 细化合同变更页面表单项间距和对齐方式 - 统一弹窗表单项的底部间距样式,改为注释禁用 - 调整 port-terminal 和 region 组件的表单项底部间距 - 修改 vite 配置中 API 请求代理地址 - 优化凭证执行详情搜索表单列宽布局 - 在运单管理页面更新型号字段为非必填,调整变更记录内容格式化显示 - 引入并使用 formatRouteChangeContent 工具函数 - 统一明细按钮显示逻辑,草稿状态也显示详情按钮 - 新增路由变更相关工具函数与样式支持
1728 lines
55 KiB
Vue
1728 lines
55 KiB
Vue
<template>
|
|
<basic-container class="port-terminal-page">
|
|
<avue-crud
|
|
:option="option"
|
|
:table-loading="loading"
|
|
:data="data"
|
|
v-model:page="page"
|
|
v-model="form"
|
|
ref="crud"
|
|
:permission="permissionList"
|
|
:before-open="beforeOpen"
|
|
@row-save="rowSave"
|
|
@row-update="rowUpdate"
|
|
@row-del="rowDel"
|
|
@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-upload"
|
|
plain
|
|
v-if="hasPermission('port_terminal_import')"
|
|
@click="handleImport"
|
|
>批量导入
|
|
</el-button>
|
|
<el-button
|
|
type="primary"
|
|
icon="el-icon-download"
|
|
plain
|
|
v-if="hasPermission('port_terminal_template')"
|
|
@click="handleTemplate"
|
|
>下载模板
|
|
</el-button>
|
|
<el-button
|
|
type="primary"
|
|
icon="el-icon-download"
|
|
plain
|
|
v-if="hasPermission('port_terminal_export')"
|
|
@click="handleExport"
|
|
>批量导出
|
|
</el-button>
|
|
<el-button
|
|
type="danger"
|
|
icon="el-icon-delete"
|
|
plain
|
|
v-if="hasPermission('port_terminal_delete')"
|
|
@click="handleDelete"
|
|
>批量删除
|
|
</el-button>
|
|
</template>
|
|
<template #parentName="{ row }">
|
|
<span>{{ row.category === '港口' ? '-' : row.parentName }}</span>
|
|
</template>
|
|
<template #parentCode="{ row }">
|
|
<span>{{ row.category === '港口' ? '-' : row.parentCode }}</span>
|
|
</template>
|
|
<template #name-header>
|
|
<span>港口/码头</span>
|
|
</template>
|
|
<template #status="{ row }">
|
|
<el-tag :type="row.status === 1 ? 'primary' : 'info'" class="status-text">
|
|
{{ row.status === 1 ? '启用' : '停用' }}
|
|
</el-tag>
|
|
</template>
|
|
<template #country-form>
|
|
<el-select
|
|
v-model="form.countryCode"
|
|
clearable
|
|
filterable
|
|
:disabled="isParentRegionLocked"
|
|
:placeholder="isParentRegionLocked ? '自动带出' : '请选择国家'"
|
|
autocomplete="off"
|
|
style="width: 100%"
|
|
@change="handleCountryChange"
|
|
>
|
|
<el-option
|
|
v-for="item in countryOptions"
|
|
:key="item.id"
|
|
:label="item.title"
|
|
:value="item.id"
|
|
/>
|
|
</el-select>
|
|
</template>
|
|
<template #category-form>
|
|
<el-radio-group v-model="form.category" @change="handleCategoryChange">
|
|
<el-radio label="港口">港口</el-radio>
|
|
<el-radio label="码头">码头</el-radio>
|
|
</el-radio-group>
|
|
</template>
|
|
<template #provinceCode-form>
|
|
<el-select
|
|
v-model="form.provinceCode"
|
|
clearable
|
|
filterable
|
|
:disabled="isProvinceDisabled"
|
|
placeholder="请选择"
|
|
style="width: 100%"
|
|
@change="handleProvinceChange"
|
|
>
|
|
<el-option
|
|
v-for="item in provinceOptions"
|
|
:key="item.id"
|
|
:label="item.title"
|
|
:value="item.id"
|
|
/>
|
|
</el-select>
|
|
</template>
|
|
<template #code-form>
|
|
<el-input
|
|
v-if="form.category !== '码头'"
|
|
v-model="form.code"
|
|
clearable
|
|
maxlength="5"
|
|
placeholder="请输入5位大写字母"
|
|
@input="handleCodeChange"
|
|
@blur="handleCodeChange(form.code)"
|
|
/>
|
|
<div v-else>
|
|
<el-input
|
|
v-model="form.terminalCode"
|
|
clearable
|
|
maxlength="24"
|
|
placeholder="请输入码头标识"
|
|
@input="handleTerminalCodeChange"
|
|
>
|
|
<template v-if="form.parentCode" #prepend>{{ form.parentCode }}</template>
|
|
</el-input>
|
|
</div>
|
|
</template>
|
|
<template #menu="{ row, index }">
|
|
<el-link
|
|
type="primary"
|
|
v-if="hasPermission('port_terminal_edit')"
|
|
@click="$refs.crud.rowEdit(row, index)"
|
|
>
|
|
编辑
|
|
</el-link>
|
|
<el-link
|
|
type="primary"
|
|
v-if="hasPermission('port_terminal_status')"
|
|
@click="handleStatus(row)"
|
|
>
|
|
{{ row.status === 1 ? '停用' : '启用' }}
|
|
</el-link>
|
|
</template>
|
|
<template #cityCode-form>
|
|
<el-select
|
|
v-model="form.cityCode"
|
|
clearable
|
|
filterable
|
|
:loading="cityLoading"
|
|
:disabled="isCityDisabled"
|
|
placeholder="请选择"
|
|
style="width: 100%"
|
|
@change="handleCityChange"
|
|
>
|
|
<el-option
|
|
v-for="item in cityOptions"
|
|
:key="item.id"
|
|
:label="item.title"
|
|
:value="item.id"
|
|
/>
|
|
</el-select>
|
|
</template>
|
|
<template #districtCode-form>
|
|
<el-select
|
|
v-model="form.districtCode"
|
|
clearable
|
|
filterable
|
|
:loading="districtLoading"
|
|
:disabled="isDistrictDisabled"
|
|
placeholder="请选择"
|
|
style="width: 100%"
|
|
@change="handleDistrictChange"
|
|
>
|
|
<el-option
|
|
v-for="item in districtOptions"
|
|
:key="item.id"
|
|
:label="item.title"
|
|
:value="item.id"
|
|
/>
|
|
</el-select>
|
|
</template>
|
|
<template #detailAddress="{ row }">
|
|
<span class="port-terminal-page__detail-address">{{ row.detailAddress || '-' }}</span>
|
|
</template>
|
|
<template #detailAddress-form>
|
|
<el-input
|
|
v-model="form.detailAddress"
|
|
class="detail-address-input"
|
|
readonly
|
|
maxlength="255"
|
|
show-word-limit
|
|
placeholder="点击选择地图地址"
|
|
@click="openAddressMapPicker"
|
|
/>
|
|
</template>
|
|
<template #remark-form>
|
|
<el-input
|
|
v-model="form.remark"
|
|
class="remark-input"
|
|
type="textarea"
|
|
:rows="2"
|
|
maxlength="200"
|
|
show-word-limit
|
|
placeholder="请输入"
|
|
/>
|
|
</template>
|
|
<template #longitude-form>
|
|
<el-input
|
|
v-model="form.longitude"
|
|
clearable
|
|
placeholder="请输入经度"
|
|
@input="handleCoordinateInput('longitude', $event)"
|
|
/>
|
|
</template>
|
|
<template #latitude-form>
|
|
<el-input
|
|
v-model="form.latitude"
|
|
clearable
|
|
placeholder="请输入纬度"
|
|
@input="handleCoordinateInput('latitude', $event)"
|
|
/>
|
|
</template>
|
|
</avue-crud>
|
|
<empty-pagination
|
|
:page="page"
|
|
@size-change="sizeChange"
|
|
@current-change="currentChange"
|
|
@load="onLoad(page, query)"
|
|
/>
|
|
|
|
<el-dialog title="港口码头主数据导入" append-to-body v-model="excelBox" width="500px">
|
|
<avue-form :option="excelOption" v-model="excelForm">
|
|
<template #excelTemplate>
|
|
<el-button type="primary" @click="handleTemplate">
|
|
点击下载<i class="el-icon-download el-icon--right"></i>
|
|
</el-button>
|
|
</template>
|
|
</avue-form>
|
|
</el-dialog>
|
|
<address-map-picker
|
|
v-model="addressMapPickerVisible"
|
|
:address="form.detailAddress"
|
|
:longitude="form.longitude"
|
|
:latitude="form.latitude"
|
|
@confirm="handleAddressMapConfirm"
|
|
/>
|
|
</basic-container>
|
|
</template>
|
|
|
|
<script>
|
|
import {
|
|
getList,
|
|
getDetail,
|
|
submit,
|
|
remove,
|
|
changeStatus,
|
|
getPortSelect,
|
|
} from '@/api/base/port-terminal';
|
|
import { getDetail as getRegionDetail, getLazyTree } from '@/api/base/region';
|
|
import { exportBlob } from '@/api/common';
|
|
import { mapGetters } from 'vuex';
|
|
import { downloadXls } from '@/utils/util';
|
|
import { openImportDialog } from '@/utils/import-excel';
|
|
import { formatUpdateUserName } from '@/utils/audit';
|
|
import { getToken, isAdministrator } from '@/utils/auth';
|
|
import { getCoordinateValidationMessage, normalizeCoordinateInput } from '@/utils/coordinate';
|
|
import AddressMapPicker from '@/components/address-map-picker/main.vue';
|
|
import NProgress from 'nprogress';
|
|
import 'nprogress/nprogress.css';
|
|
|
|
const DEFAULT_COUNTRY_CODE = '+86';
|
|
const newLocal = '请选择类型';
|
|
export default {
|
|
components: {
|
|
AddressMapPicker,
|
|
},
|
|
data() {
|
|
const validateCode = (rule, value, callback) => {
|
|
const code = String(value || '').toUpperCase();
|
|
if (this.form.category === '港口') {
|
|
if (!/^[A-Z]{5}$/.test(code)) {
|
|
callback(new Error('港口编码为5位大写字母,如:CNSHG'));
|
|
} else {
|
|
callback();
|
|
}
|
|
return;
|
|
}
|
|
if (this.form.category === '码头') {
|
|
if (!/^[A-Z]{5}-[A-Z0-9]+$/.test(code)) {
|
|
callback(new Error('码头编码格式为“港口编码-码头标识”,如:CNSHG-CT1'));
|
|
} else if (this.form.parentCode && !code.startsWith(`${this.form.parentCode}-`)) {
|
|
callback(new Error(`码头编码须以所选上级港口编码 ${this.form.parentCode}- 开头`));
|
|
} else {
|
|
callback();
|
|
}
|
|
return;
|
|
}
|
|
callback();
|
|
};
|
|
const validateLongitude = (rule, value, callback) => {
|
|
const message = getCoordinateValidationMessage(value, 'longitude');
|
|
if (message) {
|
|
callback(new Error(message));
|
|
} else {
|
|
callback();
|
|
}
|
|
};
|
|
const validateLatitude = (rule, value, callback) => {
|
|
const message = getCoordinateValidationMessage(value, 'latitude');
|
|
if (message) {
|
|
callback(new Error(message));
|
|
} else {
|
|
callback();
|
|
}
|
|
};
|
|
return {
|
|
form: {},
|
|
query: {},
|
|
loading: true,
|
|
data: [],
|
|
excelBox: false,
|
|
addressMapPickerVisible: false,
|
|
excelForm: {},
|
|
portOptions: [],
|
|
countryOptions: [],
|
|
provinceOptions: [],
|
|
provinceOptionCache: {},
|
|
cityOptions: [],
|
|
cityLoading: false,
|
|
cityOptionCache: {},
|
|
districtOptions: [],
|
|
districtLoading: false,
|
|
districtOptionCache: {},
|
|
page: {
|
|
pageSize: 10,
|
|
pageSizes: [10, 20, 50, 100],
|
|
currentPage: 1,
|
|
total: 0,
|
|
},
|
|
selectionList: [],
|
|
option: {
|
|
height: 'auto',
|
|
calcHeight: 32,
|
|
dialogWidth: 900,
|
|
dialogCustomClass: 'port-terminal-edit-dialog',
|
|
labelPosition: 'right',
|
|
labelWidth: '108px',
|
|
tip: false,
|
|
searchShow: true,
|
|
searchLabelWidth: 88,
|
|
searchIcon: true,
|
|
searchIndex: 4,
|
|
searchMenuSpan: 24,
|
|
searchMenuPosition: 'right',
|
|
border: true,
|
|
index: true,
|
|
indexLabel: '序号',
|
|
indexWidth: 70,
|
|
viewBtn: false,
|
|
delBtn: false,
|
|
editBtn: false,
|
|
selection: true,
|
|
dialogClickModal: false,
|
|
menuWidth: 180,
|
|
column: [
|
|
{
|
|
label: '编码',
|
|
prop: 'code',
|
|
search: true,
|
|
formslot: true,
|
|
width: 140,
|
|
span: 12,
|
|
order: 89,
|
|
placeholder: '请输入编码',
|
|
rules: [
|
|
{ required: true, message: '请输入编码', trigger: 'blur' },
|
|
{ validator: validateCode, trigger: 'blur' },
|
|
],
|
|
change: ({ value }) => this.handleCodeChange(value),
|
|
blur: ({ value }) => this.handleCodeChange(value),
|
|
},
|
|
{
|
|
label: '港口/码头名称',
|
|
prop: 'name',
|
|
minWidth: 140,
|
|
search: true,
|
|
searchOrder: 7,
|
|
span: 12,
|
|
order: 90,
|
|
placeholder: '请输入',
|
|
rules: [{ required: true, message: '请输入港口/码头名称', trigger: 'blur' }],
|
|
},
|
|
{
|
|
label: '类型',
|
|
prop: 'category',
|
|
type: 'select',
|
|
search: true,
|
|
searchOrder: 6,
|
|
formslot: true,
|
|
span: 24,
|
|
order: 102,
|
|
dicData: [
|
|
{ label: '全部', value: '' },
|
|
{ label: '港口', value: '港口' },
|
|
{ label: '码头', value: '码头' },
|
|
],
|
|
rules: [{ required: true, message: '请选择类型', trigger: 'change' }],
|
|
change: ({ value }) => this.handleCategoryChange(value),
|
|
},
|
|
{
|
|
label: '上级港口',
|
|
prop: 'parentId',
|
|
type: 'select',
|
|
props: {
|
|
label: 'name',
|
|
value: 'id',
|
|
},
|
|
dicData: [],
|
|
hide: true,
|
|
span: 12,
|
|
order: 101,
|
|
placeholder: '请选择',
|
|
rules: [{ required: false, message: '请选择上级港口', trigger: 'change' }],
|
|
change: ({ value }) => this.handleParentChange(value),
|
|
},
|
|
{
|
|
label: '上级港口',
|
|
prop: 'parentName',
|
|
minWidth: 140,
|
|
slot: true,
|
|
addDisplay: false,
|
|
editDisplay: false,
|
|
},
|
|
{
|
|
label: '上级港口编码',
|
|
prop: 'parentCode',
|
|
minWidth: 120,
|
|
slot: true,
|
|
span: 12,
|
|
order: 101,
|
|
disabled: true,
|
|
addDisplay: false,
|
|
editDisplay: false,
|
|
placeholder: '自动带出',
|
|
},
|
|
{
|
|
label: '国家',
|
|
prop: 'country',
|
|
type: 'select',
|
|
search: true,
|
|
searchOrder: 5,
|
|
formslot: true,
|
|
filterable: true,
|
|
dicData: [],
|
|
span: 12,
|
|
order: 89,
|
|
minWidth: 140,
|
|
overHidden: false,
|
|
placeholder: '自动带出',
|
|
searchPlaceholder: '请选择 国家',
|
|
change: ({ value }) => this.handleCountryNameChange(value),
|
|
rules: [{ required: true, message: '请选择国家', trigger: 'change' }],
|
|
},
|
|
{
|
|
label: '所属省份',
|
|
prop: 'provinceCode',
|
|
formslot: true,
|
|
hide: true,
|
|
span: 12,
|
|
order: 88,
|
|
placeholder: '请选择',
|
|
rules: [{ required: true, message: '请选择所属省份', trigger: 'change' }],
|
|
},
|
|
{
|
|
label: '所属省份',
|
|
prop: 'provinceName',
|
|
addDisplay: false,
|
|
editDisplay: false,
|
|
minWidth: 120,
|
|
},
|
|
{
|
|
label: '城市',
|
|
prop: 'cityCode',
|
|
formslot: true,
|
|
hide: true,
|
|
span: 12,
|
|
order: 87,
|
|
placeholder: '请选择',
|
|
rules: [{ required: true, message: '请选择所属城市', trigger: 'change' }],
|
|
},
|
|
{
|
|
label: '城市',
|
|
prop: 'city',
|
|
type: 'select',
|
|
search: true,
|
|
searchOrder: 4,
|
|
filterable: true,
|
|
dicData: [],
|
|
addDisplay: false,
|
|
editDisplay: false,
|
|
minWidth: 100,
|
|
change: ({ value }) => this.handleCityNameChange(value),
|
|
},
|
|
{
|
|
label: '区县',
|
|
prop: 'districtCode',
|
|
type: 'select',
|
|
formslot: true,
|
|
hide: true,
|
|
order: 86,
|
|
props: {
|
|
label: 'title',
|
|
value: 'id',
|
|
},
|
|
dicData: [],
|
|
span: 12,
|
|
placeholder: '请选择',
|
|
rules: [{ required: true, message: '请选择区县', trigger: 'change' }],
|
|
},
|
|
{
|
|
label: '区县',
|
|
prop: 'districtName',
|
|
type: 'select',
|
|
search: true,
|
|
searchOrder: 3,
|
|
filterable: true,
|
|
dicData: [],
|
|
minWidth: 100,
|
|
hide: true,
|
|
addDisplay: false,
|
|
editDisplay: false,
|
|
},
|
|
{
|
|
label: '经度',
|
|
prop: 'longitude',
|
|
formslot: true,
|
|
minWidth: 130,
|
|
span: 12,
|
|
order: 68,
|
|
placeholder: '请输入',
|
|
overHidden: false,
|
|
rules: [
|
|
{ required: true, message: '请输入经度', trigger: 'blur' },
|
|
{ validator: validateLongitude, trigger: 'blur' },
|
|
],
|
|
},
|
|
{
|
|
label: '纬度',
|
|
prop: 'latitude',
|
|
formslot: true,
|
|
minWidth: 130,
|
|
span: 12,
|
|
order: 66,
|
|
placeholder: '请输入',
|
|
overHidden: false,
|
|
rules: [
|
|
{ required: true, message: '请输入纬度', trigger: 'blur' },
|
|
{ validator: validateLatitude, trigger: 'blur' },
|
|
],
|
|
},
|
|
{
|
|
label: '行政区划编码',
|
|
prop: 'regionCode',
|
|
minWidth: 140,
|
|
span: 12,
|
|
placeholder: '选择区县后自动填写',
|
|
addDisplay: false,
|
|
editDisplay: false,
|
|
rules: [{ max: 32, message: '行政区划编码不能超过32字', trigger: 'blur' }],
|
|
},
|
|
{
|
|
label: '详细地址',
|
|
prop: 'detailAddress',
|
|
slot: true,
|
|
formslot: true,
|
|
span: 12,
|
|
minWidth: 220,
|
|
overHidden: false,
|
|
order: 90,
|
|
placeholder: '点击选择地图地址',
|
|
maxlength: 255,
|
|
showWordLimit: true,
|
|
rules: [
|
|
{ required: true, message: '请选择详细地址', trigger: 'change' },
|
|
{ max: 255, message: '详细地址不能超过255字', trigger: 'blur' },
|
|
],
|
|
},
|
|
{
|
|
label: '数据来源',
|
|
prop: 'dataSource',
|
|
type: 'select',
|
|
search: true,
|
|
searchOrder: 2,
|
|
minWidth: 120,
|
|
overHidden: false,
|
|
addDisplay: false,
|
|
editDisplay: false,
|
|
dicData: [
|
|
{ label: '全部', value: '' },
|
|
{ label: '批量导入', value: '批量导入' },
|
|
{ label: '手动输入', value: '手动录入' },
|
|
],
|
|
},
|
|
{
|
|
label: '备注',
|
|
prop: 'remark',
|
|
type: 'textarea',
|
|
minRows: 2,
|
|
span: 24,
|
|
formslot: true,
|
|
hide: true,
|
|
maxlength: 200,
|
|
showWordLimit: true,
|
|
rules: [{ max: 200, message: '备注不能超过200个字', trigger: 'blur' }],
|
|
},
|
|
{
|
|
label: '创建人',
|
|
prop: 'createUserName',
|
|
addDisplay: false,
|
|
editDisplay: false,
|
|
minWidth: 120,
|
|
},
|
|
{
|
|
label: '更新人',
|
|
prop: 'updateUserName',
|
|
formatter: formatUpdateUserName,
|
|
addDisplay: false,
|
|
editDisplay: false,
|
|
minWidth: 120,
|
|
},
|
|
{
|
|
label: '更新时间',
|
|
prop: 'updateTime',
|
|
sortable: true,
|
|
type: 'datetime',
|
|
format: 'YYYY-MM-DD HH:mm:ss',
|
|
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
|
addDisplay: false,
|
|
editDisplay: false,
|
|
minWidth: 160,
|
|
},
|
|
{
|
|
label: '创建时间',
|
|
prop: 'createTime',
|
|
sortable: true,
|
|
type: 'datetime',
|
|
format: 'YYYY-MM-DD HH:mm:ss',
|
|
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
|
addDisplay: false,
|
|
editDisplay: false,
|
|
minWidth: 160,
|
|
},
|
|
{
|
|
label: '状态',
|
|
prop: 'status',
|
|
type: 'select',
|
|
search: true,
|
|
searchOrder: 1,
|
|
slot: true,
|
|
dataType: 'number',
|
|
minWidth: 110,
|
|
overHidden: false,
|
|
addDisplay: false,
|
|
editDisplay: false,
|
|
dicData: [
|
|
{ label: '全部', value: '' },
|
|
{ label: '启用', value: 1 },
|
|
{ label: '停用', value: 2 },
|
|
],
|
|
},
|
|
],
|
|
},
|
|
excelOption: {
|
|
labelPosition: 'right',
|
|
submitBtn: false,
|
|
emptyBtn: false,
|
|
column: [
|
|
{
|
|
label: '模板上传',
|
|
prop: 'excelFile',
|
|
type: 'upload',
|
|
drag: true,
|
|
loadText: '模板上传中,请稍等',
|
|
span: 24,
|
|
propsHttp: {
|
|
res: 'data',
|
|
},
|
|
tip: '请上传 .xls,.xlsx 标准格式文件;日期支持 2026-08-02、2026-8-2、2026/8/2 等写法',
|
|
accept: '.xls,.xlsx',
|
|
action: '/blade-system/port-terminal/import-port-terminal',
|
|
},
|
|
{
|
|
label: '模板下载',
|
|
prop: 'excelTemplate',
|
|
formslot: true,
|
|
span: 24,
|
|
},
|
|
],
|
|
},
|
|
};
|
|
},
|
|
computed: {
|
|
...mapGetters(['permission', 'userInfo']),
|
|
permissionList() {
|
|
return {
|
|
addBtn: this.hasPermission('port_terminal_add'),
|
|
};
|
|
},
|
|
isAdmin() {
|
|
return isAdministrator(this.userInfo);
|
|
},
|
|
isParentRegionLocked() {
|
|
return this.form.category === '码头';
|
|
},
|
|
isCityDisabled() {
|
|
return this.isParentRegionLocked || !this.form.provinceCode;
|
|
},
|
|
isProvinceDisabled() {
|
|
return this.isParentRegionLocked || !this.form.countryCode;
|
|
},
|
|
isDistrictDisabled() {
|
|
return this.isParentRegionLocked || !this.form.cityCode;
|
|
},
|
|
ids() {
|
|
let ids = [];
|
|
this.selectionList.forEach(ele => {
|
|
ids.push(ele.id);
|
|
});
|
|
return ids.join(',');
|
|
},
|
|
},
|
|
created() {
|
|
this.loadCountryOptions();
|
|
this.loadPortOptions();
|
|
},
|
|
methods: {
|
|
hasPermission(code) {
|
|
return this.isAdmin || this.validData(this.permission && this.permission[code], false);
|
|
},
|
|
handleCodeChange(value) {
|
|
const code = String(value || '').toUpperCase();
|
|
if (this.form.category === '港口') {
|
|
this.form.code = code.replace(/[^A-Z]/g, '').slice(0, 5);
|
|
return;
|
|
}
|
|
if (this.form.category === '码头') {
|
|
const terminalCode = this.getTerminalCodePart(code).replace(/[^A-Z0-9]/g, '');
|
|
if (this.form.parentCode) {
|
|
this.handleTerminalCodeChange(terminalCode);
|
|
} else {
|
|
this.form.terminalCode = terminalCode;
|
|
this.form.code = code.replace(/[^A-Z0-9-]/g, '');
|
|
}
|
|
}
|
|
},
|
|
handleTerminalCodeChange(value) {
|
|
const terminalCode = String(value || '')
|
|
.toUpperCase()
|
|
.replace(/[^A-Z0-9]/g, '');
|
|
this.form.terminalCode = terminalCode;
|
|
this.form.code =
|
|
this.form.parentCode && terminalCode ? `${this.form.parentCode}-${terminalCode}` : '';
|
|
},
|
|
getTerminalCodePart(code) {
|
|
const value = String(code || '').toUpperCase();
|
|
if (this.form.parentCode && value.startsWith(`${this.form.parentCode}-`)) {
|
|
return value.slice(this.form.parentCode.length + 1);
|
|
}
|
|
return value.split('-').slice(1).join('-');
|
|
},
|
|
syncTerminalCode() {
|
|
if (this.form.category !== '码头') return;
|
|
this.form.terminalCode = this.getTerminalCodePart(this.form.code);
|
|
this.handleTerminalCodeChange(this.form.terminalCode);
|
|
},
|
|
setColumnDisplay(prop, display) {
|
|
const column = this.findColumn(this.option.column, prop);
|
|
if (!column) return;
|
|
column.display = display;
|
|
column.addDisplay = display;
|
|
column.editDisplay = display;
|
|
column.viewDisplay = display;
|
|
},
|
|
loadCountryOptions() {
|
|
return getLazyTree('0').then(res => {
|
|
this.countryOptions = (res.data.data || []).map(this.normalizeRegionOption);
|
|
const countryColumn = this.findColumn(this.option.column, 'country');
|
|
countryColumn.dicData = this.countryOptions.map(country => ({
|
|
label: country.title,
|
|
value: country.title,
|
|
id: country.id,
|
|
}));
|
|
});
|
|
},
|
|
loadPortOptions() {
|
|
getPortSelect().then(res => {
|
|
this.portOptions = res.data.data;
|
|
this.findColumn(this.option.column, 'parentId').dicData = this.portOptions;
|
|
});
|
|
},
|
|
clearParentPortFields() {
|
|
this.form.parentId = undefined;
|
|
this.form.parentCode = '';
|
|
this.form.parentName = '';
|
|
this.form.countryCode = '';
|
|
this.form.country = '';
|
|
this.clearProvinceValue();
|
|
},
|
|
handleCategoryChange(value, reset = true) {
|
|
const required = value === '码头';
|
|
const parentColumn = this.findColumn(this.option.column, 'parentId');
|
|
if (parentColumn && parentColumn.rules && parentColumn.rules[0]) {
|
|
parentColumn.rules[0].required = required;
|
|
}
|
|
this.setColumnDisplay('parentId', required);
|
|
this.setColumnDisplay('parentCode', required);
|
|
if (value === '港口') {
|
|
this.form.parentId = undefined;
|
|
this.form.parentCode = '';
|
|
this.form.parentName = '';
|
|
this.form.terminalCode = '';
|
|
this.form.code = reset ? '' : String(this.form.code || '').split('-')[0];
|
|
} else if (reset) {
|
|
this.clearParentPortFields();
|
|
}
|
|
this.handleCodeChange(this.form.code);
|
|
},
|
|
handleCountryChange(value) {
|
|
const country = this.countryOptions.find(item => item.id === value);
|
|
this.form.country = country ? country.title : '';
|
|
this.clearProvinceValue();
|
|
this.loadProvinceOptions(value);
|
|
},
|
|
handleCountryNameChange(value) {
|
|
const country = this.countryOptions.find(item => item.title === value);
|
|
this.setCityOptions([]);
|
|
this.setDistrictOptions([]);
|
|
this.clearSearchCityAndDistrict();
|
|
// 搜索栏为国家 → 城市 → 区县(无省份),选中国家后需直接加载该国下全部城市
|
|
this.loadCityOptionsByCountry(country ? country.id : '');
|
|
},
|
|
clearSearchCityAndDistrict() {
|
|
const searchForm = this.$refs.crud && this.$refs.crud.searchForm;
|
|
if (searchForm) {
|
|
searchForm.city = undefined;
|
|
searchForm.districtName = undefined;
|
|
}
|
|
if (this.query) {
|
|
this.query.city = undefined;
|
|
this.query.districtName = undefined;
|
|
}
|
|
},
|
|
loadCityOptionsByCountry(countryCode) {
|
|
if (!countryCode) {
|
|
this.setCityOptions([]);
|
|
return Promise.resolve([]);
|
|
}
|
|
this.cityLoading = true;
|
|
return this.loadProvinceOptions(countryCode)
|
|
.then(provinces =>
|
|
Promise.all((provinces || []).map(province => this.fetchCityOptions(province.id)))
|
|
)
|
|
.then(cityGroups => {
|
|
const cityMap = new Map();
|
|
cityGroups.flat().forEach(city => {
|
|
if (city && city.id && !cityMap.has(city.id)) {
|
|
cityMap.set(city.id, city);
|
|
}
|
|
});
|
|
const cities = Array.from(cityMap.values());
|
|
this.setCityOptions(cities);
|
|
return cities;
|
|
})
|
|
.finally(() => {
|
|
this.cityLoading = false;
|
|
});
|
|
},
|
|
fetchCityOptions(provinceCode) {
|
|
if (!provinceCode) {
|
|
return Promise.resolve([]);
|
|
}
|
|
if (this.cityOptionCache[provinceCode]) {
|
|
return Promise.resolve(this.cityOptionCache[provinceCode]);
|
|
}
|
|
return getLazyTree(provinceCode).then(res => {
|
|
const options = (res.data.data || []).map(this.normalizeRegionOption);
|
|
this.cityOptionCache[provinceCode] = options;
|
|
return options;
|
|
});
|
|
},
|
|
syncCountryName(countryCode) {
|
|
const country = this.countryOptions.find(item => item.id === countryCode);
|
|
if (country) {
|
|
this.form.country = country.title;
|
|
}
|
|
},
|
|
normalizeRegionOption(region) {
|
|
return {
|
|
...region,
|
|
id: region.id === undefined || region.id === null ? region.id : String(region.id),
|
|
parentCode:
|
|
region.parentCode === undefined || region.parentCode === null
|
|
? region.parentCode
|
|
: String(region.parentCode),
|
|
};
|
|
},
|
|
setCityOptions(cityOptions) {
|
|
this.cityOptions = cityOptions;
|
|
const cityCodeColumn = this.findColumn(this.option.column, 'cityCode');
|
|
if (cityCodeColumn) {
|
|
cityCodeColumn.dicData = cityOptions;
|
|
}
|
|
const cityColumn = this.findColumn(this.option.column, 'city');
|
|
if (cityColumn) {
|
|
cityColumn.dicData = cityOptions.map(city => ({
|
|
label: city.title,
|
|
value: city.title,
|
|
id: city.id,
|
|
}));
|
|
}
|
|
},
|
|
setProvinceOptions(provinceOptions) {
|
|
this.provinceOptions = provinceOptions;
|
|
const provinceCodeColumn = this.findColumn(this.option.column, 'provinceCode');
|
|
if (provinceCodeColumn) {
|
|
provinceCodeColumn.dicData = provinceOptions;
|
|
}
|
|
},
|
|
clearProvinceValue() {
|
|
this.form.provinceCode = undefined;
|
|
this.form.provinceName = '';
|
|
this.clearCityValue();
|
|
},
|
|
clearCityValue() {
|
|
this.form.cityCode = undefined;
|
|
this.form.city = '';
|
|
this.clearDistrictValue();
|
|
},
|
|
clearDistrictValue() {
|
|
this.form.districtCode = undefined;
|
|
this.form.districtName = '';
|
|
this.form.regionCode = '';
|
|
},
|
|
loadProvinceOptions(countryCode) {
|
|
if (!countryCode) {
|
|
this.setProvinceOptions([]);
|
|
return Promise.resolve([]);
|
|
}
|
|
if (this.provinceOptionCache[countryCode]) {
|
|
this.setProvinceOptions(this.provinceOptionCache[countryCode]);
|
|
return Promise.resolve(this.provinceOptionCache[countryCode]);
|
|
}
|
|
return getLazyTree(countryCode).then(res => {
|
|
const options = (res.data.data || []).map(this.normalizeRegionOption);
|
|
this.provinceOptionCache[countryCode] = options;
|
|
this.setProvinceOptions(options);
|
|
return options;
|
|
});
|
|
},
|
|
loadCityOptions(provinceCode) {
|
|
if (!provinceCode) {
|
|
this.setCityOptions([]);
|
|
return Promise.resolve([]);
|
|
}
|
|
if (this.cityOptionCache[provinceCode]) {
|
|
this.setCityOptions(this.cityOptionCache[provinceCode]);
|
|
return Promise.resolve(this.cityOptionCache[provinceCode]);
|
|
}
|
|
this.cityLoading = true;
|
|
return this.fetchCityOptions(provinceCode)
|
|
.then(options => {
|
|
this.setCityOptions(options);
|
|
return options;
|
|
})
|
|
.finally(() => {
|
|
this.cityLoading = false;
|
|
});
|
|
},
|
|
resolveCityCode(cityName, regionCode) {
|
|
const city = this.cityOptions.find(item => item.title === cityName || item.id === regionCode);
|
|
return city ? city.id : '';
|
|
},
|
|
resolveProvinceCode(provinceName) {
|
|
const province = this.provinceOptions.find(
|
|
item => item.title === provinceName || item.id === provinceName
|
|
);
|
|
return province ? province.id : '';
|
|
},
|
|
setDistrictOptions(districtOptions) {
|
|
this.districtOptions = districtOptions;
|
|
const districtCodeColumn = this.findColumn(this.option.column, 'districtCode');
|
|
if (districtCodeColumn) {
|
|
districtCodeColumn.dicData = districtOptions;
|
|
}
|
|
const districtNameColumn = this.findColumn(this.option.column, 'districtName');
|
|
if (districtNameColumn) {
|
|
districtNameColumn.dicData = districtOptions.map(district => ({
|
|
label: district.title,
|
|
value: district.title,
|
|
id: district.id,
|
|
}));
|
|
}
|
|
},
|
|
loadDistrictOptions(cityCode) {
|
|
if (!cityCode) {
|
|
this.setDistrictOptions([]);
|
|
return Promise.resolve([]);
|
|
}
|
|
if (this.districtOptionCache[cityCode]) {
|
|
this.setDistrictOptions(this.districtOptionCache[cityCode]);
|
|
return Promise.resolve(this.districtOptionCache[cityCode]);
|
|
}
|
|
this.districtLoading = true;
|
|
return getLazyTree(cityCode)
|
|
.then(res => {
|
|
const options = (res.data.data || []).map(this.normalizeRegionOption);
|
|
this.districtOptionCache[cityCode] = options;
|
|
this.setDistrictOptions(options);
|
|
return options;
|
|
})
|
|
.finally(() => {
|
|
this.districtLoading = false;
|
|
});
|
|
},
|
|
resolveDistrictCode(districtName, regionCode) {
|
|
const district = this.districtOptions.find(
|
|
item => item.id === regionCode || item.title === districtName
|
|
);
|
|
return district ? district.id : regionCode || '';
|
|
},
|
|
handleParentChange(value) {
|
|
const parent = this.portOptions.find(item => item.id === value);
|
|
if (!parent) {
|
|
this.clearParentPortFields();
|
|
this.syncTerminalCode();
|
|
return;
|
|
}
|
|
const terminalCode =
|
|
this.form.terminalCode ||
|
|
String(this.form.code || '')
|
|
.split('-')
|
|
.slice(1)
|
|
.join('-');
|
|
this.form.parentCode = parent.code;
|
|
this.form.parentName = parent.name;
|
|
this.form.terminalCode = terminalCode;
|
|
this.handleTerminalCodeChange(terminalCode);
|
|
this.form.country = parent.country;
|
|
this.form.provinceCode = parent.provinceCode;
|
|
this.form.provinceName = parent.provinceName;
|
|
this.form.city = parent.city;
|
|
this.form.districtCode = parent.districtCode;
|
|
this.form.districtName = parent.districtName;
|
|
this.form.regionCode = parent.regionCode;
|
|
this.resolveCountryCode(parent.country)
|
|
.then(countryCode => {
|
|
this.form.countryCode = countryCode;
|
|
this.syncCountryName(countryCode);
|
|
return this.loadProvinceOptions(countryCode);
|
|
})
|
|
.then(() => {
|
|
this.form.provinceCode =
|
|
String(parent.provinceCode || '') || this.resolveProvinceCode(parent.provinceName);
|
|
return this.loadCityOptions(this.form.provinceCode);
|
|
})
|
|
.then(() => {
|
|
this.form.cityCode = this.resolveCityCode(parent.city, parent.regionCode);
|
|
return this.loadDistrictOptions(this.form.cityCode);
|
|
})
|
|
.then(() => {
|
|
this.form.districtCode = this.resolveDistrictCode(parent.districtName, parent.regionCode);
|
|
this.handleDistrictChange(this.form.districtCode);
|
|
});
|
|
},
|
|
handleCityChange(value) {
|
|
const city = this.cityOptions.find(item => item.id === value);
|
|
this.form.city = city ? city.title : '';
|
|
this.clearDistrictValue();
|
|
this.loadDistrictOptions(value);
|
|
},
|
|
handleProvinceChange(value) {
|
|
const province = this.provinceOptions.find(item => item.id === value);
|
|
this.form.provinceName = province ? province.title : '';
|
|
this.clearCityValue();
|
|
this.loadCityOptions(value);
|
|
},
|
|
handleCityNameChange(value) {
|
|
const city = this.cityOptions.find(item => item.title === value);
|
|
this.setDistrictOptions([]);
|
|
const searchForm = this.$refs.crud && this.$refs.crud.searchForm;
|
|
if (searchForm) {
|
|
searchForm.districtName = undefined;
|
|
}
|
|
if (this.query) {
|
|
this.query.districtName = undefined;
|
|
}
|
|
this.loadDistrictOptions(city ? city.id : '');
|
|
},
|
|
handleDistrictChange(value) {
|
|
const district = this.districtOptions.find(item => item.id === value);
|
|
this.form.districtName = district ? district.title : '';
|
|
this.form.regionCode = value || '';
|
|
},
|
|
handleCoordinateInput(prop, value) {
|
|
this.form[prop] = normalizeCoordinateInput(value);
|
|
},
|
|
openAddressMapPicker() {
|
|
this.addressMapPickerVisible = true;
|
|
},
|
|
formatMapCoordinate(value) {
|
|
if (value === undefined || value === null || value === '') {
|
|
return '';
|
|
}
|
|
const number = Number(value);
|
|
if (!Number.isFinite(number)) {
|
|
return '';
|
|
}
|
|
return normalizeCoordinateInput(number.toFixed(6));
|
|
},
|
|
patchDialogForm(patch = {}) {
|
|
// 同步页面 form 与 Avue 弹窗内部 tableForm,避免只改了一边导致经纬度不刷新
|
|
Object.assign(this.form, patch);
|
|
const tableForm = this.$refs.crud?.tableForm;
|
|
if (tableForm && tableForm !== this.form) {
|
|
Object.assign(tableForm, patch);
|
|
}
|
|
},
|
|
handleAddressMapConfirm(payload) {
|
|
const selection = typeof payload === 'string' ? { address: payload } : payload || {};
|
|
const longitude = this.formatMapCoordinate(selection.longitude);
|
|
const latitude = this.formatMapCoordinate(selection.latitude);
|
|
const patch = {};
|
|
if (selection.address) {
|
|
patch.detailAddress = selection.address;
|
|
}
|
|
// 换地址时必须同步覆盖经纬度,不能保留上一次选点
|
|
if (longitude !== '' && latitude !== '') {
|
|
patch.longitude = longitude;
|
|
patch.latitude = latitude;
|
|
}
|
|
if (Object.keys(patch).length) {
|
|
this.patchDialogForm(patch);
|
|
}
|
|
this.applyMapRegionSelection(selection);
|
|
},
|
|
normalizeAmapCityName(city, province) {
|
|
if (Array.isArray(city)) {
|
|
return String(province || '').trim();
|
|
}
|
|
const cityName = String(city || '').trim();
|
|
return cityName || String(province || '').trim();
|
|
},
|
|
buildRegionLookupCodes(adcode) {
|
|
const code = String(adcode || '').trim();
|
|
if (!code) {
|
|
return [];
|
|
}
|
|
const codes = [code];
|
|
if (/^\d{6}$/.test(code)) {
|
|
if (code.endsWith('00')) {
|
|
const cityCode = code.slice(0, 4);
|
|
if (cityCode && !codes.includes(cityCode)) {
|
|
codes.push(cityCode);
|
|
}
|
|
}
|
|
if (code.endsWith('0000')) {
|
|
const provinceCode = code.slice(0, 2);
|
|
if (provinceCode && !codes.includes(provinceCode)) {
|
|
codes.push(provinceCode);
|
|
}
|
|
}
|
|
}
|
|
return codes;
|
|
},
|
|
fetchRegionDetailByCodes(codes = []) {
|
|
const tryNext = index => {
|
|
if (index >= codes.length) {
|
|
return Promise.resolve(null);
|
|
}
|
|
return getRegionDetail(codes[index])
|
|
.then(res => {
|
|
const detail = res.data && res.data.data;
|
|
if (detail && detail.code) {
|
|
return detail;
|
|
}
|
|
return tryNext(index + 1);
|
|
})
|
|
.catch(() => tryNext(index + 1));
|
|
};
|
|
return tryNext(0);
|
|
},
|
|
resolveCountryFromRegionDetail(detail = {}) {
|
|
const ancestors = String(detail.ancestors || '')
|
|
.split(',')
|
|
.map(item => String(item || '').trim())
|
|
.filter(Boolean);
|
|
for (let index = 0; index < ancestors.length; index += 1) {
|
|
const ancestorCode = ancestors[index];
|
|
if (this.countryOptions.some(item => item.id === ancestorCode)) {
|
|
return ancestorCode;
|
|
}
|
|
}
|
|
if (
|
|
detail.parentCode &&
|
|
Number(detail.regionLevel) === 1 &&
|
|
this.countryOptions.some(item => item.id === String(detail.parentCode))
|
|
) {
|
|
return String(detail.parentCode);
|
|
}
|
|
if (/^\d{6}$/.test(String(detail.code || ''))) {
|
|
return DEFAULT_COUNTRY_CODE;
|
|
}
|
|
return '';
|
|
},
|
|
applyRegionDetail(detail = {}) {
|
|
const countryHint = this.resolveCountryFromRegionDetail(detail);
|
|
return this.resolveCountryCode(countryHint || DEFAULT_COUNTRY_CODE)
|
|
.then(countryCode => {
|
|
this.form.countryCode = countryCode;
|
|
this.syncCountryName(countryCode);
|
|
return this.loadProvinceOptions(countryCode);
|
|
})
|
|
.then(() => {
|
|
this.form.provinceCode =
|
|
String(detail.provinceCode || '') || this.resolveProvinceCode(detail.provinceName);
|
|
const province = this.provinceOptions.find(item => item.id === this.form.provinceCode);
|
|
this.form.provinceName = province ? province.title : detail.provinceName || '';
|
|
return this.loadCityOptions(this.form.provinceCode);
|
|
})
|
|
.then(() => {
|
|
this.form.cityCode =
|
|
String(detail.cityCode || '') ||
|
|
this.resolveCityCode(detail.cityName, detail.districtCode || detail.code);
|
|
const city = this.cityOptions.find(item => item.id === this.form.cityCode);
|
|
this.form.city = city ? city.title : detail.cityName || '';
|
|
return this.loadDistrictOptions(this.form.cityCode);
|
|
})
|
|
.then(() => {
|
|
this.form.districtCode = this.resolveDistrictCode(
|
|
detail.districtName || detail.name,
|
|
detail.districtCode || detail.code
|
|
);
|
|
this.handleDistrictChange(this.form.districtCode);
|
|
});
|
|
},
|
|
applyMapRegionByNames(selection = {}) {
|
|
const provinceName = String(selection.province || selection.provinceName || '').trim();
|
|
const cityName = this.normalizeAmapCityName(
|
|
selection.city || selection.cityName,
|
|
provinceName
|
|
);
|
|
const districtName = String(selection.district || selection.districtName || '').trim();
|
|
const countryName = String(selection.country || selection.countryName || '').trim();
|
|
const adcode = String(selection.adcode || selection.regionCode || '').trim();
|
|
const countryPromise = countryName
|
|
? this.resolveCountryCode(countryName)
|
|
: Promise.resolve(/^\d{6}$/.test(adcode) ? DEFAULT_COUNTRY_CODE : '');
|
|
return countryPromise
|
|
.then(countryCode => {
|
|
if (!countryCode) {
|
|
return null;
|
|
}
|
|
this.form.countryCode = countryCode;
|
|
this.syncCountryName(countryCode);
|
|
return this.loadProvinceOptions(countryCode);
|
|
})
|
|
.then(provinces => {
|
|
if (!provinces) {
|
|
return null;
|
|
}
|
|
this.form.provinceCode = this.resolveProvinceCode(provinceName);
|
|
const province = this.provinceOptions.find(item => item.id === this.form.provinceCode);
|
|
this.form.provinceName = province ? province.title : provinceName;
|
|
if (!this.form.provinceCode) {
|
|
this.clearCityValue();
|
|
return null;
|
|
}
|
|
return this.loadCityOptions(this.form.provinceCode);
|
|
})
|
|
.then(cities => {
|
|
if (!cities) {
|
|
return null;
|
|
}
|
|
this.form.cityCode = this.resolveCityCode(cityName, adcode);
|
|
const city = this.cityOptions.find(item => item.id === this.form.cityCode);
|
|
this.form.city = city ? city.title : cityName;
|
|
if (!this.form.cityCode) {
|
|
this.clearDistrictValue();
|
|
return null;
|
|
}
|
|
return this.loadDistrictOptions(this.form.cityCode);
|
|
})
|
|
.then(districts => {
|
|
if (!districts) {
|
|
return;
|
|
}
|
|
this.form.districtCode = this.resolveDistrictCode(districtName, adcode);
|
|
this.handleDistrictChange(this.form.districtCode);
|
|
});
|
|
},
|
|
applyMapRegionSelection(selection = {}) {
|
|
if (this.isParentRegionLocked) {
|
|
return Promise.resolve();
|
|
}
|
|
const adcode = String(selection.adcode || selection.regionCode || '').trim();
|
|
const hasNameHint = Boolean(
|
|
selection.country ||
|
|
selection.countryName ||
|
|
selection.province ||
|
|
selection.provinceName ||
|
|
selection.city ||
|
|
selection.cityName ||
|
|
selection.district ||
|
|
selection.districtName
|
|
);
|
|
if (!adcode && !hasNameHint) {
|
|
return Promise.resolve();
|
|
}
|
|
const lookupCodes = this.buildRegionLookupCodes(adcode);
|
|
return this.fetchRegionDetailByCodes(lookupCodes)
|
|
.then(detail => {
|
|
if (detail) {
|
|
return this.applyRegionDetail(detail);
|
|
}
|
|
return this.applyMapRegionByNames(selection);
|
|
})
|
|
.catch(() => this.applyMapRegionByNames(selection));
|
|
},
|
|
resolveCountryCode(country) {
|
|
if (!country) {
|
|
return Promise.resolve('');
|
|
}
|
|
const countryOption = this.countryOptions.find(
|
|
item => item.title === country || item.id === country
|
|
);
|
|
if (countryOption) {
|
|
return Promise.resolve(countryOption.id);
|
|
}
|
|
return this.loadCountryOptions().then(() => {
|
|
const option = this.countryOptions.find(
|
|
item => item.title === country || item.id === country
|
|
);
|
|
return option ? option.id : DEFAULT_COUNTRY_CODE;
|
|
});
|
|
},
|
|
normalizeRow(row) {
|
|
const submitRow = { ...row };
|
|
submitRow.code = String(submitRow.code || '').toUpperCase();
|
|
submitRow.regionCode = String(submitRow.regionCode || '').trim();
|
|
submitRow.districtCode = String(submitRow.districtCode || '').trim();
|
|
submitRow.districtName = String(submitRow.districtName || '').trim();
|
|
submitRow.provinceCode = String(submitRow.provinceCode || '').trim();
|
|
submitRow.provinceName = String(submitRow.provinceName || '').trim();
|
|
submitRow.detailAddress = String(submitRow.detailAddress || '').trim();
|
|
submitRow.longitude = normalizeCoordinateInput(submitRow.longitude);
|
|
submitRow.latitude = normalizeCoordinateInput(submitRow.latitude);
|
|
if (submitRow.category === '港口') {
|
|
submitRow.parentId = undefined;
|
|
submitRow.parentCode = undefined;
|
|
submitRow.parentName = undefined;
|
|
}
|
|
if (!submitRow.dataSource) submitRow.dataSource = '手动录入';
|
|
if (!submitRow.status) submitRow.status = 1;
|
|
delete submitRow.terminalCode;
|
|
delete submitRow.cityCode;
|
|
delete submitRow.countryCode;
|
|
return submitRow;
|
|
},
|
|
validateRequiredFields(row) {
|
|
const isBlank = value => value === undefined || value === null || String(value).trim() === '';
|
|
if (isBlank(row.code)) {
|
|
this.$message.warning('请输入编码');
|
|
return false;
|
|
}
|
|
if (isBlank(row.name)) {
|
|
this.$message.warning('请输入港口/码头名称');
|
|
return false;
|
|
}
|
|
if (isBlank(row.category)) {
|
|
this.$message.warning(newLocal);
|
|
return false;
|
|
}
|
|
if (row.category === '码头' && isBlank(row.parentId)) {
|
|
this.$message.warning('请选择上级港口');
|
|
return false;
|
|
}
|
|
if (isBlank(row.provinceCode)) {
|
|
this.$message.warning('请选择所属省份');
|
|
return false;
|
|
}
|
|
if (isBlank(row.regionCode)) {
|
|
this.$message.warning('请选择区县');
|
|
return false;
|
|
}
|
|
if (isBlank(row.detailAddress)) {
|
|
this.$message.warning('请选择详细地址');
|
|
return false;
|
|
}
|
|
if (isBlank(row.longitude)) {
|
|
this.$message.warning('请输入经度');
|
|
return false;
|
|
}
|
|
const longitudeMessage = getCoordinateValidationMessage(row.longitude, 'longitude');
|
|
if (longitudeMessage) {
|
|
this.$message.warning(longitudeMessage);
|
|
return false;
|
|
}
|
|
if (isBlank(row.latitude)) {
|
|
this.$message.warning('请输入纬度');
|
|
return false;
|
|
}
|
|
const latitudeMessage = getCoordinateValidationMessage(row.latitude, 'latitude');
|
|
if (latitudeMessage) {
|
|
this.$message.warning(latitudeMessage);
|
|
return false;
|
|
}
|
|
return true;
|
|
},
|
|
stopSubmitLoading(loading) {
|
|
if (typeof loading === 'function') {
|
|
loading();
|
|
}
|
|
},
|
|
rowSave(row, done, loading) {
|
|
const submitRow = this.normalizeRow(row);
|
|
if (!this.validateRequiredFields(submitRow)) {
|
|
this.stopSubmitLoading(loading);
|
|
return;
|
|
}
|
|
submit(submitRow).then(
|
|
() => {
|
|
this.onLoad(this.page);
|
|
this.loadPortOptions();
|
|
this.$message({ type: 'success', message: '操作成功!' });
|
|
done();
|
|
},
|
|
error => {
|
|
window.console.log(error);
|
|
loading();
|
|
}
|
|
);
|
|
},
|
|
rowUpdate(row, index, done, loading) {
|
|
const submitRow = this.normalizeRow(row);
|
|
if (!this.validateRequiredFields(submitRow)) {
|
|
this.stopSubmitLoading(loading);
|
|
return;
|
|
}
|
|
submit(submitRow).then(
|
|
() => {
|
|
this.onLoad(this.page);
|
|
this.loadPortOptions();
|
|
this.$message({ type: 'success', message: '操作成功!' });
|
|
done();
|
|
},
|
|
error => {
|
|
window.console.log(error);
|
|
loading();
|
|
}
|
|
);
|
|
},
|
|
rowDel(row) {
|
|
this.$confirm('确定将选择数据删除?', {
|
|
confirmButtonText: '确定',
|
|
cancelButtonText: '取消',
|
|
type: 'warning',
|
|
})
|
|
.then(() => remove(row.id))
|
|
.then(() => {
|
|
this.onLoad(this.page);
|
|
this.loadPortOptions();
|
|
this.$message({ type: 'success', message: '操作成功!' });
|
|
});
|
|
},
|
|
handleDelete() {
|
|
if (this.selectionList.length === 0) {
|
|
this.$message.warning('请选择至少一条数据');
|
|
return;
|
|
}
|
|
this.$confirm('确定将选择数据删除?', {
|
|
confirmButtonText: '确定',
|
|
cancelButtonText: '取消',
|
|
type: 'warning',
|
|
})
|
|
.then(() => remove(this.ids))
|
|
.then(() => {
|
|
this.onLoad(this.page);
|
|
this.loadPortOptions();
|
|
this.$message({ type: 'success', message: '操作成功!' });
|
|
this.$refs.crud.toggleSelection();
|
|
});
|
|
},
|
|
handleStatus(row) {
|
|
const nextStatus = row.status === 1 ? 2 : 1;
|
|
const actionName = nextStatus === 1 ? '启用' : '停用';
|
|
this.$confirm(`是否确认${actionName}该条数据?`, {
|
|
confirmButtonText: '确定',
|
|
cancelButtonText: '取消',
|
|
type: 'warning',
|
|
})
|
|
.then(() => changeStatus(row.id, nextStatus))
|
|
.then(() => {
|
|
this.onLoad(this.page);
|
|
this.loadPortOptions();
|
|
this.$message({ type: 'success', message: '操作成功!' });
|
|
});
|
|
},
|
|
createEmptyPortTerminalForm() {
|
|
return {
|
|
category: '港口',
|
|
dataSource: '手动录入',
|
|
status: 1,
|
|
parentId: undefined,
|
|
parentCode: '',
|
|
parentName: '',
|
|
name: '',
|
|
code: '',
|
|
terminalCode: '',
|
|
country: '',
|
|
countryCode: '',
|
|
provinceCode: undefined,
|
|
provinceName: '',
|
|
cityCode: undefined,
|
|
city: '',
|
|
districtCode: undefined,
|
|
districtName: '',
|
|
regionCode: '',
|
|
detailAddress: '',
|
|
longitude: '',
|
|
latitude: '',
|
|
remark: '',
|
|
};
|
|
},
|
|
resetAddForm() {
|
|
// 新增必须整表重置,避免沿用上一次编辑/查看残留
|
|
this.form = this.createEmptyPortTerminalForm();
|
|
this.setProvinceOptions([]);
|
|
this.setCityOptions([]);
|
|
this.setDistrictOptions([]);
|
|
},
|
|
beforeOpen(done, type) {
|
|
if (type === 'add') {
|
|
this.resetAddForm();
|
|
this.handleCategoryChange('港口', false);
|
|
done();
|
|
this.$nextTick(() => {
|
|
this.$refs.crud?.clearValidate?.();
|
|
setTimeout(() => this.disableDialogAutocomplete(), 50);
|
|
});
|
|
return;
|
|
}
|
|
if (['edit', 'view'].includes(type)) {
|
|
getDetail(this.form.id).then(res => {
|
|
this.form = res.data.data;
|
|
this.form.terminalCode = this.getTerminalCodePart(this.form.code);
|
|
this.resolveCountryCode(this.form.country)
|
|
.then(countryCode => {
|
|
this.form.countryCode = countryCode;
|
|
this.syncCountryName(countryCode);
|
|
return this.loadProvinceOptions(countryCode);
|
|
})
|
|
.then(() => {
|
|
this.form.provinceCode =
|
|
String(this.form.provinceCode || '') ||
|
|
this.resolveProvinceCode(this.form.provinceName);
|
|
return this.loadCityOptions(this.form.provinceCode);
|
|
})
|
|
.then(() => {
|
|
this.form.cityCode = this.resolveCityCode(this.form.city, this.form.regionCode);
|
|
return this.loadDistrictOptions(this.form.cityCode);
|
|
})
|
|
.then(() => {
|
|
this.form.districtCode = this.resolveDistrictCode(
|
|
this.form.districtName,
|
|
this.form.regionCode
|
|
);
|
|
if (!this.form.regionCode) {
|
|
this.handleDistrictChange(this.form.districtCode);
|
|
}
|
|
this.handleCategoryChange(this.form.category, false);
|
|
this.syncTerminalCode();
|
|
done();
|
|
setTimeout(() => this.disableDialogAutocomplete(), 50);
|
|
});
|
|
});
|
|
}
|
|
},
|
|
disableDialogAutocomplete() {
|
|
this.$nextTick(() => {
|
|
const dialog = document.querySelector('.port-terminal-edit-dialog');
|
|
if (!dialog) return;
|
|
dialog.querySelectorAll('input, textarea').forEach(el => {
|
|
el.setAttribute('autocomplete', 'off');
|
|
el.setAttribute('autocorrect', 'off');
|
|
el.setAttribute('autocapitalize', 'off');
|
|
el.setAttribute('spellcheck', 'false');
|
|
});
|
|
});
|
|
},
|
|
searchReset() {
|
|
this.query = {};
|
|
this.setProvinceOptions([]);
|
|
this.setCityOptions([]);
|
|
this.setDistrictOptions([]);
|
|
this.onLoad(this.page);
|
|
},
|
|
searchChange(params, done) {
|
|
this.query = params;
|
|
this.page.currentPage = 1;
|
|
this.onLoad(this.page, params);
|
|
done();
|
|
},
|
|
selectionChange(list) {
|
|
this.selectionList = list;
|
|
},
|
|
selectionClear() {
|
|
this.selectionList = [];
|
|
this.$refs.crud.toggleSelection();
|
|
},
|
|
currentChange(currentPage) {
|
|
this.page.currentPage = currentPage;
|
|
},
|
|
sizeChange(pageSize) {
|
|
this.page.pageSize = pageSize;
|
|
},
|
|
refreshChange() {
|
|
this.onLoad(this.page, this.query);
|
|
},
|
|
onLoad(page, params = {}) {
|
|
this.loading = true;
|
|
getList(page.currentPage, page.pageSize, { ...params, ...this.query })
|
|
.then(res => {
|
|
const data = res.data.data;
|
|
this.page.total = data.total;
|
|
this.data = data.records;
|
|
this.selectionClear();
|
|
})
|
|
.finally(() => {
|
|
this.loading = false;
|
|
});
|
|
},
|
|
handleImport() {
|
|
openImportDialog(this, '港口码头主数据', () => {
|
|
this.loadPortOptions();
|
|
this.onLoad(this.page, this.query);
|
|
});
|
|
},
|
|
handleExport() {
|
|
this.$confirm('是否导出港口码头主数据?', '提示', {
|
|
confirmButtonText: '确定',
|
|
cancelButtonText: '取消',
|
|
type: 'warning',
|
|
}).then(() => {
|
|
NProgress.start();
|
|
exportBlob('/blade-system/port-terminal/export-port-terminal', 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.query,
|
|
ids: this.ids,
|
|
[this.website.tokenHeader]: getToken(),
|
|
};
|
|
},
|
|
handleTemplate() {
|
|
exportBlob(
|
|
`/blade-system/port-terminal/export-template?${this.website.tokenHeader}=${getToken()}`,
|
|
undefined,
|
|
{ feedback: true }
|
|
).then(res => {
|
|
downloadXls(res.data, '港口码头主数据模板.xlsx');
|
|
});
|
|
},
|
|
},
|
|
};
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.port-terminal-page__detail-address {
|
|
display: block;
|
|
white-space: normal;
|
|
word-break: break-all;
|
|
line-height: 20px;
|
|
padding: 4px 0;
|
|
}
|
|
|
|
.detail-address-input {
|
|
cursor: pointer;
|
|
|
|
:deep(.el-input__wrapper),
|
|
:deep(.el-input__inner) {
|
|
cursor: pointer;
|
|
}
|
|
}
|
|
</style>
|
|
|
|
<style lang="scss">
|
|
/* 港口码头编辑/新增弹窗:与全局表单项行距保持一致 */
|
|
.port-terminal-edit-dialog {
|
|
.el-form-item,
|
|
.el-form-item--default.el-form-item,
|
|
.el-form-item--small.el-form-item,
|
|
.el-form-item--large.el-form-item,
|
|
.el-form-item--medium.el-form-item {
|
|
margin-bottom: 20px;
|
|
}
|
|
}
|
|
</style>
|