diff --git a/src/js/pages/basic-info.page.js b/src/js/pages/basic-info.page.js index e78288f..0d462d7 100644 --- a/src/js/pages/basic-info.page.js +++ b/src/js/pages/basic-info.page.js @@ -6,10 +6,12 @@ import { CityPicker, Modal } from '../ui/index.js'; import { Validator, Formatter } from '../utils/index.js'; import { DraftManager, FormIdGenerator, UserCache } from '../core/index.js'; -import { ASSET_CONFIG, BASIC_INFO_CONFIG, PROVINCE_CITY_DATA, CACHE_CONFIG } from '../config/index.js'; +import { ASSET_CONFIG, BASIC_INFO_CONFIG, PROVINCE_CITY_DATA, CACHE_CONFIG, API_CONFIG } from '../config/index.js'; import { showToast } from '../ui/toast.js'; import { AreaService } from '../services/area.service.js'; +import { getLocationService } from '../services/location.service.js'; import { AuthFlowService, AUTH_STATUS } from '../services/index.js'; +import { ApiClient } from '../core/api.js'; export class BasicInfoPage { constructor() { @@ -71,6 +73,9 @@ export class BasicInfoPage { this.renderForm(); this.bindEvents(); this.updateProgress(); + + // IP定位自动填充城市 + this.autoFillCityByLocation(); } /** @@ -687,29 +692,27 @@ export class BasicInfoPage { // 如果没有 h5Urls,显示成功提示 if (!h5Urls || h5Urls.length === 0) { + // 有 redirectUrl 跳转到指定地址,否则返回首页 + const finalUrl = redirectUrl || window.location.href.split('?')[0]; + const btnText = redirectUrl ? '立即跳转' : '返回首页'; + authContainer.innerHTML = `
信息提交成功
您的申请已提交成功!
- ${redirectUrl ? ` -
- 5 秒后自动跳转... -
- - ` : ` - - `} +
+ 5 秒后自动跳转... +
+
`; const mainContainer = document.querySelector('.container') || document.body; mainContainer.insertBefore(authContainer, mainContainer.firstChild); - // 如果有 redirectUrl,启动倒计时 - if (redirectUrl) { - this.startFinalCountdown(redirectUrl); - } + // 启动 5 秒倒计时 + this.startFinalCountdown(finalUrl); return; } @@ -919,6 +922,82 @@ export class BasicInfoPage { } } + /** + * 通过IP定位自动填充城市 + */ + async autoFillCityByLocation() { + try { + // 如果已经有城市值,跳过 + if (this.basicInfoValues.city) { + console.log('[BasicInfoPage] 城市已存在,跳过IP定位填充'); + return; + } + + console.log('[BasicInfoPage] 开始IP定位...'); + const locationService = getLocationService(); + const location = await locationService.getLocation(); + + if (location) { + const { province, city } = location; + console.log('[BasicInfoPage] IP定位成功:', province, city); + + // 查找城市代码 + const cityCode = await this.findCityCode(province, city); + + if (cityCode) { + // 自动填充城市 + this.handleCityConfirm({ + value: city, + province: province, + city: city, + provinceCode: cityCode.provinceCode, + cityCode: cityCode.cityCode + }); + + console.log('[BasicInfoPage] 城市自动填充成功:', city); + } else { + console.warn('[BasicInfoPage] 未找到城市代码:', province, city); + } + } else { + console.warn('[BasicInfoPage] IP定位失败: 未获取到位置信息'); + } + } catch (error) { + console.error('[BasicInfoPage] IP定位异常:', error); + // 静默失败,不影响用户正常使用 + } + } + + /** + * 查找城市代码 + */ + async findCityCode(provinceName, cityName) { + try { + // 获取省份数据 + const provinces = await AreaService.getProvinces(); + const province = provinces.find(p => p.name === provinceName); + + if (!province) { + return null; + } + + // 获取城市数据 + const cities = await AreaService.getCities(province.code); + const city = cities.find(c => c.name === cityName && c.code.length === 4); + + if (city) { + return { + provinceCode: province.code, + cityCode: city.code + }; + } + + return null; + } catch (error) { + console.error('[BasicInfoPage] 查找城市代码失败:', error); + return null; + } + } + /** * 销毁页面 */ diff --git a/src/js/services/area.service.js b/src/js/services/area.service.js index 16c9557..9fdbec3 100644 --- a/src/js/services/area.service.js +++ b/src/js/services/area.service.js @@ -13,23 +13,50 @@ const CACHE_DURATION = 10 * 60 * 1000; // 10分钟 * 区域服务 */ export class AreaService { + // 正在进行的请求缓存 + static pendingRequests = new Map(); + /** * 获取区域列表 * @param {string} provincecode - 省份代码(可选) * @returns {Promise} 区域列表 [{code, name}] */ static async getAreaList(provincecode) { - // 检查缓存 const cacheKey = `${CACHE_KEY_PREFIX}${provincecode || 'all'}`; - const cached = localStorage.getItem(cacheKey); + // 检查 localStorage 缓存 + const cached = localStorage.getItem(cacheKey); if (cached) { const { data, timestamp } = JSON.parse(cached); if (Date.now() - timestamp < CACHE_DURATION) { + console.log(`[AreaService] 使用缓存 ${provincecode || 'all'}`); return data; } } + // 检查是否有正在进行的请求 + if (this.pendingRequests.has(cacheKey)) { + console.log(`[AreaService] 等待正在进行的请求 ${provincecode || 'all'}`); + return this.pendingRequests.get(cacheKey); + } + + // 创建新请求 + const requestPromise = this.doGetAreaList(provincecode, cacheKey); + this.pendingRequests.set(cacheKey, requestPromise); + + try { + return await requestPromise; + } finally { + // 请求完成后清除缓存 + this.pendingRequests.delete(cacheKey); + } + } + + /** + * 执行实际的区域列表请求 + * @private + */ + static async doGetAreaList(provincecode, cacheKey) { try { // 构建请求参数 const params = {}; @@ -37,6 +64,8 @@ export class AreaService { params.provincecode = provincecode; } + console.log(`[AreaService] 请求区域数据 ${provincecode || 'all'}`); + // 使用 ApiClient 发送请求 const result = await ApiClient.get(API_CONFIG.ENDPOINTS.AREA_LIST, params); @@ -46,7 +75,7 @@ export class AreaService { const areaList = result.result || []; - // 保存到缓存 + // 保存到 localStorage 缓存 localStorage.setItem(cacheKey, JSON.stringify({ data: areaList, timestamp: Date.now() diff --git a/src/js/services/location.service.js b/src/js/services/location.service.js new file mode 100644 index 0000000..e44cef3 --- /dev/null +++ b/src/js/services/location.service.js @@ -0,0 +1,124 @@ +/** + * 定位服务 + * 提供全局 IP 定位功能,避免重复请求 + */ + +import { API_CONFIG } from '../config/api.config.js'; +import { ApiClient } from '../core/api.js'; + +/** + * 定位服务(单例模式) + */ +export class LocationService { + static instance = null; + + // 定位缓存 + locationCache = null; + + // 正在定位的 Promise + locatingPromise = null; + + // 缓存有效期(10分钟) + CACHE_DURATION = 10 * 60 * 1000; + + /** + * 获取单例实例 + */ + static getInstance() { + if (!this.instance) { + this.instance = new LocationService(); + } + return this.instance; + } + + /** + * 获取当前定位(带缓存) + * @returns {Promise} 定位结果 {province, city, code} + */ + async getLocation() { + // 如果有缓存且未过期,直接返回 + if (this.locationCache) { + const { data, timestamp } = this.locationCache; + if (Date.now() - timestamp < this.CACHE_DURATION) { + console.log('[LocationService] 使用缓存定位:', data); + return data; + } + } + + // 如果正在定位,返回同一个 Promise + if (this.locatingPromise) { + console.log('[LocationService] 定位进行中,等待结果...'); + return this.locatingPromise; + } + + // 开始定位 + this.locatingPromise = this.doLocation(); + + try { + const result = await this.locatingPromise; + + // 缓存结果 + this.locationCache = { + data: result, + timestamp: Date.now() + }; + + return result; + } finally { + // 清除定位 Promise + this.locatingPromise = null; + } + } + + /** + * 执行 IP 定位 + * @private + */ + async doLocation() { + try { + console.log('[LocationService] 开始 IP 定位...'); + const result = await ApiClient.get(API_CONFIG.ENDPOINTS.IP_LOCATION); + + if (result.retcode === 0 && result.result) { + const { province, city } = result.result; + + if (province && city) { + console.log('[LocationService] IP 定位成功:', province, city); + return { + province, + city, + code: null // 稍后可以添加城市代码 + }; + } + } + + throw new Error('定位失败:未获取到省市信息'); + } catch (error) { + console.error('[LocationService] IP 定位失败:', error); + throw error; + } + } + + /** + * 清除缓存 + */ + clearCache() { + this.locationCache = null; + console.log('[LocationService] 缓存已清除'); + } + + /** + * 设置定位结果(手动设置) + * @param {Object} location 定位结果 {province, city, code} + */ + setLocation(location) { + this.locationCache = { + data: location, + timestamp: Date.now() + }; + console.log('[LocationService] 手动设置定位:', location); + } +} + +// 导出单例获取函数 +export const getLocationService = () => LocationService.getInstance(); diff --git a/src/js/ui/city-picker.js b/src/js/ui/city-picker.js index b6af9d9..865897c 100644 --- a/src/js/ui/city-picker.js +++ b/src/js/ui/city-picker.js @@ -3,23 +3,25 @@ * 提供省份和城市的联动选择功能,支持热门城市快速选择和定位 */ -import { AreaService } from '../services/area.service.js'; -import { API_CONFIG } from '../config/api.config.js'; +import {AreaService} from '../services/area.service.js'; +import {getLocationService} from '../services/location.service.js'; +import {API_CONFIG} from '../config/api.config.js'; +import {ApiClient} from '../core/api.js'; // 热门城市列表(12个核心城市) const HOT_CITIES = [ - { name: '北京市', province: '北京市', code: '1100' }, - { name: '上海市', province: '上海市', code: '3100' }, - { name: '广州市', province: '广东省', code: '4401' }, - { name: '深圳市', province: '广东省', code: '4403' }, - { name: '成都市', province: '四川省', code: '5101' }, - { name: '杭州市', province: '浙江省', code: '3301' }, - { name: '重庆市', province: '重庆市', code: '5000' }, - { name: '武汉市', province: '湖北省', code: '4201' }, - { name: '西安市', province: '陕西省', code: '6101' }, - { name: '南京市', province: '江苏省', code: '3201' }, - { name: '苏州市', province: '江苏省', code: '3205' }, - { name: '天津市', province: '天津市', code: '1200' } + {name: '北京市', province: '北京市', code: '1100'}, + {name: '上海市', province: '上海市', code: '3100'}, + {name: '广州市', province: '广东省', code: '4401'}, + {name: '深圳市', province: '广东省', code: '4403'}, + {name: '成都市', province: '四川省', code: '5101'}, + {name: '杭州市', province: '浙江省', code: '3301'}, + {name: '重庆市', province: '重庆市', code: '5000'}, + {name: '武汉市', province: '湖北省', code: '4201'}, + {name: '西安市', province: '陕西省', code: '6101'}, + {name: '南京市', province: '江苏省', code: '3201'}, + {name: '苏州市', province: '江苏省', code: '3205'}, + {name: '天津市', province: '天津市', code: '1200'} ]; // 直辖市列表(省和市同名) @@ -194,7 +196,7 @@ export class CityPicker { */ async selectHotCity(city) { const isMunicipality = MUNICIPALITIES.includes(city.province); - + this.tempSelectedProvince = city.province; this.tempSelectedCity = city.name; this.tempSelectedProvinceCode = city.provinceCode || city.code.substring(0, 2) + '00'; @@ -216,7 +218,7 @@ export class CityPicker { if (province) { this.tempSelectedProvince = province.name; this.tempSelectedProvinceCode = province.code; - + // 更新省份列表选中状态 const provinceItems = this.provinceColumn.querySelectorAll('.province-item'); provinceItems.forEach(item => { @@ -225,7 +227,7 @@ export class CityPicker { // 加载并渲染城市列表 await this.loadCities(province.code); - + // 选中对应的城市 const cityItems = this.cityColumn.querySelectorAll('.city-item'); if (isMunicipality) { @@ -240,7 +242,7 @@ export class CityPicker { }); } } - + // 更新确认按钮状态 this.updateConfirmButtonState(); } @@ -344,8 +346,8 @@ export class CityPicker { }]; } else { // 普通省份:显示市级数据(4位代码),排除"市辖区" - cities = this.cities.filter(area => - area.code.length === 4 && + cities = this.cities.filter(area => + area.code.length === 4 && area.name !== '市辖区' ); } @@ -383,7 +385,7 @@ export class CityPicker { this.selectCity(existingCity.name, existingCity.code); } } - + // 对于直辖市,自动选中唯一的选项 if (isMunicipality && cities.length === 1 && !this.tempSelectedCityCode) { this.selectCity(cities[0].name, cities[0].code); @@ -660,39 +662,35 @@ export class CityPicker { } try { - // 调用后端IP定位接口 - const response = await fetch(API_CONFIG.ENDPOINTS.IP_LOCATION); - const result = await response.json(); + // 使用全局定位服务 + const locationService = getLocationService(); + const location = await locationService.getLocation(); - if (result.retcode === 0 && result.result) { - const { province, city } = result.result; + if (location) { + const {province, city} = location; + console.log('[CityPicker] IP定位成功:', province, city); - if (province && city) { - console.log('[CityPicker] IP定位成功:', province, city); + // 查找城市代码 + const cityCode = await this.findCityCode(province, city); - // 查找城市代码 - const cityCode = await this.findCityCode(province, city); - - if (cityCode) { - this.currentLocationCity = { - name: city, - province: province, - code: cityCode.cityCode, - provinceCode: cityCode.provinceCode - }; - console.log('[CityPicker] 定位成功:', this.currentLocationCity.name); - } else { - // 如果找不到代码,返回基本信息(代码为空) - this.currentLocationCity = { - name: city, - province: province, - code: '', - provinceCode: '' - }; - } + if (cityCode) { + this.currentLocationCity = { + name: city, + province: province, + code: cityCode.cityCode, + provinceCode: cityCode.provinceCode + }; + console.log('[CityPicker] 定位成功:', this.currentLocationCity.name); + } else { + // 如果找不到代码,返回基本信息(代码为空) + this.currentLocationCity = { + name: city, + province: province, + code: '', + provinceCode: '' + }; } } - } catch (error) { console.error('[CityPicker] IP定位失败:', error); // 定位失败时不显示错误,静默处理