Fix learn 404, coin animation, full regions, and AI key errors.
Use numeric /learn/{num} routes, register Tailwind coin animations with方孔铜钱 UI, expand to 34 provinces, and surface missing OPENAI_API_KEY clearly.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -6,29 +6,50 @@ import MarkdownContent from "@/components/learn/markdown-content";
|
||||
import {
|
||||
getGuaName,
|
||||
getGuaNumber,
|
||||
guaNumFromMark,
|
||||
listGuaMarks,
|
||||
markFromNum,
|
||||
readLearnMarkdown,
|
||||
stripFrontmatter,
|
||||
} from "@/lib/content/zhouyi";
|
||||
|
||||
export async function generateStaticParams() {
|
||||
const marks = await listGuaMarks("traditional");
|
||||
return marks.map((guaMark) => ({ guaMark }));
|
||||
async function resolveGuaMark(slug: string): Promise<string | null> {
|
||||
const decoded = decodeURIComponent(slug);
|
||||
if (/^\d{1,2}$/.test(decoded)) {
|
||||
return markFromNum(decoded, "traditional");
|
||||
}
|
||||
const marks = await listGuaMarks("traditional");
|
||||
if (marks.includes(decoded)) {
|
||||
return decoded;
|
||||
}
|
||||
if (/^\d{2}\./.test(decoded)) {
|
||||
return marks.find((m) => m === decoded || m.startsWith(decoded.split(".")[0] + ".")) ?? null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export async function generateStaticParams() {
|
||||
return Array.from({ length: 64 }, (_, i) => ({
|
||||
slug: String(i + 1).padStart(2, "0"),
|
||||
}));
|
||||
}
|
||||
|
||||
export const dynamicParams = true;
|
||||
|
||||
export default async function GuaDetailPage({
|
||||
params,
|
||||
}: {
|
||||
params: Promise<{ guaMark: string }>;
|
||||
params: Promise<{ slug: string }>;
|
||||
}) {
|
||||
const { guaMark } = await params;
|
||||
const marks = await listGuaMarks("traditional");
|
||||
if (!marks.includes(guaMark)) {
|
||||
const { slug } = await params;
|
||||
const guaMark = await resolveGuaMark(slug);
|
||||
if (!guaMark) {
|
||||
notFound();
|
||||
}
|
||||
|
||||
const raw = await readLearnMarkdown(guaMark, "traditional");
|
||||
const content = stripFrontmatter(raw);
|
||||
const num = guaNumFromMark(guaMark);
|
||||
|
||||
return (
|
||||
<PageShell className="max-w-3xl px-4 py-8">
|
||||
@@ -41,7 +62,7 @@ export default async function GuaDetailPage({
|
||||
第 {getGuaNumber(guaMark)} 卦 · {getGuaName(guaMark)}
|
||||
</div>
|
||||
<MarkdownContent content={content} variant="traditional" />
|
||||
<GuaFooter guaMark={guaMark} />
|
||||
<GuaFooter guaMark={guaMark} guaNum={num} />
|
||||
</PageShell>
|
||||
);
|
||||
}
|
||||
@@ -6,24 +6,41 @@ import MarkdownContent from "@/components/learn/markdown-content";
|
||||
import {
|
||||
getGuaName,
|
||||
getGuaNumber,
|
||||
guaNumFromMark,
|
||||
listGuaMarks,
|
||||
markFromNum,
|
||||
readLearnMarkdown,
|
||||
stripFrontmatter,
|
||||
} from "@/lib/content/zhouyi";
|
||||
|
||||
export async function generateStaticParams() {
|
||||
const marks = await listGuaMarks("simplified");
|
||||
return marks.map((guaMark) => ({ guaMark }));
|
||||
async function resolveGuaMark(slug: string): Promise<string | null> {
|
||||
const decoded = decodeURIComponent(slug);
|
||||
if (/^\d{1,2}$/.test(decoded)) {
|
||||
return markFromNum(decoded, "simplified");
|
||||
}
|
||||
const marks = await listGuaMarks("simplified");
|
||||
if (marks.includes(decoded)) {
|
||||
return decoded;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export async function generateStaticParams() {
|
||||
return Array.from({ length: 64 }, (_, i) => ({
|
||||
slug: String(i + 1).padStart(2, "0"),
|
||||
}));
|
||||
}
|
||||
|
||||
export const dynamicParams = true;
|
||||
|
||||
export default async function GuaOtherDetailPage({
|
||||
params,
|
||||
}: {
|
||||
params: Promise<{ guaMark: string }>;
|
||||
params: Promise<{ slug: string }>;
|
||||
}) {
|
||||
const { guaMark } = await params;
|
||||
const marks = await listGuaMarks("simplified");
|
||||
if (!marks.includes(guaMark)) {
|
||||
const { slug } = await params;
|
||||
const guaMark = await resolveGuaMark(slug);
|
||||
if (!guaMark) {
|
||||
notFound();
|
||||
}
|
||||
|
||||
@@ -41,7 +58,7 @@ export default async function GuaOtherDetailPage({
|
||||
第 {getGuaNumber(guaMark)} 卦 · {getGuaName(guaMark)}
|
||||
</div>
|
||||
<MarkdownContent content={content} variant="simplified" />
|
||||
<GuaFooter guaMark={guaMark} />
|
||||
<GuaFooter guaMark={guaMark} guaNum={guaNumFromMark(guaMark)} />
|
||||
</PageShell>
|
||||
);
|
||||
}
|
||||
@@ -2,7 +2,7 @@ import Link from "next/link";
|
||||
import PageShell from "@/components/page-shell";
|
||||
import {
|
||||
getGuaName,
|
||||
getGuaNumber,
|
||||
guaNumFromMark,
|
||||
listGuaMarks,
|
||||
} from "@/lib/content/zhouyi";
|
||||
|
||||
@@ -35,11 +35,11 @@ export default async function LearnOtherPage() {
|
||||
{guaMarks.map((mark) => (
|
||||
<tr key={mark} className="border-t">
|
||||
<td className="px-4 py-2 font-mono text-muted-foreground">
|
||||
{getGuaNumber(mark)}
|
||||
{guaNumFromMark(mark)}
|
||||
</td>
|
||||
<td className="px-4 py-2">
|
||||
<Link
|
||||
href={`/learn/other/${encodeURIComponent(mark)}`}
|
||||
href={`/learn/other/${guaNumFromMark(mark)}`}
|
||||
className="text-primary underline-offset-4 hover:underline"
|
||||
>
|
||||
{getGuaName(mark)}
|
||||
|
||||
+3
-3
@@ -2,7 +2,7 @@ import Link from "next/link";
|
||||
import PageShell from "@/components/page-shell";
|
||||
import {
|
||||
getGuaName,
|
||||
getGuaNumber,
|
||||
guaNumFromMark,
|
||||
listGuaMarks,
|
||||
} from "@/lib/content/zhouyi";
|
||||
|
||||
@@ -35,11 +35,11 @@ export default async function LearnPage() {
|
||||
{guaMarks.map((mark) => (
|
||||
<tr key={mark} className="border-t">
|
||||
<td className="px-4 py-2 font-mono text-muted-foreground">
|
||||
{getGuaNumber(mark)}
|
||||
{guaNumFromMark(mark)}
|
||||
</td>
|
||||
<td className="px-4 py-2">
|
||||
<Link
|
||||
href={`/learn/${encodeURIComponent(mark)}`}
|
||||
href={`/learn/${guaNumFromMark(mark)}`}
|
||||
className="text-primary underline-offset-4 hover:underline"
|
||||
>
|
||||
{getGuaName(mark)}
|
||||
|
||||
+23
-18
@@ -2,7 +2,13 @@ import React, { useEffect, useState } from "react";
|
||||
import clsx from "clsx";
|
||||
|
||||
const rotationDuration = 3800;
|
||||
const bezier = "cubic-bezier(0.645,0.045,0.355,1)";
|
||||
|
||||
const COIN_ANIM: Record<string, string> = {
|
||||
"front-front": "animate-coin-front-front",
|
||||
"front-back": "animate-coin-front-back",
|
||||
"back-front": "animate-coin-back-front",
|
||||
"back-back": "animate-coin-back-back",
|
||||
};
|
||||
|
||||
function Coin(props: {
|
||||
frontList: boolean[];
|
||||
@@ -15,7 +21,6 @@ function Coin(props: {
|
||||
if (!props.rotation) {
|
||||
return;
|
||||
}
|
||||
|
||||
const id = setTimeout(() => {
|
||||
setLastFront(props.frontList);
|
||||
props.onTransitionEnd();
|
||||
@@ -37,23 +42,30 @@ function Coin(props: {
|
||||
);
|
||||
}
|
||||
|
||||
/** 方孔铜钱造型 */
|
||||
function CoinFace({ side }: { side: "front" | "back" }) {
|
||||
const isFront = side === "front";
|
||||
return (
|
||||
<div
|
||||
className={clsx(
|
||||
"absolute inset-0 flex items-center justify-center rounded-full border-[3px] shadow-inner",
|
||||
"absolute inset-0 rounded-full border-[3px] shadow-md",
|
||||
isFront
|
||||
? "border-amber-500 bg-gradient-to-br from-amber-200 to-amber-400 text-amber-950"
|
||||
: "border-stone-500 bg-gradient-to-br from-stone-300 to-stone-500 text-stone-800",
|
||||
? "border-amber-600 bg-gradient-to-br from-amber-300 via-amber-400 to-amber-600"
|
||||
: "border-stone-600 bg-gradient-to-br from-stone-400 via-stone-500 to-stone-700",
|
||||
)}
|
||||
style={{
|
||||
backfaceVisibility: "hidden",
|
||||
transform: isFront ? undefined : "rotateY(180deg)",
|
||||
}}
|
||||
>
|
||||
<span className="select-none text-lg font-bold sm:text-xl">
|
||||
{isFront ? "正" : "反"}
|
||||
<div className="absolute inset-[18%] rounded-sm border-2 border-amber-800/40 bg-amber-900/10" />
|
||||
<span
|
||||
className={clsx(
|
||||
"absolute bottom-1 left-0 right-0 text-center text-[10px] font-bold sm:text-xs",
|
||||
isFront ? "text-amber-950/80" : "text-stone-200/90",
|
||||
)}
|
||||
>
|
||||
{isFront ? "乾" : "坤"}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
@@ -64,24 +76,17 @@ function CoinItem(props: {
|
||||
lastFront: boolean;
|
||||
rotation: boolean;
|
||||
}) {
|
||||
let animate = "";
|
||||
if (props.rotation) {
|
||||
animate = `animate-[coin-${getFront(props.lastFront)}-${getFront(
|
||||
props.front,
|
||||
)}_${rotationDuration / 1000}s_${bezier}]`;
|
||||
}
|
||||
const animKey = `${getFront(props.lastFront)}-${getFront(props.front)}`;
|
||||
const animClass = props.rotation ? COIN_ANIM[animKey] : "";
|
||||
|
||||
return (
|
||||
<div
|
||||
className="h-16 w-16 sm:h-20 sm:w-20"
|
||||
style={{ perspective: "800px" }}
|
||||
>
|
||||
<div className="h-[4.5rem] w-[4.5rem] sm:h-20 sm:w-20" style={{ perspective: "900px" }}>
|
||||
<div
|
||||
style={{
|
||||
transform: `rotateY(${props.front ? 0 : 180}deg)`,
|
||||
transformStyle: "preserve-3d",
|
||||
}}
|
||||
className={clsx("relative h-full w-full", animate)}
|
||||
className={clsx("relative h-full w-full", animClass)}
|
||||
>
|
||||
<CoinFace side="front" />
|
||||
<CoinFace side="back" />
|
||||
|
||||
@@ -1,12 +1,20 @@
|
||||
import Link from "next/link";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Sparkles } from "lucide-react";
|
||||
import { guaNumFromMark } from "@/lib/content/zhouyi";
|
||||
|
||||
export default function GuaFooter({ guaMark }: { guaMark: string }) {
|
||||
export default function GuaFooter({
|
||||
guaMark,
|
||||
guaNum,
|
||||
}: {
|
||||
guaMark: string;
|
||||
guaNum?: string;
|
||||
}) {
|
||||
const num = guaNum ?? guaNumFromMark(guaMark);
|
||||
return (
|
||||
<div className="mt-8 flex flex-wrap gap-3 border-t pt-6">
|
||||
<Button asChild size="sm">
|
||||
<Link href={`/liuyao?gua=${encodeURIComponent(guaMark)}`}>
|
||||
<Link href={`/liuyao?gua=${num}`}>
|
||||
<Sparkles size={16} className="mr-1" />
|
||||
以此卦起卦
|
||||
</Link>
|
||||
|
||||
@@ -24,9 +24,12 @@ function resolveLearnHref(
|
||||
if (href.endsWith("/index.md")) {
|
||||
const mark = href.replace(/\/index\.md$/, "").replace(/^\.\//, "");
|
||||
if (mark.startsWith("other/")) {
|
||||
return `/learn/other/${mark.slice("other/".length)}`;
|
||||
const folder = mark.slice("other/".length);
|
||||
const num = folder.split(".")[0];
|
||||
return `/learn/other/${num}`;
|
||||
}
|
||||
return `${base}/${mark}`;
|
||||
const num = mark.split(".")[0];
|
||||
return `${base}/${num}`;
|
||||
}
|
||||
return href;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import React from "react";
|
||||
import Link from "next/link";
|
||||
import { guaNumFromMark } from "@/lib/content/zhouyi";
|
||||
|
||||
export interface ResultObj {
|
||||
guaTitle: string;
|
||||
@@ -8,16 +10,17 @@ export interface ResultObj {
|
||||
}
|
||||
|
||||
function Result(props: ResultObj) {
|
||||
const guaNum = guaNumFromMark(props.guaMark);
|
||||
return (
|
||||
<div className="flex flex-col items-start justify-center gap-2 sm:gap-3">
|
||||
{props.guaTitle}
|
||||
<a
|
||||
<Link
|
||||
className="group flex items-center gap-1 font-medium text-primary/80 underline underline-offset-4 transition-colors hover:text-primary/100"
|
||||
href={`/learn/${props.guaMark}`}
|
||||
href={`/learn/${guaNum}`}
|
||||
>
|
||||
<div className="mt-1 h-[90%] w-1.5 bg-blue-400/80 transition-colors group-hover:bg-blue-400/100" />
|
||||
<span>{props.guaResult}</span>
|
||||
</a>
|
||||
</Link>
|
||||
<span className="text-sm italic text-muted-foreground">
|
||||
{props.guaChange}
|
||||
</span>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { bool } from "aimless.js";
|
||||
import Coin from "@/components/coin";
|
||||
import Hexagram from "@/components/hexagram";
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
YAO_LABELS,
|
||||
} from "@/lib/calc/hexagram";
|
||||
|
||||
const AUTO_DELAY = 600;
|
||||
const AUTO_DELAY = 800;
|
||||
|
||||
interface HexagramInputProps {
|
||||
mode: "online" | "offline";
|
||||
@@ -30,43 +30,30 @@ export default function HexagramInput({
|
||||
}: HexagramInputProps) {
|
||||
const [frontList, setFrontList] = useState([true, true, true]);
|
||||
const [rotation, setRotation] = useState(false);
|
||||
const [hexagramList, setHexagramList] = useState<
|
||||
GuaResult["list"]
|
||||
>([]);
|
||||
const [count, setCount] = useState(0);
|
||||
const [hexagramList, setHexagramList] = useState<GuaResult["list"]>([]);
|
||||
const [offlineCounts, setOfflineCounts] = useState<(number | null)[]>(
|
||||
Array(6).fill(null),
|
||||
);
|
||||
|
||||
const complete = hexagramList.length === 6;
|
||||
|
||||
useEffect(() => {
|
||||
setHexagramList([]);
|
||||
setCount(0);
|
||||
setOfflineCounts(Array(6).fill(null));
|
||||
setRotation(false);
|
||||
}, [mode]);
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
mode !== "online" ||
|
||||
!enabled ||
|
||||
rotation ||
|
||||
hexagramList.length >= 6 ||
|
||||
count >= 6
|
||||
) {
|
||||
return;
|
||||
}
|
||||
const timer = setTimeout(startOnlineCast, AUTO_DELAY);
|
||||
return () => clearTimeout(timer);
|
||||
}, [mode, enabled, rotation, count, hexagramList.length]);
|
||||
|
||||
function finishList(list: GuaResult["list"]) {
|
||||
const finishList = useCallback(
|
||||
(list: GuaResult["list"]) => {
|
||||
const result = computeGuaResult(list);
|
||||
if (result) {
|
||||
onResult({ list, result });
|
||||
}
|
||||
}
|
||||
},
|
||||
[onResult],
|
||||
);
|
||||
|
||||
function onTransitionEnd() {
|
||||
const onTransitionEnd = useCallback(() => {
|
||||
setRotation(false);
|
||||
const frontCount = frontList.reduce(
|
||||
(acc, val) => (val ? acc + 1 : acc),
|
||||
@@ -86,16 +73,23 @@ export default function HexagramInput({
|
||||
}
|
||||
return newList;
|
||||
});
|
||||
}
|
||||
}, [frontList, finishList]);
|
||||
|
||||
function startOnlineCast() {
|
||||
const startOnlineCast = useCallback(() => {
|
||||
if (rotation || !enabled || hexagramList.length >= 6) {
|
||||
return;
|
||||
}
|
||||
setFrontList([bool(), bool(), bool()]);
|
||||
setRotation(true);
|
||||
setCount((c) => c + 1);
|
||||
}, [rotation, enabled, hexagramList.length]);
|
||||
|
||||
useEffect(() => {
|
||||
if (mode !== "online" || !enabled || rotation || complete) {
|
||||
return;
|
||||
}
|
||||
const timer = setTimeout(startOnlineCast, AUTO_DELAY);
|
||||
return () => clearTimeout(timer);
|
||||
}, [mode, enabled, rotation, complete, hexagramList.length, startOnlineCast]);
|
||||
|
||||
function handleOfflineSelect(index: number, frontCount: number) {
|
||||
const next = [...offlineCounts];
|
||||
@@ -114,13 +108,11 @@ export default function HexagramInput({
|
||||
|
||||
function handleReset() {
|
||||
setHexagramList([]);
|
||||
setCount(0);
|
||||
setOfflineCounts(Array(6).fill(null));
|
||||
setRotation(false);
|
||||
onClear();
|
||||
}
|
||||
|
||||
const complete = hexagramList.length === 6;
|
||||
const offlineReady = offlineCounts.every((v) => v !== null);
|
||||
|
||||
if (!enabled) {
|
||||
@@ -133,7 +125,7 @@ export default function HexagramInput({
|
||||
|
||||
return (
|
||||
<div className="flex w-full flex-col items-center gap-4">
|
||||
{mode === "online" && !complete && (
|
||||
{mode === "online" && (
|
||||
<>
|
||||
<Coin
|
||||
onTransitionEnd={onTransitionEnd}
|
||||
@@ -143,9 +135,12 @@ export default function HexagramInput({
|
||||
<span className="text-lg font-medium">
|
||||
🎲 第{" "}
|
||||
<span className="font-mono text-xl font-bold text-orange-500">
|
||||
{count === 0 ? "-/-" : `${count}/6`}
|
||||
{complete ? "6/6" : `${hexagramList.length + (rotation ? 1 : 0)}/6`}
|
||||
</span>{" "}
|
||||
次卜筮
|
||||
{rotation && (
|
||||
<span className="ml-2 text-sm text-muted-foreground">铜钱旋转中…</span>
|
||||
)}
|
||||
</span>
|
||||
</>
|
||||
)}
|
||||
|
||||
@@ -71,10 +71,10 @@ export default function RegionSelect({
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
{cityOptional && provinceCode && (
|
||||
{cityOptional && provinceCode && location && (
|
||||
<p className="text-xs text-muted-foreground">
|
||||
城市可不选,默认使用省份经度;已选:
|
||||
{location ? `${location.name}(${location.longitude}°)` : "—"}
|
||||
城市可选;未选城市时使用省份经度。当前:
|
||||
{location.name}({location.longitude}°)
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -18,6 +18,10 @@ export async function streamAIResponse(
|
||||
system: string,
|
||||
user: string,
|
||||
): Promise<{ data?: ReturnType<typeof createStreamableValue<string>>["value"]; error?: string }> {
|
||||
if (!process.env.OPENAI_API_KEY?.trim()) {
|
||||
return { error: "未配置 OPENAI_API_KEY,请在 .env.local 或 Docker env_file 中设置" };
|
||||
}
|
||||
|
||||
const stream = createStreamableValue<string>();
|
||||
|
||||
try {
|
||||
|
||||
@@ -21,6 +21,21 @@ export async function listGuaMarks(
|
||||
.sort();
|
||||
}
|
||||
|
||||
/** 序号 slug,如 "35" */
|
||||
export function guaNumFromMark(guaMark: string): string {
|
||||
return guaMark.split(".")[0];
|
||||
}
|
||||
|
||||
/** 由序号解析目录名,如 "35" → "35.火地晋" */
|
||||
export async function markFromNum(
|
||||
num: string,
|
||||
variant: LearnVariant = "traditional",
|
||||
): Promise<string | null> {
|
||||
const padded = num.padStart(2, "0").slice(-2);
|
||||
const marks = await listGuaMarks(variant);
|
||||
return marks.find((m) => m.startsWith(`${padded}.`)) ?? null;
|
||||
}
|
||||
|
||||
export async function readGuaMarkdown(guaMark: string): Promise<string> {
|
||||
const filePath = path.join(DOCS_ROOT, guaMark, "index.md");
|
||||
return fs.readFile(filePath, "utf-8");
|
||||
|
||||
+700
-69
@@ -3,105 +3,736 @@
|
||||
"name": "北京市",
|
||||
"longitude": 116.4074,
|
||||
"children": {
|
||||
"110101": { "name": "东城区", "longitude": 116.4164 },
|
||||
"110105": { "name": "朝阳区", "longitude": 116.4434 },
|
||||
"110108": { "name": "海淀区", "longitude": 116.2983 }
|
||||
"110101": {
|
||||
"name": "东城区",
|
||||
"longitude": 116.4164
|
||||
},
|
||||
"110105": {
|
||||
"name": "朝阳区",
|
||||
"longitude": 116.4434
|
||||
},
|
||||
"110108": {
|
||||
"name": "海淀区",
|
||||
"longitude": 116.2983
|
||||
},
|
||||
"110114": {
|
||||
"name": "昌平区",
|
||||
"longitude": 116.2312
|
||||
}
|
||||
}
|
||||
},
|
||||
"120000": {
|
||||
"name": "天津市",
|
||||
"longitude": 117.201,
|
||||
"children": {
|
||||
"120101": {
|
||||
"name": "和平区",
|
||||
"longitude": 117.2147
|
||||
},
|
||||
"120103": {
|
||||
"name": "河西区",
|
||||
"longitude": 117.2234
|
||||
},
|
||||
"120110": {
|
||||
"name": "东丽区",
|
||||
"longitude": 117.3143
|
||||
},
|
||||
"120116": {
|
||||
"name": "滨海新区",
|
||||
"longitude": 117.7105
|
||||
}
|
||||
}
|
||||
},
|
||||
"130000": {
|
||||
"name": "河北省",
|
||||
"longitude": 114.5149,
|
||||
"children": {
|
||||
"130100": {
|
||||
"name": "石家庄市",
|
||||
"longitude": 114.5149
|
||||
},
|
||||
"130200": {
|
||||
"name": "唐山市",
|
||||
"longitude": 118.1802
|
||||
},
|
||||
"130300": {
|
||||
"name": "秦皇岛市",
|
||||
"longitude": 119.6005
|
||||
},
|
||||
"130600": {
|
||||
"name": "保定市",
|
||||
"longitude": 115.4648
|
||||
},
|
||||
"131000": {
|
||||
"name": "廊坊市",
|
||||
"longitude": 116.6838
|
||||
}
|
||||
}
|
||||
},
|
||||
"140000": {
|
||||
"name": "山西省",
|
||||
"longitude": 112.5624,
|
||||
"children": {
|
||||
"140100": {
|
||||
"name": "太原市",
|
||||
"longitude": 112.5624
|
||||
},
|
||||
"140200": {
|
||||
"name": "大同市",
|
||||
"longitude": 113.3001
|
||||
},
|
||||
"140500": {
|
||||
"name": "晋城市",
|
||||
"longitude": 112.8513
|
||||
},
|
||||
"140700": {
|
||||
"name": "晋中市",
|
||||
"longitude": 112.7528
|
||||
}
|
||||
}
|
||||
},
|
||||
"150000": {
|
||||
"name": "内蒙古自治区",
|
||||
"longitude": 111.7652,
|
||||
"children": {
|
||||
"150100": {
|
||||
"name": "呼和浩特市",
|
||||
"longitude": 111.7652
|
||||
},
|
||||
"150200": {
|
||||
"name": "包头市",
|
||||
"longitude": 109.8403
|
||||
},
|
||||
"150400": {
|
||||
"name": "赤峰市",
|
||||
"longitude": 118.8869
|
||||
},
|
||||
"150500": {
|
||||
"name": "通辽市",
|
||||
"longitude": 122.2434
|
||||
}
|
||||
}
|
||||
},
|
||||
"210000": {
|
||||
"name": "辽宁省",
|
||||
"longitude": 123.4315,
|
||||
"children": {
|
||||
"210100": {
|
||||
"name": "沈阳市",
|
||||
"longitude": 123.4315
|
||||
},
|
||||
"210200": {
|
||||
"name": "大连市",
|
||||
"longitude": 121.6147
|
||||
},
|
||||
"210300": {
|
||||
"name": "鞍山市",
|
||||
"longitude": 122.9946
|
||||
},
|
||||
"210600": {
|
||||
"name": "丹东市",
|
||||
"longitude": 124.3545
|
||||
}
|
||||
}
|
||||
},
|
||||
"220000": {
|
||||
"name": "吉林省",
|
||||
"longitude": 125.3235,
|
||||
"children": {
|
||||
"220100": {
|
||||
"name": "长春市",
|
||||
"longitude": 125.3235
|
||||
},
|
||||
"220200": {
|
||||
"name": "吉林市",
|
||||
"longitude": 126.5494
|
||||
},
|
||||
"220300": {
|
||||
"name": "四平市",
|
||||
"longitude": 124.3505
|
||||
},
|
||||
"222400": {
|
||||
"name": "延边州",
|
||||
"longitude": 129.5132
|
||||
}
|
||||
}
|
||||
},
|
||||
"230000": {
|
||||
"name": "黑龙江省",
|
||||
"longitude": 126.6425,
|
||||
"children": {
|
||||
"230100": {
|
||||
"name": "哈尔滨市",
|
||||
"longitude": 126.6425
|
||||
},
|
||||
"230200": {
|
||||
"name": "齐齐哈尔市",
|
||||
"longitude": 123.9182
|
||||
},
|
||||
"230600": {
|
||||
"name": "大庆市",
|
||||
"longitude": 125.1031
|
||||
},
|
||||
"231000": {
|
||||
"name": "牡丹江市",
|
||||
"longitude": 129.6332
|
||||
}
|
||||
}
|
||||
},
|
||||
"310000": {
|
||||
"name": "上海市",
|
||||
"longitude": 121.4737,
|
||||
"children": {
|
||||
"310101": { "name": "黄浦区", "longitude": 121.4903 },
|
||||
"310115": { "name": "浦东新区", "longitude": 121.5447 },
|
||||
"310104": { "name": "徐汇区", "longitude": 121.4365 }
|
||||
}
|
||||
"310101": {
|
||||
"name": "黄浦区",
|
||||
"longitude": 121.4903
|
||||
},
|
||||
"440000": {
|
||||
"name": "广东省",
|
||||
"longitude": 113.2665,
|
||||
"children": {
|
||||
"440100": { "name": "广州市", "longitude": 113.2644 },
|
||||
"440300": { "name": "深圳市", "longitude": 114.0579 },
|
||||
"440600": { "name": "佛山市", "longitude": 113.1214 }
|
||||
}
|
||||
"310104": {
|
||||
"name": "徐汇区",
|
||||
"longitude": 121.4365
|
||||
},
|
||||
"330000": {
|
||||
"name": "浙江省",
|
||||
"longitude": 120.1536,
|
||||
"children": {
|
||||
"330100": { "name": "杭州市", "longitude": 120.1551 },
|
||||
"330200": { "name": "宁波市", "longitude": 121.5503 },
|
||||
"330300": { "name": "温州市", "longitude": 120.6994 }
|
||||
"310115": {
|
||||
"name": "浦东新区",
|
||||
"longitude": 121.5447
|
||||
},
|
||||
"310117": {
|
||||
"name": "松江区",
|
||||
"longitude": 121.2277
|
||||
}
|
||||
}
|
||||
},
|
||||
"320000": {
|
||||
"name": "江苏省",
|
||||
"longitude": 118.7969,
|
||||
"children": {
|
||||
"320100": { "name": "南京市", "longitude": 118.7969 },
|
||||
"320500": { "name": "苏州市", "longitude": 120.5853 },
|
||||
"320200": { "name": "无锡市", "longitude": 120.3119 }
|
||||
"320100": {
|
||||
"name": "南京市",
|
||||
"longitude": 118.7969
|
||||
},
|
||||
"320200": {
|
||||
"name": "无锡市",
|
||||
"longitude": 120.3119
|
||||
},
|
||||
"320300": {
|
||||
"name": "徐州市",
|
||||
"longitude": 117.1848
|
||||
},
|
||||
"320500": {
|
||||
"name": "苏州市",
|
||||
"longitude": 120.5853
|
||||
},
|
||||
"320600": {
|
||||
"name": "南通市",
|
||||
"longitude": 120.8945
|
||||
}
|
||||
}
|
||||
},
|
||||
"510000": {
|
||||
"name": "四川省",
|
||||
"longitude": 104.0665,
|
||||
"330000": {
|
||||
"name": "浙江省",
|
||||
"longitude": 120.1536,
|
||||
"children": {
|
||||
"510100": { "name": "成都市", "longitude": 104.0665 },
|
||||
"510700": { "name": "绵阳市", "longitude": 104.6796 }
|
||||
"330100": {
|
||||
"name": "杭州市",
|
||||
"longitude": 120.1551
|
||||
},
|
||||
"330200": {
|
||||
"name": "宁波市",
|
||||
"longitude": 121.5503
|
||||
},
|
||||
"330300": {
|
||||
"name": "温州市",
|
||||
"longitude": 120.6994
|
||||
},
|
||||
"330400": {
|
||||
"name": "嘉兴市",
|
||||
"longitude": 120.7555
|
||||
},
|
||||
"330700": {
|
||||
"name": "金华市",
|
||||
"longitude": 119.6474
|
||||
}
|
||||
}
|
||||
},
|
||||
"420000": {
|
||||
"name": "湖北省",
|
||||
"longitude": 114.3419,
|
||||
"340000": {
|
||||
"name": "安徽省",
|
||||
"longitude": 117.283,
|
||||
"children": {
|
||||
"420100": { "name": "武汉市", "longitude": 114.3055 },
|
||||
"420500": { "name": "宜昌市", "longitude": 111.2865 }
|
||||
}
|
||||
"340100": {
|
||||
"name": "合肥市",
|
||||
"longitude": 117.283
|
||||
},
|
||||
"610000": {
|
||||
"name": "陕西省",
|
||||
"longitude": 108.9398,
|
||||
"children": {
|
||||
"610100": { "name": "西安市", "longitude": 108.9398 },
|
||||
"610300": { "name": "宝鸡市", "longitude": 107.2376 }
|
||||
}
|
||||
"340200": {
|
||||
"name": "芜湖市",
|
||||
"longitude": 118.4329
|
||||
},
|
||||
"370000": {
|
||||
"name": "山东省",
|
||||
"longitude": 117.0009,
|
||||
"children": {
|
||||
"370100": { "name": "济南市", "longitude": 117.1205 },
|
||||
"370200": { "name": "青岛市", "longitude": 120.3826 },
|
||||
"370300": { "name": "淄博市", "longitude": 118.0550 },
|
||||
"370600": { "name": "烟台市", "longitude": 121.4479 },
|
||||
"370700": { "name": "潍坊市", "longitude": 119.1619 },
|
||||
"371300": { "name": "临沂市", "longitude": 118.3565 }
|
||||
}
|
||||
"340300": {
|
||||
"name": "蚌埠市",
|
||||
"longitude": 117.3889
|
||||
},
|
||||
"430000": {
|
||||
"name": "湖南省",
|
||||
"longitude": 112.9834,
|
||||
"children": {
|
||||
"430100": { "name": "长沙市", "longitude": 112.9388 },
|
||||
"430200": { "name": "株洲市", "longitude": 113.1340 }
|
||||
"341200": {
|
||||
"name": "阜阳市",
|
||||
"longitude": 115.8142
|
||||
}
|
||||
},
|
||||
"500000": {
|
||||
"name": "重庆市",
|
||||
"longitude": 106.5516,
|
||||
"children": {
|
||||
"500103": { "name": "渝中区", "longitude": 106.5629 },
|
||||
"500112": { "name": "渝北区", "longitude": 106.6304 }
|
||||
}
|
||||
},
|
||||
"350000": {
|
||||
"name": "福建省",
|
||||
"longitude": 119.2965,
|
||||
"children": {
|
||||
"350100": { "name": "福州市", "longitude": 119.2965 },
|
||||
"350200": { "name": "厦门市", "longitude": 118.0894 }
|
||||
"350100": {
|
||||
"name": "福州市",
|
||||
"longitude": 119.2965
|
||||
},
|
||||
"350200": {
|
||||
"name": "厦门市",
|
||||
"longitude": 118.0894
|
||||
},
|
||||
"350500": {
|
||||
"name": "泉州市",
|
||||
"longitude": 118.6757
|
||||
},
|
||||
"350600": {
|
||||
"name": "漳州市",
|
||||
"longitude": 117.6471
|
||||
}
|
||||
}
|
||||
},
|
||||
"360000": {
|
||||
"name": "江西省",
|
||||
"longitude": 115.9092,
|
||||
"children": {
|
||||
"360100": {
|
||||
"name": "南昌市",
|
||||
"longitude": 115.9092
|
||||
},
|
||||
"360400": {
|
||||
"name": "九江市",
|
||||
"longitude": 115.9928
|
||||
},
|
||||
"360700": {
|
||||
"name": "赣州市",
|
||||
"longitude": 114.935
|
||||
},
|
||||
"360900": {
|
||||
"name": "宜春市",
|
||||
"longitude": 114.4168
|
||||
}
|
||||
}
|
||||
},
|
||||
"370000": {
|
||||
"name": "山东省",
|
||||
"longitude": 117.0009,
|
||||
"children": {
|
||||
"370100": {
|
||||
"name": "济南市",
|
||||
"longitude": 117.1205
|
||||
},
|
||||
"370200": {
|
||||
"name": "青岛市",
|
||||
"longitude": 120.3826
|
||||
},
|
||||
"370300": {
|
||||
"name": "淄博市",
|
||||
"longitude": 118.055
|
||||
},
|
||||
"370600": {
|
||||
"name": "烟台市",
|
||||
"longitude": 121.4479
|
||||
},
|
||||
"370700": {
|
||||
"name": "潍坊市",
|
||||
"longitude": 119.1619
|
||||
},
|
||||
"371300": {
|
||||
"name": "临沂市",
|
||||
"longitude": 118.3565
|
||||
},
|
||||
"371400": {
|
||||
"name": "德州市",
|
||||
"longitude": 116.3575
|
||||
},
|
||||
"371500": {
|
||||
"name": "聊城市",
|
||||
"longitude": 115.9855
|
||||
}
|
||||
}
|
||||
},
|
||||
"410000": {
|
||||
"name": "河南省",
|
||||
"longitude": 113.6254,
|
||||
"children": {
|
||||
"410100": {
|
||||
"name": "郑州市",
|
||||
"longitude": 113.6254
|
||||
},
|
||||
"410300": {
|
||||
"name": "洛阳市",
|
||||
"longitude": 112.454
|
||||
},
|
||||
"410700": {
|
||||
"name": "新乡市",
|
||||
"longitude": 113.9268
|
||||
},
|
||||
"411300": {
|
||||
"name": "南阳市",
|
||||
"longitude": 112.5288
|
||||
},
|
||||
"411400": {
|
||||
"name": "商丘市",
|
||||
"longitude": 115.6564
|
||||
}
|
||||
}
|
||||
},
|
||||
"420000": {
|
||||
"name": "湖北省",
|
||||
"longitude": 114.3419,
|
||||
"children": {
|
||||
"420100": {
|
||||
"name": "武汉市",
|
||||
"longitude": 114.3055
|
||||
},
|
||||
"420500": {
|
||||
"name": "宜昌市",
|
||||
"longitude": 111.2865
|
||||
},
|
||||
"420600": {
|
||||
"name": "襄阳市",
|
||||
"longitude": 112.1226
|
||||
},
|
||||
"421000": {
|
||||
"name": "荆州市",
|
||||
"longitude": 112.239
|
||||
}
|
||||
}
|
||||
},
|
||||
"430000": {
|
||||
"name": "湖南省",
|
||||
"longitude": 112.9834,
|
||||
"children": {
|
||||
"430100": {
|
||||
"name": "长沙市",
|
||||
"longitude": 112.9388
|
||||
},
|
||||
"430200": {
|
||||
"name": "株洲市",
|
||||
"longitude": 113.134
|
||||
},
|
||||
"430300": {
|
||||
"name": "湘潭市",
|
||||
"longitude": 112.944
|
||||
},
|
||||
"430600": {
|
||||
"name": "岳阳市",
|
||||
"longitude": 113.1289
|
||||
},
|
||||
"430700": {
|
||||
"name": "常德市",
|
||||
"longitude": 111.6985
|
||||
}
|
||||
}
|
||||
},
|
||||
"440000": {
|
||||
"name": "广东省",
|
||||
"longitude": 113.2665,
|
||||
"children": {
|
||||
"440100": {
|
||||
"name": "广州市",
|
||||
"longitude": 113.2644
|
||||
},
|
||||
"440300": {
|
||||
"name": "深圳市",
|
||||
"longitude": 114.0579
|
||||
},
|
||||
"440400": {
|
||||
"name": "珠海市",
|
||||
"longitude": 113.5765
|
||||
},
|
||||
"440600": {
|
||||
"name": "佛山市",
|
||||
"longitude": 113.1214
|
||||
},
|
||||
"441300": {
|
||||
"name": "惠州市",
|
||||
"longitude": 114.4162
|
||||
},
|
||||
"441900": {
|
||||
"name": "东莞市",
|
||||
"longitude": 113.7518
|
||||
},
|
||||
"442000": {
|
||||
"name": "中山市",
|
||||
"longitude": 113.3928
|
||||
}
|
||||
}
|
||||
},
|
||||
"450000": {
|
||||
"name": "广西壮族自治区",
|
||||
"longitude": 108.3275,
|
||||
"children": {
|
||||
"450100": {
|
||||
"name": "南宁市",
|
||||
"longitude": 108.3275
|
||||
},
|
||||
"450300": {
|
||||
"name": "桂林市",
|
||||
"longitude": 110.299
|
||||
},
|
||||
"450500": {
|
||||
"name": "北海市",
|
||||
"longitude": 109.1201
|
||||
},
|
||||
"450700": {
|
||||
"name": "钦州市",
|
||||
"longitude": 108.6544
|
||||
}
|
||||
}
|
||||
},
|
||||
"460000": {
|
||||
"name": "海南省",
|
||||
"longitude": 110.3492,
|
||||
"children": {
|
||||
"460100": {
|
||||
"name": "海口市",
|
||||
"longitude": 110.3492
|
||||
},
|
||||
"460200": {
|
||||
"name": "三亚市",
|
||||
"longitude": 109.5119
|
||||
},
|
||||
"469006": {
|
||||
"name": "万宁市",
|
||||
"longitude": 110.3911
|
||||
}
|
||||
}
|
||||
},
|
||||
"500000": {
|
||||
"name": "重庆市",
|
||||
"longitude": 106.5516,
|
||||
"children": {
|
||||
"500103": {
|
||||
"name": "渝中区",
|
||||
"longitude": 106.5629
|
||||
},
|
||||
"500106": {
|
||||
"name": "沙坪坝区",
|
||||
"longitude": 106.4569
|
||||
},
|
||||
"500112": {
|
||||
"name": "渝北区",
|
||||
"longitude": 106.6304
|
||||
},
|
||||
"500117": {
|
||||
"name": "合川区",
|
||||
"longitude": 106.2656
|
||||
}
|
||||
}
|
||||
},
|
||||
"510000": {
|
||||
"name": "四川省",
|
||||
"longitude": 104.0665,
|
||||
"children": {
|
||||
"510100": {
|
||||
"name": "成都市",
|
||||
"longitude": 104.0665
|
||||
},
|
||||
"510500": {
|
||||
"name": "泸州市",
|
||||
"longitude": 105.4433
|
||||
},
|
||||
"510700": {
|
||||
"name": "绵阳市",
|
||||
"longitude": 104.6796
|
||||
},
|
||||
"511300": {
|
||||
"name": "南充市",
|
||||
"longitude": 106.1107
|
||||
},
|
||||
"511500": {
|
||||
"name": "宜宾市",
|
||||
"longitude": 104.6432
|
||||
}
|
||||
}
|
||||
},
|
||||
"520000": {
|
||||
"name": "贵州省",
|
||||
"longitude": 106.7135,
|
||||
"children": {
|
||||
"520100": {
|
||||
"name": "贵阳市",
|
||||
"longitude": 106.7135
|
||||
},
|
||||
"520300": {
|
||||
"name": "遵义市",
|
||||
"longitude": 106.9274
|
||||
},
|
||||
"520500": {
|
||||
"name": "毕节市",
|
||||
"longitude": 105.285
|
||||
}
|
||||
}
|
||||
},
|
||||
"530000": {
|
||||
"name": "云南省",
|
||||
"longitude": 102.71,
|
||||
"children": {
|
||||
"530100": {
|
||||
"name": "昆明市",
|
||||
"longitude": 102.71
|
||||
},
|
||||
"530300": {
|
||||
"name": "曲靖市",
|
||||
"longitude": 103.7962
|
||||
},
|
||||
"532500": {
|
||||
"name": "红河州",
|
||||
"longitude": 103.384
|
||||
},
|
||||
"532900": {
|
||||
"name": "大理州",
|
||||
"longitude": 100.2257
|
||||
}
|
||||
}
|
||||
},
|
||||
"540000": {
|
||||
"name": "西藏自治区",
|
||||
"longitude": 91.1172,
|
||||
"children": {
|
||||
"540100": {
|
||||
"name": "拉萨市",
|
||||
"longitude": 91.1172
|
||||
},
|
||||
"540200": {
|
||||
"name": "日喀则市",
|
||||
"longitude": 88.8851
|
||||
}
|
||||
}
|
||||
},
|
||||
"610000": {
|
||||
"name": "陕西省",
|
||||
"longitude": 108.9398,
|
||||
"children": {
|
||||
"610100": {
|
||||
"name": "西安市",
|
||||
"longitude": 108.9398
|
||||
},
|
||||
"610300": {
|
||||
"name": "宝鸡市",
|
||||
"longitude": 107.2376
|
||||
},
|
||||
"610400": {
|
||||
"name": "咸阳市",
|
||||
"longitude": 108.7093
|
||||
},
|
||||
"610800": {
|
||||
"name": "榆林市",
|
||||
"longitude": 109.7346
|
||||
}
|
||||
}
|
||||
},
|
||||
"620000": {
|
||||
"name": "甘肃省",
|
||||
"longitude": 103.8343,
|
||||
"children": {
|
||||
"620100": {
|
||||
"name": "兰州市",
|
||||
"longitude": 103.8343
|
||||
},
|
||||
"620500": {
|
||||
"name": "天水市",
|
||||
"longitude": 105.7249
|
||||
},
|
||||
"620700": {
|
||||
"name": "张掖市",
|
||||
"longitude": 100.4498
|
||||
}
|
||||
}
|
||||
},
|
||||
"630000": {
|
||||
"name": "青海省",
|
||||
"longitude": 101.7801,
|
||||
"children": {
|
||||
"630100": {
|
||||
"name": "西宁市",
|
||||
"longitude": 101.7801
|
||||
},
|
||||
"632800": {
|
||||
"name": "海西州",
|
||||
"longitude": 97.3701
|
||||
}
|
||||
}
|
||||
},
|
||||
"640000": {
|
||||
"name": "宁夏回族自治区",
|
||||
"longitude": 106.2586,
|
||||
"children": {
|
||||
"640100": {
|
||||
"name": "银川市",
|
||||
"longitude": 106.2586
|
||||
},
|
||||
"640200": {
|
||||
"name": "石嘴山市",
|
||||
"longitude": 106.3833
|
||||
}
|
||||
}
|
||||
},
|
||||
"650000": {
|
||||
"name": "新疆维吾尔自治区",
|
||||
"longitude": 87.6177,
|
||||
"children": {
|
||||
"650100": {
|
||||
"name": "乌鲁木齐市",
|
||||
"longitude": 87.6177
|
||||
},
|
||||
"650200": {
|
||||
"name": "克拉玛依市",
|
||||
"longitude": 84.8739
|
||||
},
|
||||
"652900": {
|
||||
"name": "阿克苏地区",
|
||||
"longitude": 80.2606
|
||||
}
|
||||
}
|
||||
},
|
||||
"710000": {
|
||||
"name": "台湾省",
|
||||
"longitude": 121.5091,
|
||||
"children": {
|
||||
"710100": {
|
||||
"name": "台北市",
|
||||
"longitude": 121.5654
|
||||
},
|
||||
"710200": {
|
||||
"name": "高雄市",
|
||||
"longitude": 120.3014
|
||||
}
|
||||
}
|
||||
},
|
||||
"810000": {
|
||||
"name": "香港特别行政区",
|
||||
"longitude": 114.1694,
|
||||
"children": {
|
||||
"810001": {
|
||||
"name": "中西区",
|
||||
"longitude": 114.1544
|
||||
},
|
||||
"810012": {
|
||||
"name": "湾仔区",
|
||||
"longitude": 114.1829
|
||||
}
|
||||
}
|
||||
},
|
||||
"820000": {
|
||||
"name": "澳门特别行政区",
|
||||
"longitude": 113.5439,
|
||||
"children": {
|
||||
"820001": {
|
||||
"name": "花地玛堂区",
|
||||
"longitude": 113.5491
|
||||
},
|
||||
"820003": {
|
||||
"name": "大堂区",
|
||||
"longitude": 113.5536
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
* 生成 lib/data/regions.json — 全国省级 + 主要城市
|
||||
* 运行:node scripts/generate-regions.mjs
|
||||
*/
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import { fileURLToPath } from "url";
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
/** [code, name, lon, cities: [code, name, lon][]] */
|
||||
const DATA = [
|
||||
["110000", "北京市", 116.4074, [["110101", "东城区", 116.4164], ["110105", "朝阳区", 116.4434], ["110108", "海淀区", 116.2983], ["110114", "昌平区", 116.2312]]],
|
||||
["120000", "天津市", 117.2010, [["120101", "和平区", 117.2147], ["120103", "河西区", 117.2234], ["120110", "东丽区", 117.3143], ["120116", "滨海新区", 117.7105]]],
|
||||
["130000", "河北省", 114.5149, [["130100", "石家庄市", 114.5149], ["130200", "唐山市", 118.1802], ["130300", "秦皇岛市", 119.6005], ["130600", "保定市", 115.4648], ["131000", "廊坊市", 116.6838]]],
|
||||
["140000", "山西省", 112.5624, [["140100", "太原市", 112.5624], ["140200", "大同市", 113.3001], ["140500", "晋城市", 112.8513], ["140700", "晋中市", 112.7528]]],
|
||||
["150000", "内蒙古自治区", 111.7652, [["150100", "呼和浩特市", 111.7652], ["150200", "包头市", 109.8403], ["150400", "赤峰市", 118.8869], ["150500", "通辽市", 122.2434]]],
|
||||
["210000", "辽宁省", 123.4315, [["210100", "沈阳市", 123.4315], ["210200", "大连市", 121.6147], ["210300", "鞍山市", 122.9946], ["210600", "丹东市", 124.3545]]],
|
||||
["220000", "吉林省", 125.3235, [["220100", "长春市", 125.3235], ["220200", "吉林市", 126.5494], ["220300", "四平市", 124.3505], ["222400", "延边州", 129.5132]]],
|
||||
["230000", "黑龙江省", 126.6425, [["230100", "哈尔滨市", 126.6425], ["230600", "大庆市", 125.1031], ["231000", "牡丹江市", 129.6332], ["230200", "齐齐哈尔市", 123.9182]]],
|
||||
["310000", "上海市", 121.4737, [["310101", "黄浦区", 121.4903], ["310104", "徐汇区", 121.4365], ["310115", "浦东新区", 121.5447], ["310117", "松江区", 121.2277]]],
|
||||
["320000", "江苏省", 118.7969, [["320100", "南京市", 118.7969], ["320200", "无锡市", 120.3119], ["320500", "苏州市", 120.5853], ["320300", "徐州市", 117.1848], ["320600", "南通市", 120.8945]]],
|
||||
["330000", "浙江省", 120.1536, [["330100", "杭州市", 120.1551], ["330200", "宁波市", 121.5503], ["330300", "温州市", 120.6994], ["330400", "嘉兴市", 120.7555], ["330700", "金华市", 119.6474]]],
|
||||
["340000", "安徽省", 117.2830, [["340100", "合肥市", 117.2830], ["340200", "芜湖市", 118.4329], ["340300", "蚌埠市", 117.3889], ["341200", "阜阳市", 115.8142]]],
|
||||
["350000", "福建省", 119.2965, [["350100", "福州市", 119.2965], ["350200", "厦门市", 118.0894], ["350500", "泉州市", 118.6757], ["350600", "漳州市", 117.6471]]],
|
||||
["360000", "江西省", 115.9092, [["360100", "南昌市", 115.9092], ["360400", "九江市", 115.9928], ["360700", "赣州市", 114.9350], ["360900", "宜春市", 114.4168]]],
|
||||
["370000", "山东省", 117.0009, [["370100", "济南市", 117.1205], ["370200", "青岛市", 120.3826], ["370300", "淄博市", 118.0550], ["370600", "烟台市", 121.4479], ["370700", "潍坊市", 119.1619], ["371300", "临沂市", 118.3565], ["371400", "德州市", 116.3575], ["371500", "聊城市", 115.9855]]],
|
||||
["410000", "河南省", 113.6254, [["410100", "郑州市", 113.6254], ["410300", "洛阳市", 112.4540], ["410700", "新乡市", 113.9268], ["411300", "南阳市", 112.5288], ["411400", "商丘市", 115.6564]]],
|
||||
["420000", "湖北省", 114.3419, [["420100", "武汉市", 114.3055], ["420500", "宜昌市", 111.2865], ["420600", "襄阳市", 112.1226], ["421000", "荆州市", 112.2390]]],
|
||||
["430000", "湖南省", 112.9834, [["430100", "长沙市", 112.9388], ["430200", "株洲市", 113.1340], ["430300", "湘潭市", 112.9440], ["430600", "岳阳市", 113.1289], ["430700", "常德市", 111.6985]]],
|
||||
["440000", "广东省", 113.2665, [["440100", "广州市", 113.2644], ["440300", "深圳市", 114.0579], ["440400", "珠海市", 113.5765], ["440600", "佛山市", 113.1214], ["441300", "惠州市", 114.4162], ["441900", "东莞市", 113.7518], ["442000", "中山市", 113.3928]]],
|
||||
["450000", "广西壮族自治区", 108.3275, [["450100", "南宁市", 108.3275], ["450300", "桂林市", 110.2990], ["450500", "北海市", 109.1201], ["450700", "钦州市", 108.6544]]],
|
||||
["460000", "海南省", 110.3492, [["460100", "海口市", 110.3492], ["460200", "三亚市", 109.5119], ["469006", "万宁市", 110.3911]]],
|
||||
["500000", "重庆市", 106.5516, [["500103", "渝中区", 106.5629], ["500112", "渝北区", 106.6304], ["500106", "沙坪坝区", 106.4569], ["500117", "合川区", 106.2656]]],
|
||||
["510000", "四川省", 104.0665, [["510100", "成都市", 104.0665], ["510700", "绵阳市", 104.6796], ["511300", "南充市", 106.1107], ["511500", "宜宾市", 104.6432], ["510500", "泸州市", 105.4433]]],
|
||||
["520000", "贵州省", 106.7135, [["520100", "贵阳市", 106.7135], ["520300", "遵义市", 106.9274], ["520500", "毕节市", 105.2850]]],
|
||||
["530000", "云南省", 102.7100, [["530100", "昆明市", 102.7100], ["530300", "曲靖市", 103.7962], ["532900", "大理州", 100.2257], ["532500", "红河州", 103.3840]]],
|
||||
["540000", "西藏自治区", 91.1172, [["540100", "拉萨市", 91.1172], ["540200", "日喀则市", 88.8851]]],
|
||||
["610000", "陕西省", 108.9398, [["610100", "西安市", 108.9398], ["610300", "宝鸡市", 107.2376], ["610400", "咸阳市", 108.7093], ["610800", "榆林市", 109.7346]]],
|
||||
["620000", "甘肃省", 103.8343, [["620100", "兰州市", 103.8343], ["620500", "天水市", 105.7249], ["620700", "张掖市", 100.4498]]],
|
||||
["630000", "青海省", 101.7801, [["630100", "西宁市", 101.7801], ["632800", "海西州", 97.3701]]],
|
||||
["640000", "宁夏回族自治区", 106.2586, [["640100", "银川市", 106.2586], ["640200", "石嘴山市", 106.3833]]],
|
||||
["650000", "新疆维吾尔自治区", 87.6177, [["650100", "乌鲁木齐市", 87.6177], ["650200", "克拉玛依市", 84.8739], ["652900", "阿克苏地区", 80.2606]]],
|
||||
["710000", "台湾省", 121.5091, [["710100", "台北市", 121.5654], ["710200", "高雄市", 120.3014]]],
|
||||
["810000", "香港特别行政区", 114.1694, [["810001", "中西区", 114.1544], ["810012", "湾仔区", 114.1829]]],
|
||||
["820000", "澳门特别行政区", 113.5439, [["820001", "花地玛堂区", 113.5491], ["820003", "大堂区", 113.5536]]],
|
||||
];
|
||||
|
||||
const regions = {};
|
||||
for (const [pCode, pName, pLon, cities] of DATA) {
|
||||
regions[pCode] = {
|
||||
name: pName,
|
||||
longitude: pLon,
|
||||
children: Object.fromEntries(
|
||||
cities.map(([cCode, cName, cLon]) => [cCode, { name: cName, longitude: cLon }]),
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
const out = path.join(__dirname, "../lib/data/regions.json");
|
||||
fs.writeFileSync(out, JSON.stringify(regions, null, 2) + "\n", "utf-8");
|
||||
console.log("Wrote", out, "provinces:", Object.keys(regions).length);
|
||||
@@ -84,6 +84,14 @@ module.exports = {
|
||||
animation: {
|
||||
"accordion-down": "accordion-down 0.2s ease-out",
|
||||
"accordion-up": "accordion-up 0.2s ease-out",
|
||||
"coin-front-front":
|
||||
"coin-front-front 3.8s cubic-bezier(0.645,0.045,0.355,1) forwards",
|
||||
"coin-front-back":
|
||||
"coin-front-back 3.8s cubic-bezier(0.645,0.045,0.355,1) forwards",
|
||||
"coin-back-front":
|
||||
"coin-back-front 3.8s cubic-bezier(0.645,0.045,0.355,1) forwards",
|
||||
"coin-back-back":
|
||||
"coin-back-back 3.8s cubic-bezier(0.645,0.045,0.355,1) forwards",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user