|
|
@@ -6,11 +6,9 @@
|
|
|
<div class="search-actions">
|
|
|
<el-input v-model="queryParams.storeOrContact" placeholder="搜索门店名称/联系人" prefix-icon="Search"
|
|
|
class="search-input" clearable @keyup.enter="handleQuery" @clear="handleQuery" />
|
|
|
- <el-cascader v-model="searchRegionValue" :options="areaOptions" placeholder="所属城市"
|
|
|
- class="filter-cascader" clearable @change="handleSearchAreaChange" />
|
|
|
- <el-select v-model="queryParams.station" placeholder="所属站点" class="filter-select" clearable @change="handleQuery">
|
|
|
- <el-option v-for="site in searchSiteOptions" :key="site.value" :label="site.label" :value="site.value" />
|
|
|
- </el-select>
|
|
|
+ <el-cascader v-model="searchRegionValue" :options="areaOptions"
|
|
|
+ :props="{ checkStrictly: true, value: 'id', label: 'name' }"
|
|
|
+ placeholder="所属站点" class="filter-cascader" style="width: 350px" clearable @change="handleSearchAreaChange" />
|
|
|
<el-select v-model="queryParams.status" placeholder="状态" class="filter-select-mini" clearable @change="handleQuery">
|
|
|
<el-option v-for="item in statusList" :key="item.value" :label="item.label" :value="item.value" />
|
|
|
</el-select>
|
|
|
@@ -66,8 +64,7 @@
|
|
|
</template>
|
|
|
</el-table-column>
|
|
|
|
|
|
- <!-- 归属区域/站点 -->
|
|
|
- <el-table-column label="归属区域/站点" align="left" width="180">
|
|
|
+ <el-table-column label="所属站点" align="left" width="180">
|
|
|
<template #default="scope">
|
|
|
<div class="location-info">
|
|
|
<div class="area-name">{{ getRegionNameBySite(scope.row.site) }}</div>
|
|
|
@@ -200,21 +197,12 @@
|
|
|
style="width: 100%">
|
|
|
</el-date-picker>
|
|
|
</el-form-item>
|
|
|
- <el-row :gutter="10">
|
|
|
- <el-col :span="12">
|
|
|
- <el-form-item label="所在区域" prop="regionId">
|
|
|
- <el-cascader v-model="regionValue" :options="areaOptions" placeholder="选择区域" style="width: 100%"
|
|
|
- @change="handleAreaChange" />
|
|
|
- </el-form-item>
|
|
|
- </el-col>
|
|
|
- <el-col :span="12">
|
|
|
- <el-form-item label="归属站点" prop="site">
|
|
|
- <el-select v-model="form.site" placeholder="选择站点" :disabled="!form.regionId">
|
|
|
- <el-option v-for="site in siteOptions" :key="site.value" :label="site.label" :value="site.value" />
|
|
|
- </el-select>
|
|
|
- </el-form-item>
|
|
|
- </el-col>
|
|
|
- </el-row>
|
|
|
+ <el-form-item label="所属站点" prop="site">
|
|
|
+ <el-cascader v-model="regionValue" :options="areaOptions"
|
|
|
+ :props="{ value: 'id', label: 'name' }"
|
|
|
+ placeholder="请选择所属站点" style="width: 100%"
|
|
|
+ @change="handleAreaChange" />
|
|
|
+ </el-form-item>
|
|
|
<el-form-item label="详细地址">
|
|
|
<el-row :gutter="10" style="margin-bottom: 10px">
|
|
|
<el-col :span="24">
|
|
|
@@ -260,8 +248,7 @@
|
|
|
<el-descriptions-item label="有效期至">{{ parseTime(detailData.validity, '{y}-{m}-{d}') }}</el-descriptions-item>
|
|
|
<el-descriptions-item label="联系人">{{ detailData.contact }}</el-descriptions-item>
|
|
|
<el-descriptions-item label="联系电话">{{ detailData.contactNumber }}</el-descriptions-item>
|
|
|
- <el-descriptions-item label="所在区域">{{ getRegionNameBySite(detailData.site) }}</el-descriptions-item>
|
|
|
- <el-descriptions-item label="归属站点">{{ detailData.siteName }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="所属站点">{{ detailData.siteName }}</el-descriptions-item>
|
|
|
<el-descriptions-item label="详细地址">{{ detailData.detailAddress }}</el-descriptions-item>
|
|
|
<el-descriptions-item label="营业执照">
|
|
|
<image-preview v-if="detailData.businessLicenseUrl" :src="detailData.businessLicenseUrl" :width="80" :height="60" />
|
|
|
@@ -334,8 +321,8 @@ import { listOnStore as listTenantCategoriesOnStore } from '@/api/system/tenantC
|
|
|
import { listAllService as listServiceOnStore } from '@/api/service/list';
|
|
|
import { listSubOrderOnStore } from '@/api/order/subOrder';
|
|
|
import { SubOrderStoreVO } from '@/api/order/subOrder/types';
|
|
|
-import { listOnStore as listAreaStationOnStore } from '@/api/system/areaStation';
|
|
|
-import { SysAreaStationOnStoreVo } from '@/api/system/areaStation/types';
|
|
|
+import { listAreaStation } from '@/api/system/areaStation';
|
|
|
+import { AreaStationVO } from '@/api/system/areaStation/types';
|
|
|
import { regionData, codeToText, textToCode } from 'element-china-area-data';
|
|
|
import PageSelect from '@/components/PageSelect/index.vue';
|
|
|
import { useUserStore } from '@/store/modules/user';
|
|
|
@@ -362,21 +349,19 @@ const searchSiteOptions = ref<any[]>([]); // 搜索的站点选项
|
|
|
|
|
|
/** 处理搜索区域选择变化 */
|
|
|
const handleSearchAreaChange = (value: any[]) => {
|
|
|
- // 清空级联站点
|
|
|
- queryParams.value.station = undefined;
|
|
|
-
|
|
|
if (value && value.length > 0) {
|
|
|
- const areaId = value[value.length - 1];
|
|
|
- queryParams.value.area = areaId;
|
|
|
- searchSiteOptions.value = areaStationList.value
|
|
|
- .filter((item: any) => String(item.type) === '2' && String(item.parentId) === String(areaId))
|
|
|
- .map((item: any) => ({
|
|
|
- value: String(item.id),
|
|
|
- label: item.name
|
|
|
- }));
|
|
|
+ const lastId = value[value.length - 1];
|
|
|
+ const node = areaStationList.value.find(item => String(item.id) === String(lastId));
|
|
|
+ if (node && String(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;
|
|
|
- searchSiteOptions.value = [];
|
|
|
+ queryParams.value.station = undefined;
|
|
|
}
|
|
|
handleQuery();
|
|
|
};
|
|
|
@@ -397,7 +382,7 @@ const serviceList = ref<any[]>([]); // 服务项目列表
|
|
|
const statusList = ref<StoreStatusVO[]>([]); // 状态列表
|
|
|
const tenantCategoriesList = ref<any[]>([]); // 商户分类列表
|
|
|
const tenantCategoriesTotal = ref(0); // 商户分类总数
|
|
|
-const areaStationList = ref<SysAreaStationOnStoreVo[]>([]); // 区域站点列表
|
|
|
+const areaStationList = ref<AreaStationVO[]>([]); // 区域站点列表
|
|
|
const areaOptions = ref<any[]>([]); // 所在区域树形选项
|
|
|
const siteOptions = ref<any[]>([]); // 归属站点选项
|
|
|
|
|
|
@@ -656,8 +641,9 @@ const handleSelectionChange = (selection: StoreVO[]) => {
|
|
|
|
|
|
/** 新增按钮操作 */
|
|
|
const handleAdd = () => {
|
|
|
- if (!areaOptions.value || areaOptions.value.length === 0) {
|
|
|
- proxy?.$modal.msgWarning("请先配置区域站点");
|
|
|
+ const hasStation = areaStationList.value.some((item: any) => String(item.type) === '2');
|
|
|
+ if (!hasStation) {
|
|
|
+ proxy?.$modal.msgWarning("请先在区域站点中配置类型为“站点”的数据");
|
|
|
return;
|
|
|
}
|
|
|
reset();
|
|
|
@@ -690,30 +676,23 @@ const handleUpdate = async (row?: StoreVO) => {
|
|
|
|
|
|
if (res.data.site) {
|
|
|
form.value.site = String(res.data.site);
|
|
|
+ const path: any[] = [];
|
|
|
+ let currentId = res.data.site;
|
|
|
+ while (currentId && String(currentId) !== '0') {
|
|
|
+ path.unshift(String(currentId));
|
|
|
+ const node = areaStationList.value.find((item: any) => String(item.id) === String(currentId));
|
|
|
+ if (node) {
|
|
|
+ currentId = node.parentId;
|
|
|
+ } else {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ regionValue.value = path;
|
|
|
+
|
|
|
+ // 设置 regionId
|
|
|
const siteData = areaStationList.value.find((item: any) => String(item.id) === String(res.data.site));
|
|
|
if (siteData) {
|
|
|
- const regionId = siteData.parentId;
|
|
|
- form.value.regionId = String(regionId);
|
|
|
-
|
|
|
- const path: any[] = [];
|
|
|
- let currentId = regionId;
|
|
|
- while (currentId && String(currentId) !== '0') {
|
|
|
- path.unshift(String(currentId));
|
|
|
- const currentArea = areaStationList.value.find((item: any) => String(item.id) === String(currentId));
|
|
|
- if (currentArea) {
|
|
|
- currentId = currentArea.parentId;
|
|
|
- } else {
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
- regionValue.value = path;
|
|
|
-
|
|
|
- siteOptions.value = areaStationList.value
|
|
|
- .filter((item: any) => String(item.type) === '2' && String(item.parentId) === String(regionId))
|
|
|
- .map((item: any) => ({
|
|
|
- value: String(item.id),
|
|
|
- label: item.name
|
|
|
- }));
|
|
|
+ form.value.regionId = String(siteData.parentId);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -754,36 +733,76 @@ const handleExport = () => {
|
|
|
}, `store_${new Date().getTime()}.xlsx`)
|
|
|
}
|
|
|
|
|
|
-/** 获取经纬度 */
|
|
|
+/** 高德地图 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 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);
|
|
|
+ });
|
|
|
+};
|
|
|
+
|
|
|
+/** 根据详细地址使用高德地图 Geocoder 获取经纬度 */
|
|
|
const getGeolocation = () => {
|
|
|
- if ('geolocation' in navigator) {
|
|
|
- navigator.geolocation.getCurrentPosition(
|
|
|
- (position) => {
|
|
|
- form.value.longitude = position.coords.longitude.toFixed(6);
|
|
|
- form.value.latitude = position.coords.latitude.toFixed(6);
|
|
|
- proxy?.$modal.msgSuccess('获取经纬度成功');
|
|
|
- },
|
|
|
- (error) => {
|
|
|
- let errorMessage = '获取位置失败';
|
|
|
- switch (error.code) {
|
|
|
- case error.PERMISSION_DENIED:
|
|
|
- errorMessage = '用户拒绝了地理定位请求';
|
|
|
- break;
|
|
|
- case error.POSITION_UNAVAILABLE:
|
|
|
- errorMessage = '位置信息不可用';
|
|
|
- break;
|
|
|
- case error.TIMEOUT:
|
|
|
- errorMessage = '获取位置超时';
|
|
|
- break;
|
|
|
- case error.UNKNOWN_ERROR:
|
|
|
- errorMessage = '未知错误';
|
|
|
- break;
|
|
|
+ // 拼接完整地址(省市区 + 详细地址)
|
|
|
+ let areaText = '';
|
|
|
+ if (addressCascaderValue.value && addressCascaderValue.value.length > 0) {
|
|
|
+ areaText = addressCascaderValue.value.map((code: string) => codeToText[code] || '').join('');
|
|
|
+ }
|
|
|
+ const detailAddr = form.value.detailAddress || '';
|
|
|
+ const fullAddress = (areaText + detailAddr).trim();
|
|
|
+
|
|
|
+ if (!fullAddress) {
|
|
|
+ proxy?.$modal.msgWarning('请先填写省市区和详细地址');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 确保高德地图脚本已加载
|
|
|
+ const doGeocode = () => {
|
|
|
+ const AMap = (window as any).AMap;
|
|
|
+ if (!AMap) {
|
|
|
+ proxy?.$modal.msgError('高德地图脚本未加载,请稍后重试');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ 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('获取经纬度成功');
|
|
|
+ } else {
|
|
|
+ proxy?.$modal.msgError('未能解析到该地址的坐标,请检查地址是否准确');
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ proxy?.$modal.msgError('地理编码失败:' + (result.info || status));
|
|
|
}
|
|
|
- proxy?.$modal.msgError(errorMessage);
|
|
|
- }
|
|
|
- );
|
|
|
+ });
|
|
|
+ });
|
|
|
+ };
|
|
|
+
|
|
|
+ if ((window as any).AMap) {
|
|
|
+ doGeocode();
|
|
|
} else {
|
|
|
- proxy?.$modal.msgError('您的浏览器不支持地理定位');
|
|
|
+ loadAMapScript().then(() => doGeocode()).catch(() => {
|
|
|
+ proxy?.$modal.msgError('高德地图加载失败,请检查网络');
|
|
|
+ });
|
|
|
}
|
|
|
};
|
|
|
|
|
|
@@ -821,14 +840,12 @@ const getServiceList = async () => {
|
|
|
/** 获取区域站点列表 */
|
|
|
const getAreaStationList = async () => {
|
|
|
try {
|
|
|
- const res = await listAreaStationOnStore();
|
|
|
+ const res = await listAreaStation();
|
|
|
const data = res.data || res;
|
|
|
areaStationList.value = data;
|
|
|
|
|
|
- // 分离所在区域数据(type为0或1)
|
|
|
- const areaData = data.filter((item: any) => String(item.type) === '0' || String(item.type) === '1');
|
|
|
- // 构建树形结构
|
|
|
- areaOptions.value = buildTree(areaData, 0);
|
|
|
+ // 构建包含所有层级(区域和站点)的树
|
|
|
+ areaOptions.value = buildSearchTree(data, 0);
|
|
|
|
|
|
// 初始化站点数据为空
|
|
|
siteOptions.value = [];
|
|
|
@@ -837,6 +854,23 @@ const getAreaStationList = async () => {
|
|
|
}
|
|
|
};
|
|
|
|
|
|
+/** 构建搜索树形结构(包含站点) */
|
|
|
+const buildSearchTree = (data: any[], parentId: any): any[] => {
|
|
|
+ return data
|
|
|
+ .filter(item => String(item.parentId) === String(parentId))
|
|
|
+ .map(item => {
|
|
|
+ const children = buildSearchTree(data, item.id);
|
|
|
+ const res: any = {
|
|
|
+ id: item.id,
|
|
|
+ name: item.name,
|
|
|
+ };
|
|
|
+ if (children && children.length > 0) {
|
|
|
+ res.children = children;
|
|
|
+ }
|
|
|
+ return res;
|
|
|
+ });
|
|
|
+};
|
|
|
+
|
|
|
/** 构建树形结构 */
|
|
|
const buildTree = (data: any[], parentId: any): any[] => {
|
|
|
return data
|
|
|
@@ -853,25 +887,18 @@ const buildTree = (data: any[], parentId: any): any[] => {
|
|
|
|
|
|
/** 处理所在区域选择变化 */
|
|
|
const handleAreaChange = (value: any[]) => {
|
|
|
- // 清空归属站点选择
|
|
|
- form.value.site = undefined;
|
|
|
-
|
|
|
if (value && value.length > 0) {
|
|
|
- // 获取最后一级的id
|
|
|
- const areaId = value[value.length - 1];
|
|
|
- // 更新regionId
|
|
|
- form.value.regionId = String(areaId);
|
|
|
- // 过滤出parentId等于areaId的站点
|
|
|
- siteOptions.value = areaStationList.value
|
|
|
- .filter((item: any) => String(item.type) === '2' && String(item.parentId) === String(areaId))
|
|
|
- .map((item: any) => ({
|
|
|
- value: String(item.id),
|
|
|
- label: item.name
|
|
|
- }));
|
|
|
+ const lastId = value[value.length - 1];
|
|
|
+ form.value.site = lastId;
|
|
|
+
|
|
|
+ // 找到当前选择节点的父节点作为 regionId (如果需要)
|
|
|
+ const node = areaStationList.value.find(item => String(item.id) === String(lastId));
|
|
|
+ if (node) {
|
|
|
+ form.value.regionId = String(node.parentId);
|
|
|
+ }
|
|
|
} else {
|
|
|
- // 如果没有选择区域,清空站点选项和regionId
|
|
|
+ form.value.site = undefined;
|
|
|
form.value.regionId = undefined;
|
|
|
- siteOptions.value = [];
|
|
|
}
|
|
|
};
|
|
|
|
|
|
@@ -1004,6 +1031,10 @@ onMounted(() => {
|
|
|
getServiceList();
|
|
|
getAreaStationList();
|
|
|
getStatusList();
|
|
|
+ // 提前加载高德地图脚本,加快首次地理编码速度
|
|
|
+ loadAMapScript().catch(() => {
|
|
|
+ console.warn('高德地图预加载失败,将在首次使用时重试');
|
|
|
+ });
|
|
|
});
|
|
|
</script>
|
|
|
|
|
|
@@ -1041,7 +1072,10 @@ onMounted(() => {
|
|
|
width: 240px;
|
|
|
}
|
|
|
|
|
|
-.filter-cascader, .filter-select {
|
|
|
+.filter-cascader {
|
|
|
+ width: 420px;
|
|
|
+}
|
|
|
+.filter-select {
|
|
|
width: 150px;
|
|
|
}
|
|
|
|