Add hierarchical geography and cloud-hosted profile images
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
'use strict';
|
||||
const ZhaoGeography=(()=>{
|
||||
const data=typeof ZhaoGeographyData!=='undefined'?ZhaoGeographyData:require('./data/geography-data.js');
|
||||
const esc=s=>String(s??'').replace(/[&<>"']/g,c=>({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c]));
|
||||
const fields=['province','city','district'];
|
||||
const key=p=>p?p+'Geography':'geography';
|
||||
const legacy=(item,p,f)=>item[p?p+f[0].toUpperCase()+f.slice(1):f]||'';
|
||||
function infer(item={},prefix=''){
|
||||
if(item[key(prefix)])return {...item[key(prefix)]};
|
||||
const v={mode:'china',body:'地球',countryCode:'CN',country:'中国',path:''};
|
||||
let children=data.china,manual=false,valid=true;
|
||||
for(const f of fields){
|
||||
v[f]=legacy(item,prefix,f);const selected=children.find(x=>x.name===v[f]);v[f+'Code']=selected?.code||'';
|
||||
if(v[f]&&!selected&&!manual)valid=false;
|
||||
manual=manual||!!selected?.manual;children=selected?.children||[];
|
||||
}
|
||||
const country=legacy(item,prefix,'country');
|
||||
if(country&&country!=='中国'&&country!=='中华人民共和国'){
|
||||
const match=Object.entries(data.countries).find(([,name])=>name===country);
|
||||
v.mode=match?'world':'custom';v.country=country;v.countryCode=match?.[0]||'';
|
||||
}else if(!valid){v.mode='custom';v.country=country||'中国';}
|
||||
return v;
|
||||
}
|
||||
function change(v,field,value){
|
||||
const next={...v,[field]:value};
|
||||
if(field==='provinceCode'){next.cityCode='';next.city='';next.districtCode='';next.district='';}
|
||||
if(field==='cityCode'){next.districtCode='';next.district='';}
|
||||
if(field==='countryCode'){next.province='';next.city='';next.district='';if(value==='CN')next.mode='china';}
|
||||
if(field==='mode'){
|
||||
Object.assign(next,{body:'地球',countryCode:value==='china'?'CN':'',country:value==='china'?'中国':'',provinceCode:'',cityCode:'',districtCode:'',province:'',city:'',district:'',path:''});
|
||||
}
|
||||
if(next.mode==='china'){
|
||||
let children=data.china,manual=false;
|
||||
for(const f of fields){if(manual)continue;const selected=children.find(x=>x.code===next[f+'Code']);next[f]=selected?.name||'';children=selected?.children||[];manual=!!selected?.manual;}
|
||||
next.country='中国';next.body='地球';next.countryCode='CN';
|
||||
}else if(next.mode==='world')next.country=data.countries[next.countryCode]||'';
|
||||
return next;
|
||||
}
|
||||
function label(v){return [v.body&&v.body!=='地球'?v.body:'',v.country,v.province,v.city,v.district,v.path].filter(Boolean).filter((x,i,a)=>!i||x!==a[i-1]).join(' · ');}
|
||||
function describe(item,prefix=''){
|
||||
if(item[key(prefix)])return label(item[key(prefix)]);
|
||||
return ['country',...fields].map(f=>legacy(item,prefix,f)).filter(Boolean).join(' · ');
|
||||
}
|
||||
function fill(form,item={}){
|
||||
for(const root of form.querySelectorAll('[data-geography]')){
|
||||
const prefix=root.dataset.geography||'';let value=infer(item,prefix);
|
||||
const title=root.dataset.geoTitle||'地域';
|
||||
function draw(){
|
||||
const select=(field,text,options,disabled=false)=>`<label>${text}<select data-geo-field="${field}" aria-label="${esc(title+' · '+text)}" ${disabled?'disabled':''}>${options.map(([id,name])=>`<option value="${esc(id)}" ${value[field]===id?'selected':''}>${esc(name)}</option>`).join('')}</select></label>`;
|
||||
const input=(field,text,placeholder='')=>`<label>${text}<input data-geo-field="${field}" value="${esc(value[field]||'')}" maxlength="${field==='path'?1000:100}" placeholder="${esc(placeholder)}" aria-label="${esc(title+' · '+text)}"></label>`;
|
||||
let html=select('mode','地域范围',[['china','中国'],['world','世界 · 国家/地区'],['custom','自定义 · 历史地名/其他天体']]);
|
||||
if(value.mode==='china'){
|
||||
const province=data.china.find(x=>x.code===value.provinceCode),city=province?.children.find(x=>x.code===value.cityCode);
|
||||
html+=select('provinceCode','省/自治区/直辖市',[['','请选择(可留空)'],...data.china.map(x=>[x.code,x.name])]);
|
||||
if(province?.manual)html+=input('city','城市/地区')+input('district','区县/区域');
|
||||
else html+=select('cityCode','地级市/州/盟',[['','请选择(可留空)'],...(province?.children||[]).map(x=>[x.code,x.name])],!province)+select('districtCode','区/县/县级市',[['','请选择(可留空)'],...(city?.children||[]).map(x=>[x.code,x.name])],!city);
|
||||
html+='<p class="description full">选择上级后再选下级;变更上级会清空下级。内置区划为 2023 年数据,港澳台下级、新设区划或旧地名可手动填写或使用自定义。</p>';
|
||||
}else if(value.mode==='world'){
|
||||
html+=select('countryCode','国家/地区',[['','请选择'],...Object.entries(data.countries).sort((a,b)=>a[1].localeCompare(b[1],'zh-CN'))]);
|
||||
html+=input('province','州/省/一级行政区')+input('city','城市/二级行政区')+input('district','区县/地方行政区');
|
||||
html+='<p class="description full">国家/地区使用选项;各国层级不同,下级按当地名称填写,不适用可留空。</p>';
|
||||
}else{
|
||||
html+=input('body','天体/世界','例如:地球、月球、火星')+input('country','国家/地区(可空)')+input('province','省州/大区(可空)')+input('city','城市/区域(可空)')+input('district','区县/分区(可空)')+input('path','补充层级路径','例如:基地 / 居住区 / 一号楼');
|
||||
html+='<p class="description full">自定义内容按原文保存,适合历史地名或扩展地域;不做行政区划校验。</p>';
|
||||
}
|
||||
root.innerHTML=`<legend>${esc(title)}</legend><div class="form-grid">${html}</div><output class="geo-path" aria-live="polite">${esc(label(value)||'地域待选择')}</output>`;
|
||||
}
|
||||
root.onchange=e=>{const field=e.target.dataset.geoField;if(!field)return;value=change(value,field,e.target.value);if(e.target.tagName==='SELECT'){draw();root.querySelector(`[data-geo-field="${field}"]`)?.focus();}else root.querySelector('output').textContent=label(value);};
|
||||
root.oninput=e=>{const field=e.target.dataset.geoField;if(field&&e.target.tagName==='INPUT'){value[field]=e.target.value;root.querySelector('output').textContent=label(value);}};
|
||||
root.geoValue=()=>({...value});draw();
|
||||
}
|
||||
}
|
||||
function collect(form){return Object.fromEntries([...form.querySelectorAll('[data-geography]')].map(root=>[key(root.dataset.geography||''),root.geoValue()]));}
|
||||
return {infer,change,label,describe,fill,collect};
|
||||
})();
|
||||
if(typeof module!=='undefined')module.exports=ZhaoGeography;
|
||||
Reference in New Issue
Block a user