$ErrorActionPreference = "Stop" $csvPath = Join-Path $PSScriptRoot "china-cities.csv" if (-not (Test-Path $csvPath)) { Write-Host "下载城市数据 CSV..." $urls = @( "https://raw.githubusercontent.com/qwd/LocationList/master/China-City-List-latest.csv", "https://cdn.jsdelivr.net/gh/qwd/LocationList@master/China-City-List-latest.csv" ) $ok = $false foreach ($u in $urls) { try { Invoke-WebRequest -Uri $u -OutFile $csvPath -UseBasicParsing; Write-Host "OK: $u"; $ok = $true; break } catch { Write-Host "失败: $($_.Exception.Message)" } } if (-not $ok) { Write-Host "CSV 下载失败"; exit 1 } } $provMap = @{ "北京市"="北京"; "天津市"="天津"; "上海市"="上海"; "重庆市"="重庆"; "河北省"="河北"; "山西省"="山西"; "辽宁省"="辽宁"; "吉林省"="吉林"; "黑龙江省"="黑龙江"; "江苏省"="江苏"; "浙江省"="浙江"; "安徽省"="安徽"; "福建省"="福建"; "江西省"="江西"; "山东省"="山东"; "河南省"="河南"; "湖北省"="湖北"; "湖南省"="湖南"; "广东省"="广东"; "海南省"="海南"; "四川省"="四川"; "贵州省"="贵州"; "云南省"="云南"; "陕西省"="陕西"; "甘肃省"="甘肃"; "青海省"="青海"; "内蒙古自治区"="内蒙古"; "广西壮族自治区"="广西"; "西藏自治区"="西藏"; "宁夏回族自治区"="宁夏"; "新疆维吾尔自治区"="新疆"; "香港特别行政区"="香港"; "澳门特别行政区"="澳门"; "台湾省"="台湾"; } $fullMap = @{} foreach ($k in $provMap.Keys) { $fullMap[$provMap[$k]] = $k } $provOrder = @( "北京","天津","上海","重庆", "河北","山西","辽宁","吉林","黑龙江", "江苏","浙江","安徽","福建","江西","山东", "河南","湖北","湖南","广东","海南", "四川","贵州","云南","陕西","甘肃","青海", "内蒙古","广西","西藏","宁夏","新疆", "香港","澳门","台湾" ) # 解析 CSV(跳过第1行版本号) $lines = Get-Content $csvPath -Encoding UTF8 $tempCsv = Join-Path $PSScriptRoot "_cities_temp.csv" $lines[1..($lines.Length-1)] | Set-Content $tempCsv -Encoding UTF8 $csv = Import-Csv $tempCsv -Encoding UTF8 Remove-Item $tempCsv -Force Write-Host "CSV 记录: $($csv.Count)" # 按 Adm1 一次性分桶 $byAdm1 = @{} foreach ($r in $csv) { $a = $r.'Adm1_Name_ZH' if (-not $byAdm1.ContainsKey($a)) { $byAdm1[$a] = New-Object System.Collections.ArrayList } [void]$byAdm1[$a].Add($r) } $result = New-Object System.Collections.ArrayList $weird = New-Object System.Collections.ArrayList foreach ($provShort in $provOrder) { $provFull = $fullMap[$provShort] $rows = @() if ($byAdm1.ContainsKey($provFull)) { $rows = @($byAdm1[$provFull]) } if ($rows.Count -eq 0) { foreach ($k in $byAdm1.Keys) { if ($k -like "*$provShort*") { $rows += @($byAdm1[$k]) } } } if ($rows.Count -eq 0) { Write-Host "省份未找到: $provShort"; continue } # 按 Adm2 分组,每 Adm2 取 Location_ID 最小(地级市中心) $adm2Groups = $rows | Group-Object 'Adm2_Name_ZH' $cnt = 0 foreach ($g in $adm2Groups) { if ($g.Name -match "省直辖|直辖县|县级行政区") { [void]$weird.Add("$provShort/$($g.Name)"); continue } $center = $g.Group | Sort-Object Location_ID | Select-Object -First 1 # 过滤非数字 ID(台湾乡镇异常十六进制 ID,非和风标准 9 位) if ($center.Location_ID -notmatch '^\d+$') { [void]$weird.Add("$provShort/$($g.Name)/$($center.Location_ID)"); continue } # name 用 Adm2 名去末尾"市"/"特别行政区"(避免区名如"双桥",简化港澳) $name = $g.Name -replace '市$','' -replace '特别行政区$','' [void]$result.Add([pscustomobject]@{ id=$center.Location_ID; name=$name; adm1=$provShort }) $cnt++ } Write-Host (" {0,-8} : {1} 城" -f $provShort, $cnt) } Write-Host "" Write-Host "总计: $($result.Count) 城" if ($weird.Count -gt 0) { Write-Host "已过滤 ($($weird.Count)): $($weird -join '; ')" } # 输出 $utf8 = [System.Text.UTF8Encoding]::new($false) [System.IO.File]::WriteAllText((Join-Path $PSScriptRoot "cities.json"), ($result | ConvertTo-Json -Depth 2), $utf8) $goLines = $result | ForEach-Object { ' {"' + $_.id + '", "' + $_.name + '", "' + $_.name + '", "' + $_.adm1 + '"},' } $goBlock = "var cities = []City{`n" + ($goLines -join "`n") + "`n}" [System.IO.File]::WriteAllText((Join-Path $PSScriptRoot "cities.go.txt"), $goBlock, $utf8) $vueLines = $result | ForEach-Object { " { id: '" + $_.id + "', name: '" + $_.name + "', province: '" + $_.adm1 + "' }," } $vueBlock = "const allCities = [`n" + ($vueLines -join "`n") + "`n].map((c, i) => ({ ...c, _idx: i }))" [System.IO.File]::WriteAllText((Join-Path $PSScriptRoot "cities.vue.txt"), $vueBlock, $utf8) Write-Host "已生成: cities.json / cities.go.txt / cities.vue.txt"