From d7bea9aed53a198a6f8b48c291cdc6e51c86b451 Mon Sep 17 00:00:00 2001 From: manpengan Date: Sat, 28 Mar 2026 23:37:17 +0800 Subject: [PATCH] =?UTF-8?q?nav:=20=E5=85=A8=E9=87=8F=20world-catalog=20+?= =?UTF-8?q?=20status=20active/planned=20=E5=88=86=E5=B1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - world-catalog.js: 6洲48国127城市全量 NavNode catalog - 统一 schema(isEnabled → status: active|planned) - 64 个 NavNode 覆盖所有洲/国家/地区 - runtime-nav.js: 从 catalog 自动过滤 active 节点 - MVP_OVERRIDES 机制跳过国家层直连城市 - V1.1+ 删除覆写即恢复完整层级 - 删除 future-catalog.js,旧 shim 指向 world-catalog - game-design 2.3 更新 schema + Framework Ready 说明 Co-Authored-By: Claude Opus 4.6 (1M context) --- docs/02-game-design.md | 8 +- js/content/continents/asia.js | 5 +- js/content/countries/japan.js | 6 +- js/content/navigation/future-catalog.js | 50 -------- js/content/navigation/nav-schema.js | 6 +- js/content/navigation/runtime-nav.js | 118 ++++------------- js/content/navigation/world-catalog.js | 164 ++++++++++++++++++++++++ js/content/regions/china.js | 5 +- 8 files changed, 207 insertions(+), 155 deletions(-) delete mode 100644 js/content/navigation/future-catalog.js create mode 100644 js/content/navigation/world-catalog.js diff --git a/docs/02-game-design.md b/docs/02-game-design.md index 7defbde..f790bb5 100644 --- a/docs/02-game-design.md +++ b/docs/02-game-design.md @@ -160,17 +160,19 @@ MVP 只开放亚洲 6 城市,直入城市选择页,不启用完整多级导 | `childType` | string\|null | 子节点类型:`country` / `region` / `city` / null | | `childIds` | string[] | 当前可消费的子节点 id(仅已启用的) | | `pageSize` | number | 每页格子数,默认 9 | -| `isEnabled` | boolean | 当前版本是否启用 | +| `status` | string | `'active'`(当前可运行)/ `'planned'`(未来规划) | | `isUnlockedByDefault` | boolean | 是否默认解锁 | 分页通过 `shared/pagination.js` 的 `paginate(childIds, pageSize)` 动态计算,不手写 page 常量。 +Framework Ready: 全量 6 洲 48 国 127 城市的 catalog 已搭好,扩城市只需加 CityManifest + 改 status 为 active。 + **数据文件分层:** | 文件 | 用途 | 是否进入 runtime | |------|------|-----------------| -| `navigation/runtime-nav.js` | 当前版本可消费的导航节点 | ✅ 是 | -| `navigation/future-catalog.js` | 未来版本城市/国家/地区规划 | ❌ 否 | +| `navigation/runtime-nav.js` | 当前版本可消费的导航节点(从 world-catalog 自动过滤) | ✅ 是 | +| `navigation/world-catalog.js` | 全量世界导航规划(含 active + planned) | ✅ 是(runtime-nav 从中过滤 active 节点) | | `navigation/nav-schema.js` | NavNode schema 定义 + 验证 | ✅ 是(类型定义) | | `docs/city-roadmap.md` | 全量 127 城市路线图 | ❌ 否 | diff --git a/js/content/continents/asia.js b/js/content/continents/asia.js index 211d028..93a13e2 100644 --- a/js/content/continents/asia.js +++ b/js/content/continents/asia.js @@ -1,5 +1,6 @@ /** - * @deprecated 亚洲国家列表已移入 navigation/future-catalog.js + * @deprecated 亚洲国家列表已移入 navigation/world-catalog.js * MVP 阶段亚洲直接列出城市,不经过国家层。 */ -export { asiaCountryPlan as asiaCountries } from '../navigation/future-catalog.js' +import { getCatalogChildren } from '../navigation/world-catalog.js' +export const asiaCountries = getCatalogChildren('asia') diff --git a/js/content/countries/japan.js b/js/content/countries/japan.js index 6ed4cbe..044b187 100644 --- a/js/content/countries/japan.js +++ b/js/content/countries/japan.js @@ -1,6 +1,6 @@ /** - * @deprecated 国家城市列表已移入 navigation/future-catalog.js + * @deprecated 国家城市列表已移入 navigation/world-catalog.js */ -import { asiaCountryPlan } from '../navigation/future-catalog.js' -export const japanCities = asiaCountryPlan.find(c => c.id === 'japan') +import { getCatalogNode } from '../navigation/world-catalog.js' +export const japanCities = getCatalogNode('japan') export default japanCities diff --git a/js/content/navigation/future-catalog.js b/js/content/navigation/future-catalog.js deleted file mode 100644 index 8f403cd..0000000 --- a/js/content/navigation/future-catalog.js +++ /dev/null @@ -1,50 +0,0 @@ -/** - * Future Catalog — 未来版本导航规划 - * - * 这些节点不进入当前 runtime 消费链路。 - * 当新版本上线新城市时,将对应节点移入 runtime-nav.js 并设 isEnabled: true。 - * 引用的 cityId 可能尚不存在对应 CityManifest。 - */ - -// V1.1: 中国一线城市扩展 -export const v11_china_cities = ['shanghai', 'guangzhou', 'chengdu', 'shenzhen', 'hangzhou', 'chongqing'] - -// V1.2: 中国二线 A -export const v12_china_cities = ['wuhan', 'xian', 'changsha', 'nanjing', 'harbin', 'xiamen'] - -// V1.3: 欧洲热门 -export const v13_europe_cities = ['paris', 'london', 'rome', 'barcelona', 'prague', 'amsterdam'] - -// 中国地区划分(V1.1+ 启用国家→地区→城市导航时使用) -export const chinaRegionPlan = [ - { id: 'north_china', name: '华北', cityIds: ['beijing', 'tianjin', 'shijiazhuang', 'taiyuan', 'hohhot'] }, - { id: 'northeast', name: '东北', cityIds: ['shenyang', 'changchun', 'harbin', 'dalian'] }, - { id: 'east_china', name: '华东', cityIds: ['shanghai', 'nanjing', 'hangzhou', 'hefei', 'fuzhou', 'nanchang', 'jinan', 'suzhou', 'xiamen', 'qingdao', 'ningbo'] }, - { id: 'central_china', name: '华中', cityIds: ['zhengzhou', 'wuhan', 'changsha'] }, - { id: 'south_china', name: '华南', cityIds: ['guangzhou', 'shenzhen', 'nanning', 'haikou', 'sanya'] }, - { id: 'southwest', name: '西南', cityIds: ['chengdu', 'chongqing', 'guiyang', 'kunming', 'lhasa'] }, - { id: 'northwest', name: '西北', cityIds: ['xian', 'lanzhou', 'xining', 'yinchuan', 'urumqi'] }, - { id: 'hk_macao_tw', name: '港澳台', cityIds: ['hongkong', 'macao', 'taipei'] }, -] - -// 全部亚洲国家(V1.1+ 启用) -export const asiaCountryPlan = [ - { id: 'china', name: '中国', hasRegions: true, totalCities: 41 }, - { id: 'japan', name: '日本', cityIds: ['tokyo', 'osaka', 'kyoto', 'sapporo'] }, - { id: 'korea', name: '韩国', cityIds: ['seoul', 'busan', 'jeju'] }, - { id: 'thailand', name: '泰国', cityIds: ['bangkok', 'chiangmai', 'phuket'] }, - { id: 'singapore_country', name: '新加坡', cityIds: ['singapore'] }, - { id: 'vietnam', name: '越南', cityIds: ['hanoi', 'hochiminh'] }, - { id: 'malaysia', name: '马来西亚', cityIds: ['kualalumpur', 'malacca'] }, - { id: 'indonesia', name: '印度尼西亚', cityIds: ['jakarta', 'bali'] }, - { id: 'philippines', name: '菲律宾', cityIds: ['manila'] }, - { id: 'india', name: '印度', cityIds: ['delhi', 'mumbai'] }, - { id: 'uae', name: '阿联酋', cityIds: ['dubai'] }, - { id: 'turkey', name: '土耳其', cityIds: ['istanbul'] }, - { id: 'israel', name: '以色列', cityIds: ['jerusalem'] }, - { id: 'nepal', name: '尼泊尔', cityIds: ['kathmandu'] }, - { id: 'cambodia', name: '柬埔寨', cityIds: ['siemreap'] }, - { id: 'sri_lanka', name: '斯里兰卡', cityIds: ['colombo'] }, - { id: 'myanmar', name: '缅甸', cityIds: ['yangon'] }, - { id: 'mongolia', name: '蒙古', cityIds: ['ulaanbaatar'] }, -] diff --git a/js/content/navigation/nav-schema.js b/js/content/navigation/nav-schema.js index 37937c5..2f16afb 100644 --- a/js/content/navigation/nav-schema.js +++ b/js/content/navigation/nav-schema.js @@ -15,15 +15,16 @@ * @property {'country'|'region'|'city'|null} childType - 子节点类型 * @property {string[]} childIds - 当前可消费的子节点 id(仅已启用的) * @property {number} pageSize - 每页格子数,默认 9 - * @property {boolean} isEnabled - 当前版本是否启用 + * @property {'active'|'planned'} status - 'active'(当前可运行)/ 'planned'(未来规划) * @property {boolean} isUnlockedByDefault - 是否默认解锁 */ // schema 验证函数 export function validateNavNode(node) { - const required = ['type', 'id', 'name', 'nameEn', 'sortOrder', 'themeColor', 'childType', 'childIds', 'pageSize', 'isEnabled', 'isUnlockedByDefault'] + const required = ['type', 'id', 'name', 'nameEn', 'sortOrder', 'themeColor', 'childType', 'childIds', 'pageSize', 'status', 'isUnlockedByDefault'] const validTypes = ['continent', 'country', 'region', 'city-group'] const validChildTypes = ['country', 'region', 'city', null] + const validStatuses = ['active', 'planned'] const errors = [] for (const key of required) { @@ -33,5 +34,6 @@ export function validateNavNode(node) { if (!validChildTypes.includes(node.childType)) errors.push(`invalid childType: ${node.childType}`) if (!Array.isArray(node.childIds)) errors.push('childIds must be array') if (node.parentId !== null && typeof node.parentId !== 'string') errors.push('parentId must be string or null') + if (!validStatuses.includes(node.status)) errors.push(`invalid status: ${node.status}, must be 'active' or 'planned'`) return errors } diff --git a/js/content/navigation/runtime-nav.js b/js/content/navigation/runtime-nav.js index 76ee479..2401dff 100644 --- a/js/content/navigation/runtime-nav.js +++ b/js/content/navigation/runtime-nav.js @@ -1,110 +1,42 @@ /** - * Runtime Navigation — MVP + * Runtime Navigation — 从 world-catalog 自动生成 * - * 只包含当前版本已启用、childIds 均指向已存在实体的节点。 - * MVP 导航:直入亚洲城市页,6 城市平铺。 - * V1.1+ 启用完整洲→国家→地区→城市多级导航时,在此文件追加节点。 + * 只包含 status === 'active' 的节点。 + * MVP 阶段手动覆写亚洲的 childType 为 'city'(跳过国家层), + * V1.1+ 切换为 catalog 默认的 'country'。 */ -export const runtimeNavNodes = [ - // ── MVP: 只有 1 个洲,直接列出 6 城市 ── - { - type: 'continent', - id: 'asia', - parentId: null, - name: '亚洲', - nameEn: 'Asia', - sortOrder: 1, - themeColor: '#FF6B6B', - childType: 'city', // MVP: 直接指向城市,跳过国家层 +import { worldCatalog } from './world-catalog.js' + +// MVP 覆写:亚洲直接指向城市,不经过国家层 +const MVP_OVERRIDES = { + asia: { + childType: 'city', childIds: ['beijing', 'tokyo', 'bangkok', 'seoul', 'singapore', 'istanbul'], - pageSize: 9, - isEnabled: true, - isUnlockedByDefault: true, }, +} - // ── 未启用的洲(占位,UI 显示锁定态) ── - { - type: 'continent', - id: 'europe', - parentId: null, - name: '欧洲', - nameEn: 'Europe', - sortOrder: 2, - themeColor: '#6B8CFF', - childType: 'country', - childIds: [], // 无可消费子节点 - pageSize: 9, - isEnabled: false, - isUnlockedByDefault: false, - }, - { - type: 'continent', - id: 'north_america', - parentId: null, - name: '北美洲', - nameEn: 'North America', - sortOrder: 3, - themeColor: '#FFB347', - childType: 'country', - childIds: [], - pageSize: 9, - isEnabled: false, - isUnlockedByDefault: false, - }, - { - type: 'continent', - id: 'south_america', - parentId: null, - name: '南美洲', - nameEn: 'South America', - sortOrder: 4, - themeColor: '#4ECDC4', - childType: 'country', - childIds: [], - pageSize: 9, - isEnabled: false, - isUnlockedByDefault: false, - }, - { - type: 'continent', - id: 'africa', - parentId: null, - name: '非洲', - nameEn: 'Africa', - sortOrder: 5, - themeColor: '#F7DC6F', - childType: 'country', - childIds: [], - pageSize: 9, - isEnabled: false, - isUnlockedByDefault: false, - }, - { - type: 'continent', - id: 'oceania', - parentId: null, - name: '大洋洲', - nameEn: 'Oceania', - sortOrder: 6, - themeColor: '#82E0AA', - childType: 'country', - childIds: [], - pageSize: 9, - isEnabled: false, - isUnlockedByDefault: false, - }, -] +function buildRuntimeNav() { + const activeNodes = worldCatalog.filter(n => n.status === 'active') + return activeNodes.map(node => { + const override = MVP_OVERRIDES[node.id] + if (override) { + return { ...node, ...override } + } + return { ...node } + }) +} + +export const runtimeNavNodes = buildRuntimeNav() -// ── 便捷查询 ── export function getNavNode(id) { return runtimeNavNodes.find(n => n.id === id) || null } export function getEnabledRoots() { - return runtimeNavNodes.filter(n => n.parentId === null && n.isEnabled) + return runtimeNavNodes.filter(n => n.parentId === null) } export function getChildren(parentId) { - return runtimeNavNodes.filter(n => n.parentId === parentId && n.isEnabled) + return runtimeNavNodes.filter(n => n.parentId === parentId) } diff --git a/js/content/navigation/world-catalog.js b/js/content/navigation/world-catalog.js new file mode 100644 index 0000000..4172bfc --- /dev/null +++ b/js/content/navigation/world-catalog.js @@ -0,0 +1,164 @@ +/** + * World Catalog — 全量世界导航规划 + * + * 所有节点使用统一 NavNode schema。 + * status: 'active' = 当前版本可运行(有对应真实内容) + * status: 'planned' = 未来版本规划(无对应内容,不进入 runtime) + * + * 扩展新城市时: + * 1. 创建城市 CityManifest 文件 + * 2. 将对应节点 status 改为 'active' + * 3. runtime-nav.js 会自动过滤出 active 节点 + */ + +export const worldCatalog = [ + // ═══════════════════════════════════════ + // 洲级 + // ═══════════════════════════════════════ + { + type: 'continent', id: 'asia', parentId: null, + name: '亚洲', nameEn: 'Asia', sortOrder: 1, themeColor: '#FF6B6B', + childType: 'country', + childIds: ['china', 'japan', 'korea', 'thailand', 'singapore_country', 'vietnam', 'malaysia', 'indonesia', 'philippines', 'india', 'uae', 'turkey', 'israel', 'nepal', 'cambodia', 'sri_lanka', 'myanmar', 'mongolia'], + pageSize: 9, status: 'active', isUnlockedByDefault: true, + }, + { + type: 'continent', id: 'europe', parentId: null, + name: '欧洲', nameEn: 'Europe', sortOrder: 2, themeColor: '#6B8CFF', + childType: 'country', + childIds: ['france', 'uk', 'italy', 'spain', 'germany', 'netherlands', 'switzerland', 'austria', 'czech', 'greece', 'russia', 'portugal', 'norway', 'sweden', 'denmark', 'finland', 'iceland', 'hungary'], + pageSize: 9, status: 'planned', isUnlockedByDefault: false, + }, + { + type: 'continent', id: 'north_america', parentId: null, + name: '北美洲', nameEn: 'North America', sortOrder: 3, themeColor: '#FFB347', + childType: 'country', + childIds: ['usa', 'canada', 'mexico'], + pageSize: 9, status: 'planned', isUnlockedByDefault: false, + }, + { + type: 'continent', id: 'south_america', parentId: null, + name: '南美洲', nameEn: 'South America', sortOrder: 4, themeColor: '#4ECDC4', + childType: 'country', + childIds: ['brazil', 'argentina', 'peru', 'chile', 'colombia'], + pageSize: 9, status: 'planned', isUnlockedByDefault: false, + }, + { + type: 'continent', id: 'africa', parentId: null, + name: '非洲', nameEn: 'Africa', sortOrder: 5, themeColor: '#F7DC6F', + childType: 'country', + childIds: ['egypt', 'south_africa', 'morocco', 'kenya'], + pageSize: 9, status: 'planned', isUnlockedByDefault: false, + }, + { + type: 'continent', id: 'oceania', parentId: null, + name: '大洋洲', nameEn: 'Oceania', sortOrder: 6, themeColor: '#82E0AA', + childType: 'country', + childIds: ['australia', 'new_zealand'], + pageSize: 9, status: 'planned', isUnlockedByDefault: false, + }, + + // ═══════════════════════════════════════ + // 亚洲国家(18 国) + // ═══════════════════════════════════════ + // MVP active: 中国/日本/韩国/泰国/新加坡/土耳其各有 1 个 active 城市 + { type: 'country', id: 'china', parentId: 'asia', name: '中国', nameEn: 'China', sortOrder: 1, themeColor: '#CC2936', childType: 'region', childIds: ['north_china', 'northeast', 'east_china', 'central_china', 'south_china', 'southwest', 'northwest', 'hk_macao_tw'], pageSize: 9, status: 'planned', isUnlockedByDefault: false }, + { type: 'country', id: 'japan', parentId: 'asia', name: '日本', nameEn: 'Japan', sortOrder: 2, themeColor: '#E84057', childType: 'city', childIds: ['tokyo', 'osaka', 'kyoto', 'sapporo'], pageSize: 9, status: 'planned', isUnlockedByDefault: false }, + { type: 'country', id: 'korea', parentId: 'asia', name: '韩国', nameEn: 'South Korea', sortOrder: 3, themeColor: '#4A90D9', childType: 'city', childIds: ['seoul', 'busan', 'jeju'], pageSize: 9, status: 'planned', isUnlockedByDefault: false }, + { type: 'country', id: 'thailand', parentId: 'asia', name: '泰国', nameEn: 'Thailand', sortOrder: 4, themeColor: '#FFB347', childType: 'city', childIds: ['bangkok', 'chiangmai', 'phuket'], pageSize: 9, status: 'planned', isUnlockedByDefault: false }, + { type: 'country', id: 'singapore_country', parentId: 'asia', name: '新加坡', nameEn: 'Singapore', sortOrder: 5, themeColor: '#00A896', childType: 'city', childIds: ['singapore'], pageSize: 9, status: 'planned', isUnlockedByDefault: false }, + { type: 'country', id: 'vietnam', parentId: 'asia', name: '越南', nameEn: 'Vietnam', sortOrder: 6, themeColor: '#E74C3C', childType: 'city', childIds: ['hanoi', 'hochiminh'], pageSize: 9, status: 'planned', isUnlockedByDefault: false }, + { type: 'country', id: 'malaysia', parentId: 'asia', name: '马来西亚', nameEn: 'Malaysia', sortOrder: 7, themeColor: '#3498DB', childType: 'city', childIds: ['kualalumpur', 'malacca'], pageSize: 9, status: 'planned', isUnlockedByDefault: false }, + { type: 'country', id: 'indonesia', parentId: 'asia', name: '印度尼西亚', nameEn: 'Indonesia', sortOrder: 8, themeColor: '#E67E22', childType: 'city', childIds: ['jakarta', 'bali'], pageSize: 9, status: 'planned', isUnlockedByDefault: false }, + { type: 'country', id: 'philippines', parentId: 'asia', name: '菲律宾', nameEn: 'Philippines', sortOrder: 9, themeColor: '#2ECC71', childType: 'city', childIds: ['manila'], pageSize: 9, status: 'planned', isUnlockedByDefault: false }, + { type: 'country', id: 'india', parentId: 'asia', name: '印度', nameEn: 'India', sortOrder: 10, themeColor: '#FF9933', childType: 'city', childIds: ['delhi', 'mumbai'], pageSize: 9, status: 'planned', isUnlockedByDefault: false }, + { type: 'country', id: 'uae', parentId: 'asia', name: '阿联酋', nameEn: 'UAE', sortOrder: 11, themeColor: '#C0A062', childType: 'city', childIds: ['dubai'], pageSize: 9, status: 'planned', isUnlockedByDefault: false }, + { type: 'country', id: 'turkey', parentId: 'asia', name: '土耳其', nameEn: 'Turkey', sortOrder: 12, themeColor: '#1A5276', childType: 'city', childIds: ['istanbul'], pageSize: 9, status: 'planned', isUnlockedByDefault: false }, + { type: 'country', id: 'israel', parentId: 'asia', name: '以色列', nameEn: 'Israel', sortOrder: 13, themeColor: '#5DADE2', childType: 'city', childIds: ['jerusalem'], pageSize: 9, status: 'planned', isUnlockedByDefault: false }, + { type: 'country', id: 'nepal', parentId: 'asia', name: '尼泊尔', nameEn: 'Nepal', sortOrder: 14, themeColor: '#DC143C', childType: 'city', childIds: ['kathmandu'], pageSize: 9, status: 'planned', isUnlockedByDefault: false }, + { type: 'country', id: 'cambodia', parentId: 'asia', name: '柬埔寨', nameEn: 'Cambodia', sortOrder: 15, themeColor: '#1E8449', childType: 'city', childIds: ['siemreap'], pageSize: 9, status: 'planned', isUnlockedByDefault: false }, + { type: 'country', id: 'sri_lanka', parentId: 'asia', name: '斯里兰卡', nameEn: 'Sri Lanka', sortOrder: 16, themeColor: '#8E44AD', childType: 'city', childIds: ['colombo'], pageSize: 9, status: 'planned', isUnlockedByDefault: false }, + { type: 'country', id: 'myanmar', parentId: 'asia', name: '缅甸', nameEn: 'Myanmar', sortOrder: 17, themeColor: '#F4D03F', childType: 'city', childIds: ['yangon'], pageSize: 9, status: 'planned', isUnlockedByDefault: false }, + { type: 'country', id: 'mongolia', parentId: 'asia', name: '蒙古', nameEn: 'Mongolia', sortOrder: 18, themeColor: '#2980B9', childType: 'city', childIds: ['ulaanbaatar'], pageSize: 9, status: 'planned', isUnlockedByDefault: false }, + + // ═══════════════════════════════════════ + // 中国地区(8 区) + // ═══════════════════════════════════════ + { type: 'region', id: 'north_china', parentId: 'china', name: '华北', nameEn: 'North China', sortOrder: 1, themeColor: '#CC2936', childType: 'city', childIds: ['beijing', 'tianjin', 'shijiazhuang', 'taiyuan', 'hohhot'], pageSize: 9, status: 'planned', isUnlockedByDefault: false }, + { type: 'region', id: 'northeast', parentId: 'china', name: '东北', nameEn: 'Northeast', sortOrder: 2, themeColor: '#5DADE2', childType: 'city', childIds: ['shenyang', 'changchun', 'harbin', 'dalian'], pageSize: 9, status: 'planned', isUnlockedByDefault: false }, + { type: 'region', id: 'east_china', parentId: 'china', name: '华东', nameEn: 'East China', sortOrder: 3, themeColor: '#2ECC71', childType: 'city', childIds: ['shanghai', 'nanjing', 'hangzhou', 'hefei', 'fuzhou', 'nanchang', 'jinan', 'suzhou', 'xiamen', 'qingdao', 'ningbo'], pageSize: 9, status: 'planned', isUnlockedByDefault: false }, + { type: 'region', id: 'central_china', parentId: 'china', name: '华中', nameEn: 'Central China', sortOrder: 4, themeColor: '#E67E22', childType: 'city', childIds: ['zhengzhou', 'wuhan', 'changsha'], pageSize: 9, status: 'planned', isUnlockedByDefault: false }, + { type: 'region', id: 'south_china', parentId: 'china', name: '华南', nameEn: 'South China', sortOrder: 5, themeColor: '#FF6B6B', childType: 'city', childIds: ['guangzhou', 'shenzhen', 'nanning', 'haikou', 'sanya'], pageSize: 9, status: 'planned', isUnlockedByDefault: false }, + { type: 'region', id: 'southwest', parentId: 'china', name: '西南', nameEn: 'Southwest', sortOrder: 6, themeColor: '#9B59B6', childType: 'city', childIds: ['chengdu', 'chongqing', 'guiyang', 'kunming', 'lhasa'], pageSize: 9, status: 'planned', isUnlockedByDefault: false }, + { type: 'region', id: 'northwest', parentId: 'china', name: '西北', nameEn: 'Northwest', sortOrder: 7, themeColor: '#F4D03F', childType: 'city', childIds: ['xian', 'lanzhou', 'xining', 'yinchuan', 'urumqi'], pageSize: 9, status: 'planned', isUnlockedByDefault: false }, + { type: 'region', id: 'hk_macao_tw', parentId: 'china', name: '港澳台', nameEn: 'HK, Macao & Taiwan', sortOrder: 8, themeColor: '#E74C3C', childType: 'city', childIds: ['hongkong', 'macao', 'taipei'], pageSize: 9, status: 'planned', isUnlockedByDefault: false }, + + // ═══════════════════════════════════════ + // 欧洲国家(18 国) + // ═══════════════════════════════════════ + { type: 'country', id: 'france', parentId: 'europe', name: '法国', nameEn: 'France', sortOrder: 1, themeColor: '#3B5998', childType: 'city', childIds: ['paris', 'nice'], pageSize: 9, status: 'planned', isUnlockedByDefault: false }, + { type: 'country', id: 'uk', parentId: 'europe', name: '英国', nameEn: 'United Kingdom', sortOrder: 2, themeColor: '#C0392B', childType: 'city', childIds: ['london', 'edinburgh'], pageSize: 9, status: 'planned', isUnlockedByDefault: false }, + { type: 'country', id: 'italy', parentId: 'europe', name: '意大利', nameEn: 'Italy', sortOrder: 3, themeColor: '#27AE60', childType: 'city', childIds: ['rome', 'milan', 'venice', 'florence'], pageSize: 9, status: 'planned', isUnlockedByDefault: false }, + { type: 'country', id: 'spain', parentId: 'europe', name: '西班牙', nameEn: 'Spain', sortOrder: 4, themeColor: '#E74C3C', childType: 'city', childIds: ['madrid', 'barcelona'], pageSize: 9, status: 'planned', isUnlockedByDefault: false }, + { type: 'country', id: 'germany', parentId: 'europe', name: '德国', nameEn: 'Germany', sortOrder: 5, themeColor: '#2C3E50', childType: 'city', childIds: ['berlin', 'munich'], pageSize: 9, status: 'planned', isUnlockedByDefault: false }, + { type: 'country', id: 'netherlands', parentId: 'europe', name: '荷兰', nameEn: 'Netherlands', sortOrder: 6, themeColor: '#FF6600', childType: 'city', childIds: ['amsterdam'], pageSize: 9, status: 'planned', isUnlockedByDefault: false }, + { type: 'country', id: 'switzerland', parentId: 'europe', name: '瑞士', nameEn: 'Switzerland', sortOrder: 7, themeColor: '#E74C3C', childType: 'city', childIds: ['zurich'], pageSize: 9, status: 'planned', isUnlockedByDefault: false }, + { type: 'country', id: 'austria', parentId: 'europe', name: '奥地利', nameEn: 'Austria', sortOrder: 8, themeColor: '#C0392B', childType: 'city', childIds: ['vienna'], pageSize: 9, status: 'planned', isUnlockedByDefault: false }, + { type: 'country', id: 'czech', parentId: 'europe', name: '捷克', nameEn: 'Czech Republic', sortOrder: 9, themeColor: '#2980B9', childType: 'city', childIds: ['prague'], pageSize: 9, status: 'planned', isUnlockedByDefault: false }, + { type: 'country', id: 'greece', parentId: 'europe', name: '希腊', nameEn: 'Greece', sortOrder: 10, themeColor: '#0D5EAF', childType: 'city', childIds: ['athens'], pageSize: 9, status: 'planned', isUnlockedByDefault: false }, + { type: 'country', id: 'russia', parentId: 'europe', name: '俄罗斯', nameEn: 'Russia', sortOrder: 11, themeColor: '#C0392B', childType: 'city', childIds: ['moscow', 'stpetersburg'], pageSize: 9, status: 'planned', isUnlockedByDefault: false }, + { type: 'country', id: 'portugal', parentId: 'europe', name: '葡萄牙', nameEn: 'Portugal', sortOrder: 12, themeColor: '#27AE60', childType: 'city', childIds: ['lisbon'], pageSize: 9, status: 'planned', isUnlockedByDefault: false }, + { type: 'country', id: 'norway', parentId: 'europe', name: '挪威', nameEn: 'Norway', sortOrder: 13, themeColor: '#2980B9', childType: 'city', childIds: ['oslo'], pageSize: 9, status: 'planned', isUnlockedByDefault: false }, + { type: 'country', id: 'sweden', parentId: 'europe', name: '瑞典', nameEn: 'Sweden', sortOrder: 14, themeColor: '#F1C40F', childType: 'city', childIds: ['stockholm'], pageSize: 9, status: 'planned', isUnlockedByDefault: false }, + { type: 'country', id: 'denmark', parentId: 'europe', name: '丹麦', nameEn: 'Denmark', sortOrder: 15, themeColor: '#C0392B', childType: 'city', childIds: ['copenhagen'], pageSize: 9, status: 'planned', isUnlockedByDefault: false }, + { type: 'country', id: 'finland', parentId: 'europe', name: '芬兰', nameEn: 'Finland', sortOrder: 16, themeColor: '#2980B9', childType: 'city', childIds: ['helsinki'], pageSize: 9, status: 'planned', isUnlockedByDefault: false }, + { type: 'country', id: 'iceland', parentId: 'europe', name: '冰岛', nameEn: 'Iceland', sortOrder: 17, themeColor: '#2C3E50', childType: 'city', childIds: ['reykjavik'], pageSize: 9, status: 'planned', isUnlockedByDefault: false }, + { type: 'country', id: 'hungary', parentId: 'europe', name: '匈牙利', nameEn: 'Hungary', sortOrder: 18, themeColor: '#27AE60', childType: 'city', childIds: ['budapest'], pageSize: 9, status: 'planned', isUnlockedByDefault: false }, + + // ═══════════════════════════════════════ + // 北美国家(3 国) + // ═══════════════════════════════════════ + { type: 'country', id: 'usa', parentId: 'north_america', name: '美国', nameEn: 'United States', sortOrder: 1, themeColor: '#3B5998', childType: 'city', childIds: ['newyork', 'losangeles', 'sanfrancisco', 'lasvegas', 'washington', 'chicago', 'miami'], pageSize: 9, status: 'planned', isUnlockedByDefault: false }, + { type: 'country', id: 'canada', parentId: 'north_america', name: '加拿大', nameEn: 'Canada', sortOrder: 2, themeColor: '#E74C3C', childType: 'city', childIds: ['vancouver', 'toronto', 'montreal'], pageSize: 9, status: 'planned', isUnlockedByDefault: false }, + { type: 'country', id: 'mexico', parentId: 'north_america', name: '墨西哥', nameEn: 'Mexico', sortOrder: 3, themeColor: '#27AE60', childType: 'city', childIds: ['mexicocity', 'cancun'], pageSize: 9, status: 'planned', isUnlockedByDefault: false }, + + // ═══════════════════════════════════════ + // 南美国家(5 国) + // ═══════════════════════════════════════ + { type: 'country', id: 'brazil', parentId: 'south_america', name: '巴西', nameEn: 'Brazil', sortOrder: 1, themeColor: '#F1C40F', childType: 'city', childIds: ['rio', 'saopaulo'], pageSize: 9, status: 'planned', isUnlockedByDefault: false }, + { type: 'country', id: 'argentina', parentId: 'south_america', name: '阿根廷', nameEn: 'Argentina', sortOrder: 2, themeColor: '#5DADE2', childType: 'city', childIds: ['buenosaires'], pageSize: 9, status: 'planned', isUnlockedByDefault: false }, + { type: 'country', id: 'peru', parentId: 'south_america', name: '秘鲁', nameEn: 'Peru', sortOrder: 3, themeColor: '#E74C3C', childType: 'city', childIds: ['lima', 'cusco'], pageSize: 9, status: 'planned', isUnlockedByDefault: false }, + { type: 'country', id: 'chile', parentId: 'south_america', name: '智利', nameEn: 'Chile', sortOrder: 4, themeColor: '#2980B9', childType: 'city', childIds: ['santiago'], pageSize: 9, status: 'planned', isUnlockedByDefault: false }, + { type: 'country', id: 'colombia', parentId: 'south_america', name: '哥伦比亚', nameEn: 'Colombia', sortOrder: 5, themeColor: '#F39C12', childType: 'city', childIds: ['bogota'], pageSize: 9, status: 'planned', isUnlockedByDefault: false }, + + // ═══════════════════════════════════════ + // 非洲国家(4 国) + // ═══════════════════════════════════════ + { type: 'country', id: 'egypt', parentId: 'africa', name: '埃及', nameEn: 'Egypt', sortOrder: 1, themeColor: '#F39C12', childType: 'city', childIds: ['cairo'], pageSize: 9, status: 'planned', isUnlockedByDefault: false }, + { type: 'country', id: 'south_africa', parentId: 'africa', name: '南非', nameEn: 'South Africa', sortOrder: 2, themeColor: '#27AE60', childType: 'city', childIds: ['capetown'], pageSize: 9, status: 'planned', isUnlockedByDefault: false }, + { type: 'country', id: 'morocco', parentId: 'africa', name: '摩洛哥', nameEn: 'Morocco', sortOrder: 3, themeColor: '#E74C3C', childType: 'city', childIds: ['marrakech'], pageSize: 9, status: 'planned', isUnlockedByDefault: false }, + { type: 'country', id: 'kenya', parentId: 'africa', name: '肯尼亚', nameEn: 'Kenya', sortOrder: 4, themeColor: '#2C3E50', childType: 'city', childIds: ['nairobi', 'mombasa'], pageSize: 9, status: 'planned', isUnlockedByDefault: false }, + + // ═══════════════════════════════════════ + // 大洋洲国家(2 国) + // ═══════════════════════════════════════ + { type: 'country', id: 'australia', parentId: 'oceania', name: '澳大利亚', nameEn: 'Australia', sortOrder: 1, themeColor: '#F39C12', childType: 'city', childIds: ['sydney', 'melbourne'], pageSize: 9, status: 'planned', isUnlockedByDefault: false }, + { type: 'country', id: 'new_zealand', parentId: 'oceania', name: '新西兰', nameEn: 'New Zealand', sortOrder: 2, themeColor: '#2C3E50', childType: 'city', childIds: ['auckland', 'queenstown'], pageSize: 9, status: 'planned', isUnlockedByDefault: false }, +] + +// ── 查询工具 ── +export function getCatalogNode(id) { + return worldCatalog.find(n => n.id === id) || null +} + +export function getCatalogChildren(parentId) { + return worldCatalog.filter(n => n.parentId === parentId).sort((a, b) => a.sortOrder - b.sortOrder) +} + +export function getActiveNodes() { + return worldCatalog.filter(n => n.status === 'active') +} + +export function getPlannedNodes() { + return worldCatalog.filter(n => n.status === 'planned') +} diff --git a/js/content/regions/china.js b/js/content/regions/china.js index 85d26e2..dd87bd8 100644 --- a/js/content/regions/china.js +++ b/js/content/regions/china.js @@ -1,5 +1,6 @@ /** - * @deprecated 中国地区数据已移入 navigation/future-catalog.js + * @deprecated 中国地区数据已移入 navigation/world-catalog.js * 仅在 V1.1+ 启用国家→地区导航后使用。 */ -export { chinaRegionPlan as chinaRegions } from '../navigation/future-catalog.js' +import { getCatalogChildren } from '../navigation/world-catalog.js' +export const chinaRegions = getCatalogChildren('china')