|
|
@@ -18,10 +18,10 @@
|
|
|
<el-cascader
|
|
|
v-model="searchRegionValue"
|
|
|
:options="areaOptions"
|
|
|
- :props="{ checkStrictly: true, value: 'id', label: 'name' }"
|
|
|
+ :props="{ value: 'id', label: 'name' }"
|
|
|
placeholder="所属站点"
|
|
|
class="station-select"
|
|
|
- style="width: 350px"
|
|
|
+ style="width: 240px"
|
|
|
clearable
|
|
|
@change="handleSearchAreaChange"
|
|
|
/>
|
|
|
@@ -221,7 +221,8 @@
|
|
|
<el-input v-model="form.detailAddress" type="textarea" placeholder="输入详细地址" rows="3" style="width: 100%" />
|
|
|
</el-form-item>
|
|
|
<el-form-item>
|
|
|
- <el-button type="primary" style="width: 100%" @click="getGeolocation">获取经纬度</el-button>
|
|
|
+ <el-button type="primary" style="width: 100%" @click="getGeolocation" :loading="geoLoading">获取经纬度</el-button>
|
|
|
+ <div v-if="geoErrorMsg" class="geo-error-tip">{{ geoErrorMsg }}</div>
|
|
|
</el-form-item>
|
|
|
<el-row :gutter="10">
|
|
|
<el-col :span="12">
|
|
|
@@ -327,6 +328,7 @@ import { listOnStore } from '@/api/system/tenant';
|
|
|
import { listOnStore as listTenantCategoriesOnStore } from '@/api/system/tenantCategories';
|
|
|
import { listAllService } from '@/api/service/list';
|
|
|
import { listAreaStation } from '@/api/system/areaStation';
|
|
|
+import { getMapSetting } from '@/api/system/mapSetting';
|
|
|
import { AreaStationVO } from '@/api/system/areaStation/types';
|
|
|
import { regionData, codeToText, textToCode } from 'element-china-area-data';
|
|
|
import PageSelect from '@/components/PageSelect/index.vue';
|
|
|
@@ -343,27 +345,8 @@ const storeFormRef = ref<ElFormInstance>();
|
|
|
|
|
|
const searchRegionValue = ref<any[]>([]); // 搜索的区域值
|
|
|
const searchSiteOptions = ref<any[]>([]); // 搜索的站点选项
|
|
|
+const regionValue = ref<any[]>([]); // 所在区域/站点的路径值
|
|
|
|
|
|
-/** 处理搜索区域选择变化 */
|
|
|
-const handleSearchAreaChange = (value: any[]) => {
|
|
|
- if (value && value.length > 0) {
|
|
|
- const lastId = value[value.length - 1];
|
|
|
- const node = areaStationList.value.find(item => item.id === lastId);
|
|
|
- if (node && node.type === 2) {
|
|
|
- queryParams.value.station = lastId;
|
|
|
- queryParams.value.area = node.parentId;
|
|
|
- } else {
|
|
|
- queryParams.value.area = lastId;
|
|
|
- queryParams.value.station = undefined;
|
|
|
- }
|
|
|
- } else {
|
|
|
- queryParams.value.area = undefined;
|
|
|
- queryParams.value.station = undefined;
|
|
|
- }
|
|
|
- handleQuery();
|
|
|
-};
|
|
|
-
|
|
|
-const regionValue = ref<any[]>([]);
|
|
|
const province = ref('');
|
|
|
const city = ref('');
|
|
|
const district = ref('');
|
|
|
@@ -674,48 +657,71 @@ const handleUpdate = async (row: StoreVO) => {
|
|
|
dialog.title = "修改门店管理";
|
|
|
}
|
|
|
|
|
|
+const geoLoading = ref(false);
|
|
|
+const geoErrorMsg = ref('');
|
|
|
+
|
|
|
/** 提交按钮 */
|
|
|
const submitForm = () => {
|
|
|
storeFormRef.value?.validate(async (valid: boolean) => {
|
|
|
if (valid) {
|
|
|
buttonLoading.value = true;
|
|
|
- if (form.value.id) {
|
|
|
- await updateStore(form.value).finally(() => buttonLoading.value = false);
|
|
|
- } else {
|
|
|
- await addStore(form.value).finally(() => buttonLoading.value = false);
|
|
|
+ try {
|
|
|
+ if (form.value.id) {
|
|
|
+ await updateStore(form.value);
|
|
|
+ } else {
|
|
|
+ await addStore(form.value);
|
|
|
+ }
|
|
|
+ proxy?.$modal.msgSuccess("操作成功");
|
|
|
+ dialog.visible = false;
|
|
|
+ await getList();
|
|
|
+ } finally {
|
|
|
+ buttonLoading.value = false;
|
|
|
}
|
|
|
- proxy?.$modal.msgSuccess("操作成功");
|
|
|
- dialog.visible = false;
|
|
|
- await getList();
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
|
|
|
-/** 高德地图 Key 配置 */
|
|
|
-const amapKey = 'a30e76f457c14b6570925522be37565d';
|
|
|
-const securityJsCode = '531ae14ec1dff87e552e1ea51e848582';
|
|
|
-
|
|
|
/** 动态加载高德地图脚本 */
|
|
|
-const loadAMapScript = (): Promise<any> => {
|
|
|
- // 设置安全密钥
|
|
|
- (window as any)._AMapSecurityConfig = {
|
|
|
- securityJsCode: securityJsCode,
|
|
|
- };
|
|
|
- return new Promise((resolve, reject) => {
|
|
|
- if ((window as any).AMap) {
|
|
|
- resolve((window as any).AMap);
|
|
|
- return;
|
|
|
+const loadAMapScript = async (): Promise<any> => {
|
|
|
+ try {
|
|
|
+ // 从接口获取配置
|
|
|
+ const res = await getMapSetting(1);
|
|
|
+ if (res.code !== 200) {
|
|
|
+ return Promise.reject(res.msg);
|
|
|
}
|
|
|
- const script = document.createElement('script');
|
|
|
- script.src = `https://webapi.amap.com/maps?v=2.0&key=${amapKey}`;
|
|
|
- script.onload = () => resolve((window as any).AMap);
|
|
|
- script.onerror = reject;
|
|
|
- document.head.appendChild(script);
|
|
|
- });
|
|
|
+ const { apiKey, apiSecret } = res.data;
|
|
|
+
|
|
|
+ if (!apiKey) {
|
|
|
+ return Promise.reject('No Map Key Configured');
|
|
|
+ }
|
|
|
+
|
|
|
+ // 设置安全密钥
|
|
|
+ (window as any)._AMapSecurityConfig = {
|
|
|
+ securityJsCode: apiSecret,
|
|
|
+ };
|
|
|
+
|
|
|
+ return new Promise((resolve, reject) => {
|
|
|
+ if ((window as any).AMap) {
|
|
|
+ resolve((window as any).AMap);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ const script = document.createElement('script');
|
|
|
+ script.src = `https://webapi.amap.com/maps?v=2.0&key=${apiKey}`;
|
|
|
+ script.onload = () => resolve((window as any).AMap);
|
|
|
+ script.onerror = () => {
|
|
|
+ reject(new Error('Script load failed'));
|
|
|
+ };
|
|
|
+ document.head.appendChild(script);
|
|
|
+ });
|
|
|
+ } catch (error) {
|
|
|
+ console.error('Map config fetch error:', error);
|
|
|
+ // 此处不重复弹窗,因为接口请求失败通常已有全局拦截器处理显示错误
|
|
|
+ return Promise.reject(error);
|
|
|
+ }
|
|
|
};
|
|
|
|
|
|
/** 根据详细地址使用高德地图 Geocoder 获取经纬度 */
|
|
|
-const getGeolocation = () => {
|
|
|
+const getGeolocation = async () => {
|
|
|
// 拼接完整地址(省市区 + 详细地址)
|
|
|
let areaText = '';
|
|
|
if (addressCascaderValue.value && addressCascaderValue.value.length > 0) {
|
|
|
@@ -729,38 +735,45 @@ const getGeolocation = () => {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- // 确保高德地图脚本已加载
|
|
|
- const doGeocode = () => {
|
|
|
+ geoLoading.value = true;
|
|
|
+ geoErrorMsg.value = '';
|
|
|
+ try {
|
|
|
+ // 确保高德地图脚本已加载
|
|
|
+ await loadAMapScript();
|
|
|
+
|
|
|
const AMap = (window as any).AMap;
|
|
|
if (!AMap) {
|
|
|
- proxy?.$modal.msgError('高德地图脚本未加载,请稍后重试');
|
|
|
- return;
|
|
|
+ throw new Error('AMap is not defined');
|
|
|
}
|
|
|
- AMap.plugin('AMap.Geocoder', () => {
|
|
|
- const geocoder = new AMap.Geocoder();
|
|
|
- geocoder.getLocation(fullAddress, (status: string, result: any) => {
|
|
|
- if (status === 'complete' && result.info === 'OK') {
|
|
|
- const location = result.geocodes[0]?.location;
|
|
|
- if (location) {
|
|
|
- form.value.longitude = location.lng.toFixed(6);
|
|
|
- form.value.latitude = location.lat.toFixed(6);
|
|
|
- proxy?.$modal.msgSuccess('获取经纬度成功');
|
|
|
+
|
|
|
+ const location: any = await new Promise((resolve, reject) => {
|
|
|
+ AMap.plugin('AMap.Geocoder', () => {
|
|
|
+ const geocoder = new AMap.Geocoder();
|
|
|
+ geocoder.getLocation(fullAddress, (status: string, result: any) => {
|
|
|
+ if (status === 'complete' && result.info === 'OK') {
|
|
|
+ resolve(result.geocodes[0]?.location);
|
|
|
} else {
|
|
|
- proxy?.$modal.msgError('未能解析到该地址的坐标,请检查地址是否准确');
|
|
|
+ console.error('Geocoder fail:', status, result);
|
|
|
+ reject(new Error(result.info || status || 'fail'));
|
|
|
}
|
|
|
- } else {
|
|
|
- proxy?.$modal.msgError('地理编码失败:' + (result.info || status));
|
|
|
- }
|
|
|
+ });
|
|
|
});
|
|
|
+ // 增加 8s 超时
|
|
|
+ setTimeout(() => reject(new Error('timeout')), 8000);
|
|
|
});
|
|
|
- };
|
|
|
|
|
|
- if ((window as any).AMap) {
|
|
|
- doGeocode();
|
|
|
- } else {
|
|
|
- loadAMapScript().then(() => doGeocode()).catch(() => {
|
|
|
- proxy?.$modal.msgError('高德地图加载失败,请检查网络');
|
|
|
- });
|
|
|
+ if (location) {
|
|
|
+ form.value.longitude = location.lng.toFixed(6);
|
|
|
+ form.value.latitude = location.lat.toFixed(6);
|
|
|
+ proxy?.$modal.msgSuccess('获取经纬度成功');
|
|
|
+ } else {
|
|
|
+ throw new Error('no location');
|
|
|
+ }
|
|
|
+ } catch (err) {
|
|
|
+ console.error('getGeolocation error:', err);
|
|
|
+ geoErrorMsg.value = '经纬度获取失败,请联系管理员处理';
|
|
|
+ } finally {
|
|
|
+ geoLoading.value = false;
|
|
|
}
|
|
|
};
|
|
|
|
|
|
@@ -806,6 +819,8 @@ const buildTree = (data: any[], parentId: any): any[] => {
|
|
|
const res: any = {
|
|
|
id: item.id,
|
|
|
name: item.name,
|
|
|
+ // 如果不是站点且没有下级了,则不可选(防止误选区域叶子点)
|
|
|
+ disabled: Number(item.type) !== 2 && (!children || children.length === 0)
|
|
|
};
|
|
|
if (children && children.length > 0) {
|
|
|
res.children = children;
|
|
|
@@ -814,7 +829,23 @@ const buildTree = (data: any[], parentId: any): any[] => {
|
|
|
});
|
|
|
};
|
|
|
|
|
|
-/** 处理所在区域选择变化 */
|
|
|
+/** 处理搜索区域选择变化 */
|
|
|
+const handleSearchAreaChange = (value: any[]) => {
|
|
|
+ if (value && value.length > 0) {
|
|
|
+ const lastId = value[value.length - 1];
|
|
|
+ queryParams.value.station = lastId;
|
|
|
+ const node = areaStationList.value.find(item => item.id === lastId);
|
|
|
+ if (node) {
|
|
|
+ queryParams.value.area = node.parentId;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ queryParams.value.station = undefined;
|
|
|
+ queryParams.value.area = undefined;
|
|
|
+ }
|
|
|
+ handleQuery();
|
|
|
+};
|
|
|
+
|
|
|
+/** 处理对话框中的所在区域选择变化 */
|
|
|
const handleAreaChange = (value: any[]) => {
|
|
|
if (value && value.length > 0) {
|
|
|
const lastId = value[value.length - 1];
|
|
|
@@ -1049,6 +1080,14 @@ onMounted(() => {
|
|
|
gap: 12px;
|
|
|
}
|
|
|
|
|
|
+.geo-error-tip {
|
|
|
+ color: #f56c6c;
|
|
|
+ font-size: 13px;
|
|
|
+ margin-top: 8px;
|
|
|
+ width: 100%;
|
|
|
+ text-align: center;
|
|
|
+}
|
|
|
+
|
|
|
.delete-item {
|
|
|
color: #f56c6c !important;
|
|
|
&:hover {
|