nav: 统一 NavNode schema + runtime/roadmap 分离 + 城市补父级

- navigation/nav-schema.js: 统一 NavNode schema 定义
- navigation/runtime-nav.js: MVP runtime(仅 6 城市 active)
- navigation/future-catalog.js: 路线图数据(不进 runtime)
- 旧索引文件标注 @deprecated,重定向到新路径
- 6 城市补 countryId/regionId
- game-design 明确 MVP vs V1.1+ 导航边界

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
manpengan
2026-03-28 23:31:52 +08:00
parent 18cd4d8409
commit 212b12ab6b
15 changed files with 470 additions and 439 deletions

View File

@@ -0,0 +1,37 @@
/**
* NavNode — 统一导航节点 schema
*
* 所有导航层级(洲/国家/地区/城市组)共用同一 shape。
* 分页通过 shared/pagination.js 的 paginate() 动态计算,不手写 page 常量。
*
* @typedef {Object} NavNode
* @property {'continent'|'country'|'region'|'city-group'} type
* @property {string} id - 全局唯一标识
* @property {string|null} parentId - 父节点 id洲级为 null
* @property {string} name - 中文名
* @property {string} nameEn - 英文名
* @property {number} sortOrder - 同级排序
* @property {string} themeColor - 主题色 HEX
* @property {'country'|'region'|'city'|null} childType - 子节点类型
* @property {string[]} childIds - 当前可消费的子节点 id仅已启用的
* @property {number} pageSize - 每页格子数,默认 9
* @property {boolean} isEnabled - 当前版本是否启用
* @property {boolean} isUnlockedByDefault - 是否默认解锁
*/
// schema 验证函数
export function validateNavNode(node) {
const required = ['type', 'id', 'name', 'nameEn', 'sortOrder', 'themeColor', 'childType', 'childIds', 'pageSize', 'isEnabled', 'isUnlockedByDefault']
const validTypes = ['continent', 'country', 'region', 'city-group']
const validChildTypes = ['country', 'region', 'city', null]
const errors = []
for (const key of required) {
if (!(key in node)) errors.push(`missing field: ${key}`)
}
if (!validTypes.includes(node.type)) errors.push(`invalid type: ${node.type}`)
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')
return errors
}