Files
tms-erp-web/src/views/base/common-address.vue
b2894lxlx 81c98f6ad8 1、完善客商模块
2、完善基础数据模块
3、完善规范
2026-08-01 01:23:13 +08:00

1230 lines
39 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="common-address-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-plus"
plain
v-if="hasPermission('common_address_add')"
@click="$refs.crud.rowAdd()"
>新增
</el-button>
<el-button
type="primary"
icon="el-icon-download"
plain
v-if="hasPermission('common_address_export')"
@click="handleExport"
>批量导出
</el-button>
<el-button
type="danger"
icon="el-icon-delete"
plain
v-if="hasPermission('common_address_delete')"
@click="handleDelete"
>批量删除
</el-button>
</template>
<template #siteCodeDisplay="{ row }">
<span>{{
row.addressType === '常规地址' ? '/' : row.siteCodeDisplay || row.siteCode
}}</span>
</template>
<template #menu="{ row, index }">
<el-button
type="primary"
text
v-if="hasPermission('common_address_view')"
@click="$refs.crud.rowView(row, index)"
>
查看
</el-button>
<el-button
type="primary"
text
v-if="hasPermission('common_address_edit') && !row.readonly"
@click="$refs.crud.rowEdit(row, index)"
>
编辑
</el-button>
<el-button
type="primary"
text
v-if="hasPermission('common_address_delete') && !row.readonly"
@click="rowDel(row)"
>
删除
</el-button>
</template>
<template #addressType-form>
<el-radio-group
v-model="form.addressType"
:disabled="dialogReadonly"
@change="handleAddressTypeChange"
>
<el-radio label="常规地址">常规地址</el-radio>
<el-radio label="港口/码头">港口/码头</el-radio>
<el-radio label="铁路车站">铁路车站</el-radio>
<el-radio label="空港机场">空港机场</el-radio>
</el-radio-group>
</template>
<template #portId-form>
<el-select
v-model="form.portId"
clearable
filterable
remote
:remote-method="loadPortOptions"
:loading="portLoading"
:disabled="dialogReadonly"
placeholder="请选择港口"
style="width: 100%"
@change="handlePortChange"
>
<el-option
v-for="item in portOptions"
:key="item.id"
:label="formatSourceLabel(item)"
:value="item.id"
/>
</el-select>
</template>
<template #terminalId-form>
<el-select
v-model="form.terminalId"
clearable
filterable
remote
:remote-method="loadTerminalOptions"
:loading="terminalLoading"
:disabled="dialogReadonly || !form.portId"
:placeholder="form.portId ? '请选择码头' : '请先选择港口'"
style="width: 100%"
@change="handleTerminalChange"
>
<el-option
v-for="item in terminalOptions"
:key="item.id"
:label="formatSourceLabel(item)"
:value="item.id"
/>
</el-select>
</template>
<template #stationId-form>
<el-select
v-model="form.stationId"
clearable
filterable
remote
:remote-method="loadStationOptions"
:loading="stationLoading"
:disabled="dialogReadonly"
placeholder="请选择车站名称"
style="width: 100%"
@change="handleStationChange"
>
<el-option
v-for="item in stationOptions"
:key="item.id"
:label="formatSourceLabel(item)"
:value="item.id"
/>
</el-select>
</template>
<template #airportId-form>
<el-select
v-model="form.airportId"
clearable
filterable
remote
:remote-method="loadAirportOptions"
:loading="airportLoading"
:disabled="dialogReadonly"
placeholder="请选择机场标准名称"
style="width: 100%"
@change="handleAirportChange"
>
<el-option
v-for="item in airportOptions"
:key="item.id"
:label="formatSourceLabel(item)"
:value="item.id"
/>
</el-select>
</template>
<template #detailAddress-form>
<div class="common-address-page__map-address">
<el-input
v-model="form.regionName"
readonly
placeholder="行政区划自动带出"
:disabled="dialogReadonly"
/>
<el-input
v-model="form.detailAddress"
readonly
maxlength="255"
placeholder="点击后弹出地图组件"
suffix-icon="el-icon-location"
:disabled="dialogReadonly"
@click="handleMapPick"
/>
</div>
</template>
</avue-crud>
<el-dialog
title="地图选择行政区划"
append-to-body
v-model="mapBox"
width="920px"
class="common-address-page__map-dialog"
@opened="initAmap"
>
<div class="common-address-page__map-toolbar">
<el-input
v-model="mapKeyword"
clearable
placeholder="输入地址关键词"
@keyup.enter="searchMapKeyword"
/>
<el-button
type="primary"
icon="el-icon-search"
:loading="mapLoading"
@click="searchMapKeyword"
>
搜索
</el-button>
</div>
<div ref="amap" class="common-address-page__map"></div>
<div class="common-address-page__map-info">
<span>{{ mapStatus }}</span>
<span v-if="mapSelected.regionName">行政区划{{ mapSelected.regionName }}</span>
</div>
<template #footer>
<el-button @click="mapBox = false">取消</el-button>
<el-button type="primary" :disabled="!mapSelected.longitude" @click="confirmMapPick">
确定
</el-button>
</template>
</el-dialog>
</basic-container>
</template>
<script>
import { getDetail, getList, remove, submit } from '@/api/base/common-address';
import { getDeptTree } from '@/api/system/dept';
import {
getDetail as getPortTerminalDetail,
getList as getPortTerminalList,
} from '@/api/base/port-terminal';
import {
getDetail as getRailwayStationDetail,
getList as getRailwayStationList,
} from '@/api/base/railway-station';
import {
getDetail as getAirportMasterDetail,
getList as getAirportMasterList,
} from '@/api/base/airport-master';
import { exportBlob } from '@/api/common';
import { createOption } from '@/option/base/common-address';
import { mapGetters } from 'vuex';
import { downloadXls } from '@/utils/util';
import { getToken } from '@/utils/auth';
import NProgress from 'nprogress';
import 'nprogress/nprogress.css';
const AMAP_KEY = '653b7cf105ad7fb8ec9b2f5198ade315';
const AMAP_SECURITY_CODE = '5aab65c632e48ebae0d0e28f5b28e21a';
let amapLoader;
export default {
data() {
return {
form: {},
query: {},
loading: true,
data: [],
allDept: 0,
dialogReadonly: false,
deptOptions: [],
portLoading: false,
terminalLoading: false,
stationLoading: false,
airportLoading: false,
portOptions: [],
terminalOptions: [],
stationOptions: [],
airportOptions: [],
mapBox: false,
mapLoading: false,
mapKeyword: '',
mapStatus: '可搜索地址或点击地图选点',
mapSelected: {},
amap: null,
amapMarker: null,
amapGeocoder: null,
option: createOption(),
page: {
pageSize: 10,
pageSizes: [10, 20, 50, 100],
currentPage: 1,
total: 0,
},
selectionList: [],
};
},
computed: {
...mapGetters(['permission', 'userInfo']),
permissionList() {
return {
addBtn: this.hasPermission('common_address_add'),
};
},
isAdmin() {
const authority = this.userInfo && this.userInfo.authority;
return Array.isArray(authority)
? authority.includes('admin')
: String(authority || '').includes('admin');
},
ids() {
return this.selectionList.map(item => item.id).join(',');
},
},
created() {
this.initDeptTree();
},
methods: {
hasPermission(code) {
return this.isAdmin || this.validData(this.permission && this.permission[code], false);
},
initDeptTree() {
getDeptTree(this.userInfo.tenantId).then(res => {
this.deptOptions = this.flattenDept(res.data.data || []);
const deptColumn = this.findColumn(this.option.column, 'deptId');
deptColumn.dicData = this.deptOptions;
});
},
flattenDept(tree, level = 0) {
const result = [];
tree.forEach(item => {
result.push({
label: `${' '.repeat(level)}${item.title || item.deptName || item.name}`,
value: item.id,
rawLabel: item.title || item.deptName || item.name,
});
if (item.children && item.children.length) {
result.push(...this.flattenDept(item.children, level + 1));
}
});
return result;
},
getRecords(res) {
const data = res && res.data && res.data.data;
if (Array.isArray(data)) return data;
return (data && data.records) || [];
},
sameId(left, right) {
return String(left || '') === String(right || '');
},
normalizeSourceOption(item = {}) {
return {
...item,
id:
item.id === undefined || item.id === null ? String(item.sourceId || '') : String(item.id),
};
},
normalizeSourceOptions(list = []) {
return list.map(this.normalizeSourceOption).filter(item => item.id);
},
ensureOption(listName, source) {
if (!source || !source.id) return;
const exists = this[listName].some(item => this.sameId(item.id, source.id));
if (!exists) {
this[listName] = [source, ...this[listName]];
}
},
formatSourceLabel(item = {}) {
const code = item.iataCode || item.code || item.tmisCode || item.siteCode;
return code ? `${item.name || item.addressName}${code}` : item.name || item.addressName;
},
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;
},
setColumnRules(prop, rules) {
const column = this.findColumn(this.option.column, prop);
if (column) {
column.rules = rules;
}
},
syncSourceColumnDisplay(addressType) {
const isPortTerminal = addressType === '港口/码头';
const isRailwayStation = addressType === '铁路车站';
const isAirport = addressType === '空港机场';
this.setColumnDisplay('portId', isPortTerminal);
this.setColumnDisplay('terminalId', isPortTerminal);
this.setColumnDisplay('stationId', isRailwayStation);
this.setColumnDisplay('airportId', isAirport);
this.setColumnRules(
'portId',
isPortTerminal ? [{ required: true, message: '请选择港口', trigger: 'change' }] : []
);
this.setColumnRules(
'terminalId',
isPortTerminal ? [{ required: true, message: '请选择码头', trigger: 'change' }] : []
);
this.setColumnRules(
'stationId',
isRailwayStation ? [{ required: true, message: '请选择车站名称', trigger: 'change' }] : []
);
this.setColumnRules(
'airportId',
isAirport ? [{ required: true, message: '请选择机场标准名称', trigger: 'change' }] : []
);
},
clearSourceFields(clearAddress = true) {
this.form.sourceId = undefined;
this.form.portId = undefined;
this.form.terminalId = undefined;
this.form.stationId = undefined;
this.form.airportId = undefined;
this.form.siteCode = this.form.addressType === '常规地址' ? '/' : '';
this.form.siteCodeDisplay = this.form.siteCode;
this.terminalOptions = [];
if (clearAddress) {
this.form.addressName = '';
this.form.detailAddress = '';
this.form.regionCode = '';
this.form.regionName = '';
this.form.longitude = '';
this.form.latitude = '';
}
},
loadPortOptions(keyword = '') {
this.portLoading = true;
getPortTerminalList(1, 30, {
category: '港口',
name: String(keyword || '').trim(),
status: 1,
})
.then(res => {
this.portOptions = this.normalizeSourceOptions(this.getRecords(res));
})
.finally(() => {
this.portLoading = false;
});
},
loadTerminalOptions(keyword = '') {
if (!this.form.portId) {
this.terminalOptions = [];
return;
}
this.terminalLoading = true;
getPortTerminalList(1, 30, {
category: '码头',
parentId: this.form.portId,
name: String(keyword || '').trim(),
status: 1,
})
.then(res => {
this.terminalOptions = this.normalizeSourceOptions(this.getRecords(res));
})
.finally(() => {
this.terminalLoading = false;
});
},
loadStationOptions(keyword = '') {
this.stationLoading = true;
getRailwayStationList(1, 30, {
name: String(keyword || '').trim(),
status: 1,
})
.then(res => {
this.stationOptions = this.normalizeSourceOptions(this.getRecords(res));
})
.finally(() => {
this.stationLoading = false;
});
},
loadAirportOptions(keyword = '') {
this.airportLoading = true;
getAirportMasterList(1, 30, {
name: String(keyword || '').trim(),
status: 1,
})
.then(res => {
this.airportOptions = this.normalizeSourceOptions(this.getRecords(res));
})
.finally(() => {
this.airportLoading = false;
});
},
handlePortChange(value) {
this.form.portId = value;
this.form.terminalId = undefined;
this.form.sourceId = undefined;
this.form.siteCode = '';
this.form.siteCodeDisplay = '';
this.terminalOptions = [];
if (value) {
this.loadTerminalOptions();
}
},
handleTerminalChange(value) {
const source = this.terminalOptions.find(item => this.sameId(item.id, value));
if (!source) {
this.form.sourceId = undefined;
this.form.siteCode = '';
this.form.siteCodeDisplay = '';
return;
}
this.applySourceAddress(source, { sourceId: value });
},
handleStationChange(value) {
const source = this.stationOptions.find(item => this.sameId(item.id, value));
if (!source) {
this.form.sourceId = undefined;
this.form.siteCode = '';
this.form.siteCodeDisplay = '';
return;
}
this.applySourceAddress(source, { sourceId: value });
},
handleAirportChange(value) {
const source = this.airportOptions.find(item => this.sameId(item.id, value));
if (!source) {
this.form.sourceId = undefined;
this.form.siteCode = '';
this.form.siteCodeDisplay = '';
return;
}
this.applySourceAddress(source, { sourceId: value });
},
buildRegionName(source = {}) {
const names = [
source.provinceName || source.country,
source.cityName || source.city,
source.districtName || source.regionName,
];
return names.filter(Boolean).join('');
},
getSourceSiteCode(source = {}) {
if (this.form.addressType === '空港机场') {
return source.iataCode || source.code || '';
}
return source.code || source.tmisCode || source.siteCode || '';
},
applySourceAddress(source = {}, { sourceId } = {}) {
const siteCode = this.getSourceSiteCode(source);
this.form.sourceId = String(sourceId || source.id || '');
this.form.siteCode = siteCode;
this.form.siteCodeDisplay = siteCode;
this.form.addressName = source.name || source.addressName || this.form.addressName;
this.form.detailAddress = source.detailAddress || this.form.detailAddress;
this.form.regionCode = source.regionCode || source.districtCode || this.form.regionCode;
this.form.regionName = this.buildRegionName(source) || this.form.regionName;
this.form.longitude = this.formatCoordinate(source.longitude) || this.form.longitude;
this.form.latitude = this.formatCoordinate(source.latitude) || this.form.latitude;
},
restoreSourceSelection() {
const addressType = this.form.addressType;
const sourceId = this.form.sourceId;
const siteCode = this.form.siteCode === '/' ? '' : this.form.siteCode;
if (addressType === '港口/码头' && sourceId) {
return getPortTerminalDetail(sourceId).then(res => {
const terminal = this.normalizeSourceOption(res.data.data || {});
this.form.terminalId = terminal.id;
this.form.portId =
terminal.parentId === undefined || terminal.parentId === null
? terminal.parentId
: String(terminal.parentId);
this.ensureOption('terminalOptions', terminal);
this.applySourceAddress(terminal, { sourceId: terminal.id });
if (!this.form.portId) {
return Promise.resolve();
}
return getPortTerminalDetail(this.form.portId)
.then(portRes => {
const port = this.normalizeSourceOption(portRes.data.data || {});
this.ensureOption('portOptions', port);
})
.catch(() => {
this.loadPortOptions();
});
});
}
if (addressType === '港口/码头' && siteCode) {
return getPortTerminalList(1, 1, { category: '码头', code: siteCode }).then(res => {
const terminal = this.normalizeSourceOptions(this.getRecords(res))[0];
if (!terminal) return;
this.form.sourceId = terminal.id;
this.ensureOption('terminalOptions', terminal);
return this.restoreSourceSelection();
});
}
if (addressType === '铁路车站' && sourceId) {
return getRailwayStationDetail(sourceId).then(res => {
const station = this.normalizeSourceOption(res.data.data || {});
this.form.stationId = station.id;
this.ensureOption('stationOptions', station);
this.applySourceAddress(station, { sourceId: station.id });
});
}
if (addressType === '铁路车站' && siteCode) {
return getRailwayStationList(1, 1, { code: siteCode }).then(res => {
const station = this.normalizeSourceOptions(this.getRecords(res))[0];
if (!station) return;
this.form.stationId = station.id;
this.ensureOption('stationOptions', station);
this.applySourceAddress(station, { sourceId: station.id });
});
}
if (addressType === '空港机场' && sourceId) {
return getAirportMasterDetail(sourceId).then(res => {
const airport = this.normalizeSourceOption(res.data.data || {});
this.form.airportId = airport.id;
this.ensureOption('airportOptions', airport);
this.applySourceAddress(airport, { sourceId: airport.id });
});
}
if (addressType === '空港机场' && siteCode) {
return getAirportMasterList(1, 1, { iataCode: siteCode }).then(res => {
const airport = this.normalizeSourceOptions(this.getRecords(res))[0];
if (!airport) return;
this.form.airportId = airport.id;
this.ensureOption('airportOptions', airport);
this.applySourceAddress(airport, { sourceId: airport.id });
});
}
return Promise.resolve();
},
handleAddressTypeChange(value, reset = true) {
this.syncSourceColumnDisplay(value);
if (reset) {
this.clearSourceFields(true);
if (value === '港口/码头') {
this.loadPortOptions();
} else if (value === '铁路车站') {
this.loadStationOptions();
} else if (value === '空港机场') {
this.loadAirportOptions();
}
}
},
formatCoordinate(value) {
if (value === undefined || value === null || value === '') {
return '';
}
const numberValue = Number(value);
return Number.isFinite(numberValue) ? numberValue.toFixed(6) : '';
},
normalizeRow(row) {
const trimValue = value => String(value || '').trim();
const submitRow = {
id: row.id,
addressName: trimValue(row.addressName),
addressType: trimValue(row.addressType),
sourceId: trimValue(row.sourceId),
siteCode: trimValue(row.siteCode),
detailAddress: trimValue(row.detailAddress),
regionCode: trimValue(row.regionCode),
regionName: trimValue(row.regionName),
longitude: this.formatCoordinate(row.longitude),
latitude: this.formatCoordinate(row.latitude),
deptId: trimValue(row.deptId),
contactName: trimValue(row.contactName),
contactPhone: trimValue(row.contactPhone),
remark: trimValue(row.remark),
};
if (submitRow.addressType === '常规地址') {
submitRow.siteCode = '/';
delete submitRow.sourceId;
}
if (!submitRow.id) {
delete submitRow.id;
}
if (!submitRow.sourceId) {
delete submitRow.sourceId;
}
if (!submitRow.deptId) {
delete submitRow.deptId;
}
return submitRow;
},
validateRequiredFields(row) {
if (!row.addressType) {
this.$message.warning('请选择类型');
return false;
}
if (!row.addressName) {
this.$message.warning('请输入地址名称');
return false;
}
if (row.addressName.length > 100) {
this.$message.warning('地址名称不能超过100个字');
return false;
}
if (row.addressType === '港口/码头') {
if (!this.form.portId) {
this.$message.warning('请选择港口');
return false;
}
if (!this.form.terminalId || !row.sourceId) {
this.$message.warning('请选择码头');
return false;
}
}
if (row.addressType === '铁路车站' && (!this.form.stationId || !row.sourceId)) {
this.$message.warning('请选择车站名称');
return false;
}
if (row.addressType === '空港机场' && (!this.form.airportId || !row.sourceId)) {
this.$message.warning('请选择机场标准名称');
return false;
}
if (!row.detailAddress) {
this.$message.warning('请输入详细地址');
return false;
}
if (row.detailAddress.length > 255) {
this.$message.warning('详细地址不能超过255个字');
return false;
}
if (!row.regionName) {
this.$message.warning('请选择行政区划');
return false;
}
if (row.contactName && row.contactName.length > 50) {
this.$message.warning('联系人不能超过50个字');
return false;
}
if (row.contactPhone && row.contactPhone.length > 20) {
this.$message.warning('联系方式不能超过20个字');
return false;
}
if (row.remark && row.remark.length > 200) {
this.$message.warning('备注不能超过200个字');
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.$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.$message({ type: 'success', message: '操作成功!' });
done();
},
error => {
window.console.log(error);
loading();
}
);
},
rowDel(row) {
this.$confirm('确定将选择数据删除?', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
.then(() => remove(row.id))
.then(res => {
this.afterRemove(res.data.data);
});
},
handleDelete() {
if (this.selectionList.length === 0) {
this.$message.warning('请选择至少一条数据');
return;
}
const editableSelection = this.selectionList.filter(item => !item.readonly);
if (!editableSelection.length) {
this.$message.warning('所选数据均为只读组织数据');
return;
}
this.$confirm('确定将选择数据删除?', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
.then(() => remove(editableSelection.map(item => item.id).join(',')))
.then(res => {
this.afterRemove(res.data.data);
});
},
afterRemove(result = {}) {
this.onLoad(this.page);
const successCount = result.successCount || 0;
const skippedCount = result.skippedCount || 0;
if (skippedCount) {
this.$message.warning(`成功删除${successCount}条,${skippedCount}条因业务引用已跳过`);
} else {
this.$message({ type: 'success', message: '删除成功!' });
}
this.$refs.crud.toggleSelection();
},
beforeOpen(done, type) {
this.dialogReadonly = type === 'view';
if (type === 'add') {
this.form.addressType = this.form.addressType || '常规地址';
this.handleAddressTypeChange(this.form.addressType, true);
done();
return;
}
if (['edit', 'view'].includes(type)) {
getDetail(this.form.id).then(res => {
this.form = {
...(res.data.data || {}),
sourceId:
res.data.data && res.data.data.sourceId
? String(res.data.data.sourceId)
: res.data.data && res.data.data.sourceId,
};
this.handleAddressTypeChange(this.form.addressType, false);
this.restoreSourceSelection()
.catch(() => {
if (this.form.addressType === '港口/码头') {
this.loadPortOptions();
} else if (this.form.addressType === '铁路车站') {
this.loadStationOptions();
} else if (this.form.addressType === '空港机场') {
this.loadAirportOptions();
}
})
.finally(() => {
done();
});
});
return;
}
done();
},
searchReset() {
this.query = {};
this.page.currentPage = 1;
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, allDept: this.allDept })
.then(res => {
const data = res.data.data;
this.page.total = data.total;
this.data = data.records;
this.selectionClear();
})
.finally(() => {
this.loading = false;
});
},
handleExport() {
this.$confirm('是否导出常用地址?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}).then(() => {
NProgress.start();
exportBlob(
'/blade-transport/common-address/export-common-address',
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,
allDept: this.allDept,
ids: this.ids,
[this.website.tokenHeader]: getToken(),
};
},
handleMapPick() {
if (this.dialogReadonly) {
return;
}
this.mapKeyword = this.form.detailAddress || this.form.regionName || '';
this.mapSelected = this.buildMapSelection({
lng: this.form.longitude,
lat: this.form.latitude,
address: this.form.detailAddress,
regionName: this.form.regionName,
regionCode: this.form.regionCode,
});
this.mapStatus = this.mapSelected.longitude
? '已加载当前选点,可重新选点'
: '可搜索地址或点击地图选点';
this.mapBox = true;
},
loadAmap() {
if (window.AMap && window.AMap.Map) {
return Promise.resolve();
}
if (!amapLoader) {
amapLoader = new Promise((resolve, reject) => {
window._AMapSecurityConfig = {
securityJsCode: AMAP_SECURITY_CODE,
};
const script = document.createElement('script');
script.src = `https://webapi.amap.com/maps?v=2.0&key=${AMAP_KEY}`;
script.async = true;
script.onload = resolve;
script.onerror = () => reject(new Error('高德地图组件加载失败'));
document.body.appendChild(script);
});
}
return amapLoader;
},
initAmap() {
this.loadAmap()
.then(() => {
this.$nextTick(() => {
if (!this.amap) {
const center = this.toLngLat(
this.mapSelected.longitude || 116.40769,
this.mapSelected.latitude || 39.89945
);
this.amap = new window.AMap.Map(this.$refs.amap, {
center,
zoom: this.mapSelected.longitude ? 14 : 11,
});
window.AMap.plugin(['AMap.ToolBar', 'AMap.Geocoder'], () => {
this.amap.addControl(new window.AMap.ToolBar());
if (!this.amapGeocoder) {
this.amapGeocoder = new window.AMap.Geocoder();
}
});
this.amap.on('click', event => this.pickMapPoint(event.lnglat));
} else {
if (typeof this.amap.resize === 'function') {
this.amap.resize();
}
}
if (this.mapSelected.longitude) {
this.renderMapMarker(
this.toLngLat(this.mapSelected.longitude, this.mapSelected.latitude)
);
}
});
})
.catch(() => {
this.$message.error('高德地图组件加载失败,请稍后重试或重新打开弹窗');
this.mapBox = false;
});
},
searchMapKeyword() {
const keyword = String(this.mapKeyword || '').trim();
if (!keyword) {
this.$message.warning('请输入地址关键词');
return;
}
this.loadAmap()
.then(() => {
this.mapLoading = true;
this.ensureAmapGeocoder()
.then(() => this.runAmapGeocode('location', keyword))
.then(result => {
const point = this.resolveMapPoint(result);
if (!point) {
this.mapStatus = '未找到匹配地址';
this.$message.warning('地图搜索无匹配地址');
return;
}
this.pickMapPoint(point, keyword);
})
.catch(() => {
this.mapStatus = '地图搜索失败';
this.$message.error('地图搜索失败,请稍后重试');
})
.finally(() => {
this.mapLoading = false;
});
})
.catch(() => {
this.$message.error('高德地图组件加载失败,请稍后重试或重新打开弹窗');
});
},
pickMapPoint(lnglat, keyword) {
const longitude = this.getPointLng(lnglat);
const latitude = this.getPointLat(lnglat);
if (longitude === undefined || latitude === undefined) {
this.$message.warning('选点坐标无效');
return;
}
const point = this.toLngLat(longitude, latitude);
this.renderMapMarker(point);
this.mapSelected = this.buildMapSelection({
lng: longitude,
lat: latitude,
address: keyword,
});
this.mapStatus = '正在反查地址...';
this.ensureAmapGeocoder()
.then(() => this.runAmapGeocode('address', point))
.then(result => {
const address = this.resolveMapAddress(result);
this.mapSelected = {
...this.mapSelected,
detailAddress: address.detailAddress || this.mapSelected.detailAddress,
regionName: address.regionName || this.mapSelected.regionName,
regionCode: address.regionCode || this.mapSelected.regionCode,
};
this.mapKeyword = this.mapSelected.detailAddress || this.mapKeyword;
this.mapStatus = this.mapSelected.detailAddress || '已选点,可确认回填';
})
.catch(() => {
this.mapStatus = '反查地址失败';
this.$message.error('反查地址失败,请重新选点');
});
},
renderMapMarker(point) {
if (!this.amap || !window.AMap) return;
if (this.amapMarker) {
this.amapMarker.setMap(null);
}
this.amapMarker = new window.AMap.Marker({
position: point,
});
this.amapMarker.setMap(this.amap);
this.amap.setCenter(point);
},
confirmMapPick() {
if (!this.mapSelected.longitude) {
this.$message.warning('请先搜索或点击地图完成选点');
return;
}
this.form.detailAddress = this.mapSelected.detailAddress || this.form.detailAddress;
this.form.regionName = this.mapSelected.regionName || this.form.regionName;
this.form.regionCode = this.mapSelected.regionCode || this.form.regionCode;
this.form.longitude = this.mapSelected.longitude;
this.form.latitude = this.mapSelected.latitude;
this.mapBox = false;
},
buildMapSelection({ lng, lat, address, regionName, regionCode }) {
const longitude = this.formatCoordinate(lng);
const latitude = this.formatCoordinate(lat);
return {
longitude,
latitude,
detailAddress: address || '',
regionName: regionName || '',
regionCode: regionCode || '',
};
},
toLngLat(lng, lat) {
return new window.AMap.LngLat(Number(lng), Number(lat));
},
getPointLng(point) {
if (!point) return undefined;
if (typeof point.getLng === 'function') return point.getLng();
return point.lng ?? point.lon;
},
getPointLat(point) {
if (!point) return undefined;
if (typeof point.getLat === 'function') return point.getLat();
return point.lat;
},
ensureAmapGeocoder() {
if (this.amapGeocoder) {
return Promise.resolve(this.amapGeocoder);
}
return new Promise((resolve, reject) => {
if (!window.AMap || !window.AMap.plugin) {
reject(new Error('高德地图组件未就绪'));
return;
}
window.AMap.plugin(['AMap.Geocoder'], () => {
try {
this.amapGeocoder = new window.AMap.Geocoder();
resolve(this.amapGeocoder);
} catch (error) {
reject(error);
}
});
});
},
runAmapGeocode(action, input) {
return new Promise((resolve, reject) => {
const timer = window.setTimeout(() => {
reject(new Error('高德地图请求超时'));
}, 10000);
const done = (status, result) => {
window.clearTimeout(timer);
if (status === 'complete' && result) {
resolve(result);
return;
}
reject(new Error('高德地图请求失败'));
};
try {
if (action === 'location') {
this.amapGeocoder.getLocation(input, done);
} else {
this.amapGeocoder.getAddress(input, done);
}
} catch (error) {
window.clearTimeout(timer);
reject(error);
}
});
},
resolveMapPoint(result) {
if (!result) return null;
const geocode = Array.isArray(result.geocodes) ? result.geocodes[0] : null;
if (geocode && geocode.location) return geocode.location;
if (result.location) return result.location;
if (result.lnglat) return result.lnglat;
if (result.getLng || result.lng || result.lon) return result;
return null;
},
resolveMapAddress(result = {}) {
const regeocode = result.regeocode || {};
const component = regeocode.addressComponent || {};
const city = Array.isArray(component.city) ? '' : component.city;
const regionName = [component.province, city, component.district].filter(Boolean).join('');
return {
detailAddress: regeocode.formattedAddress || '',
regionName,
regionCode: component.adcode || '',
};
},
},
};
</script>
<style lang="scss" scoped>
.common-address-page {
&__map-address {
display: flex;
gap: 10px;
align-items: center;
.el-input {
flex: 1;
min-width: 0;
}
.el-input:first-child {
flex: 0 0 38%;
}
}
&__map-toolbar {
display: flex;
gap: 10px;
margin-bottom: 12px;
.el-input {
flex: 1;
}
}
&__map {
width: 100%;
height: 460px;
border: 1px solid #dcdfe6;
}
&__map-info {
display: flex;
justify-content: space-between;
gap: 16px;
min-height: 22px;
margin-top: 10px;
color: #606266;
font-size: 13px;
}
}
</style>