(function (root, factory) { // 行政区划模块同时支持浏览器页面和 Node 单元测试。 if (typeof module === 'object' && module.exports) { module.exports = factory(root); return; } root.RegionPages = factory(root); if (root.document) { root.document.addEventListener('DOMContentLoaded', function () { root.RegionPages.init(); }); } })(typeof globalThis !== 'undefined' ? globalThis : window, function (root) { 'use strict'; var documentRef = root.document; function normalizeList(data) { // 行政区划接口使用通用 ListResult,兼容常见数组包裹字段。 if (Array.isArray(data)) return data; if (!data || typeof data !== 'object') return []; if (Array.isArray(data.rows)) return data.rows; if (Array.isArray(data.records)) return data.records; if (Array.isArray(data.list)) return data.list; if (Array.isArray(data.data)) return data.data; return []; } function pick(item, keys, fallback) { var source = item || {}; var index; var value; for (index = 0; index < keys.length; index += 1) { value = source[keys[index]]; if (value !== undefined && value !== null && value !== '') return value; } return fallback || ''; } function escapeHtml(value) { // 地区名称和编码进入 option 前统一转义。 return String(value === undefined || value === null ? '' : value) .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"') .replace(/'/g, '''); } function getRegionCode(item) { return pick(item, ['regionCode', 'code', 'value'], ''); } function getRegionName(item) { return pick(item, ['regionName', 'name', 'label'], '未命名地区'); } function getRegionLevel(item) { var source = item || {}; var level = Number(pick(source, ['level', 'regionLevel'], 0)); var code = getRegionCode(source); if (level >= 1 && level <= 3) return level; if (!/^\d{6}$/.test(code)) return 0; if (/0000$/.test(code)) return 1; if (/00$/.test(code)) return 2; return 3; } function buildRegionSelection(items) { var selection = { provinceCode: '', cityCode: '', districtCode: '' }; normalizeList(items).forEach(function (item) { var code = getRegionCode(item); var level = getRegionLevel(item); if (level === 1) selection.provinceCode = code; if (level === 2) selection.cityCode = code; if (level === 3) selection.districtCode = code; }); return selection; } function buildRegionOption(item) { return ''; } function buildPathText(items) { return normalizeList(items).map(function (item) { return getRegionName(item); }).filter(Boolean).join(' / '); } function getFinalRegionCode(values) { var source = values || {}; return source.districtCode || source.cityCode || source.provinceCode || ''; } function trimOrUndefined(value) { var text = String(value === undefined || value === null ? '' : value).trim(); return text || undefined; } function buildProfileRegionBody(values) { var source = values || {}; return { provinceCode: trimOrUndefined(source.provinceCode), cityCode: trimOrUndefined(source.cityCode), districtCode: trimOrUndefined(source.districtCode) }; } function getApi() { return root.GenealogyApi && root.GenealogyApi.defaultClient; } function query(selector, rootNode) { return (rootNode || documentRef).querySelector(selector); } function queryAll(selector, rootNode) { return Array.prototype.slice.call((rootNode || documentRef).querySelectorAll(selector)); } function showMessage(message) { if (root.layui && root.layui.layer) { root.layui.layer.msg(message); return; } root.alert(message); } function getFormValues(form) { var values = {}; queryAll('[name]', form).forEach(function (field) { values[field.name] = field.value; }); return values; } function setOptions(select, items, placeholder) { var list = normalizeList(items); if (!select) return; select.innerHTML = '' + list.map(buildRegionOption).join(''); select.disabled = false; } function resetSelect(select, placeholder) { if (!select) return; select.innerHTML = ''; select.disabled = true; } function updateTargetCode(picker) { var values = { provinceCode: query('[data-region-level="province"]', picker).value, cityCode: query('[data-region-level="city"]', picker).value, districtCode: query('[data-region-level="district"]', picker).value }; var target = query('[data-region-code]', picker); if (target) target.value = getFinalRegionCode(values); } async function loadChildrenInto(select, parentCode, placeholder) { var api = getApi(); if (!api || !select) return; setOptions(select, await api.regionChildren(parentCode || '0'), placeholder); } async function initPicker(picker) { var province = query('[data-region-level="province"]', picker); var city = query('[data-region-level="city"]', picker); var district = query('[data-region-level="district"]', picker); resetSelect(city, '请选择市'); resetSelect(district, '请选择区县'); await loadChildrenInto(province, '0', '请选择省'); province.addEventListener('change', async function () { resetSelect(city, '请选择市'); resetSelect(district, '请选择区县'); updateTargetCode(picker); if (province.value) await loadChildrenInto(city, province.value, '请选择市'); }); city.addEventListener('change', async function () { resetSelect(district, '请选择区县'); updateTargetCode(picker); if (city.value) await loadChildrenInto(district, city.value, '请选择区县'); }); district.addEventListener('change', function () { updateTargetCode(picker); }); } async function applyRegionSelection(picker, selection) { var province = query('[data-region-level="province"]', picker); var city = query('[data-region-level="city"]', picker); var district = query('[data-region-level="district"]', picker); var values = selection || {}; if (!values.provinceCode) return; province.value = values.provinceCode; resetSelect(city, '请选择市'); resetSelect(district, '请选择区县'); if (values.cityCode) { await loadChildrenInto(city, values.provinceCode, '请选择市'); city.value = values.cityCode; } if (values.districtCode && values.cityCode) { await loadChildrenInto(district, values.cityCode, '请选择区县'); district.value = values.districtCode; } updateTargetCode(picker); } function renderSearchResults(container, items) { var list = normalizeList(items); if (!container) return; container.innerHTML = list.length ? list.map(function (item) { var code = getRegionCode(item); var name = getRegionName(item); return ''; }).join('') : '
未找到匹配地区
'; } async function submitRegionSearch(form) { var api = getApi(); var values = getFormValues(form); var results = query('[data-region-search-results]', form.parentNode); var pathText = query('[data-region-search-path]', form.parentNode); if (!api) return; if (!values.regionKeyword || !values.regionKeyword.trim()) { showMessage('请输入地区名称或编码'); return; } if (pathText) pathText.textContent = '搜索中...'; try { renderSearchResults(results, await api.regionSearch({ keyword: values.regionKeyword.trim(), limit: 20 })); if (pathText) pathText.textContent = '请选择一个地区'; } catch (error) { if (pathText) pathText.textContent = error.message || '地区搜索失败'; showMessage(error.message || '地区搜索失败'); } } async function selectSearchResult(button) { var api = getApi(); var code = button.getAttribute('data-region-search-result'); var section = button.closest('.module-panel'); var picker = section && query('[data-region-picker]', section); var pathText = section && query('[data-region-search-path]', section); var detail; var path; if (!api || !code || !picker) return; if (pathText) pathText.textContent = '正在定位地区...'; try { detail = await api.regionDetail(code); path = await api.regionPath(getRegionCode(detail) || code); await applyRegionSelection(picker, buildRegionSelection(path)); if (pathText) pathText.textContent = buildPathText(path); } catch (error) { if (pathText) pathText.textContent = error.message || '地区定位失败'; showMessage(error.message || '地区定位失败'); } } async function submitProfileRegion(form) { var api = getApi(); var values = getFormValues(form); var body = buildProfileRegionBody(values); if (!api) return; if (!body.provinceCode) { showMessage('请选择省份'); return; } try { await api.updateProfile(body); showMessage('地区资料已保存'); } catch (error) { showMessage(error.message || '地区资料保存失败'); } } function bindProfileForms() { queryAll('[data-region-profile-form]').forEach(function (form) { form.addEventListener('submit', function (event) { event.preventDefault(); submitProfileRegion(form); }); }); } function bindSearchForms() { queryAll('[data-region-search-form]').forEach(function (form) { form.addEventListener('submit', function (event) { event.preventDefault(); submitRegionSearch(form); }); }); documentRef.addEventListener('click', function (event) { var button = event.target.closest('[data-region-search-result]'); if (!button) return; event.preventDefault(); selectSearchResult(button); }); } function init() { if (!documentRef) return; queryAll('[data-region-picker]').forEach(function (picker) { initPicker(picker); }); bindProfileForms(); bindSearchForms(); } return { normalizeList: normalizeList, buildRegionOption: buildRegionOption, buildPathText: buildPathText, buildRegionSelection: buildRegionSelection, getFinalRegionCode: getFinalRegionCode, buildProfileRegionBody: buildProfileRegionBody, init: init }; });