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

@@ -75,160 +75,124 @@
## 2. 地图结构 ## 2. 地图结构
### 2.1 导航层级 ### 2.1 MVP 导航(当前实现)
MVP 只开放亚洲 6 城市,直入城市选择页,不启用完整多级导航。
``` ```
3x3 棋盘) 启动 → 亚洲城市页3x3 棋盘6 城市)→ 关卡
└── 国家3x3 棋盘,超过 9 国分页)
└── 城市3x3 棋盘,超过 9 城分页)
└── 关卡(单局游戏)
``` ```
中国因城市数量多40 城),额外增加"地区"层级: - 跳过洲选择页(只有 1 个洲,多余点击)
- 跳过国家/地区层级
- 6 城市平铺在一页 3x3 棋盘中
- 其他洲的入口不显示
**MVP 城市列表(固定 6 城):**
| 序号 | 城市 | 国家 | 解锁条件 |
|------|------|------|---------|
| 1 | 北京 | 中国 | 默认解锁 |
| 2 | 东京 | 日本 | 通关北京 |
| 3 | 曼谷 | 泰国 | 通关东京 |
| 4 | 首尔 | 韩国 | 通关曼谷 |
| 5 | 新加坡 | 新加坡 | 通关首尔 |
| 6 | 伊斯坦布尔 | 土耳其 | 通关新加坡 |
### 2.2 V1.1+ 完整导航(后续启用)
城市数量超过 9 时启用完整多级导航,每页 3×3 棋盘,超过 9 项翻页。
``` ```
亚洲 → 中国 → 地区3x3→ 城市3x3→ 关卡 洲选择3x3
亚洲 → 日本 → 城市3x3→ 关卡 └── 国家选择3x3分页
└── 城市选择3x3分页
└── 关卡
``` ```
### 2.2 洲选择页(第 1 级) 中国因城市多41 城),额外增加地区层级:
1 页3×3 棋盘:
``` ```
┌────────┬────────┬────────┐ 亚洲 → 中国 → 地区3x3→ 城市3x3
│ 亚洲 │ 欧洲 │ 北美洲 │ 亚洲 → 日本 → 城市3x3
├────────┼────────┼────────┤
│ 南美洲 │ 非洲 │ 大洋洲 │
├────────┼────────┼────────┤
│ │ │ │
└────────┴────────┴────────┘
``` ```
6 个洲3 个空位留给未来扩展。MVP 只开放亚洲,其余显示锁定 + "即将开放"。 #### 洲选择页
每个洲的封面:洲标志性图案 + 解锁进度(如"6/70 城市已解锁" 6 个洲1 页:亚洲、欧洲、北美洲、南美洲、非洲、大洋洲
### 2.3 国家选择页(第 2 级) #### 亚洲国家页
#### 亚洲 — 2 页 18 国,分 2 页
- Page 1: 中国、日本、韩国、泰国、新加坡、越南、马来西亚、印度尼西亚、菲律宾
- Page 2: 印度、阿联酋、土耳其、以色列、尼泊尔、柬埔寨、斯里兰卡、缅甸、蒙古
Page 1: #### 中国地区页
```
┌────────┬────────┬────────┐
│ 中国 │ 日本 │ 韩国 │
├────────┼────────┼────────┤
│ 泰国 │ 新加坡 │ 越南 │
├────────┼────────┼────────┤
│马来西亚│印度尼西亚│ 菲律宾│
└────────┴────────┴────────┘
```
Page 2: 8 个地区1 页:
```
┌────────┬────────┬────────┐
│ 印度 │ 阿联酋 │ 土耳其 │
├────────┼────────┼────────┤
│ 以色列 │ 尼泊尔 │ 柬埔寨 │
├────────┼────────┼────────┤
│斯里兰卡│ 缅甸 │ 蒙古 │
└────────┴────────┴────────┘
```
#### 欧洲 — 2 页 | 地区 | 城市 | 数量 |
|------|------|------|
| 华北 | 北京、天津、石家庄、太原、呼和浩特 | 5 |
| 东北 | 沈阳、长春、哈尔滨、大连 | 4 |
| 华东 | 上海、南京、杭州、合肥、福州、南昌、济南、苏州、厦门、青岛、宁波 | 112 页) |
| 华中 | 郑州、武汉、长沙 | 3 |
| 华南 | 广州、深圳、南宁、海口、三亚 | 5 |
| 西南 | 成都、重庆、贵阳、昆明、拉萨 | 5 |
| 西北 | 西安、兰州、西宁、银川、乌鲁木齐 | 5 |
| 港澳台 | 香港、澳门、台北 | 3 |
Page 1: > 港澳台统一为 3 城(香港、澳门、台北),文档和索引一致。
```
法国、英国、意大利、西班牙、德国、荷兰、瑞士、奥地利、捷克
```
Page 2: ### 2.3 导航数据合同
```
希腊、俄罗斯、葡萄牙、挪威、瑞典、丹麦、芬兰、冰岛、匈牙利
```
#### 北美洲 — 1 页 所有导航层级使用统一的 NavNode schema定义在 `js/content/navigation/nav-schema.js`
```
美国、加拿大、墨西哥 + 6 空位
```
#### 南美洲 — 1 页 | 字段 | 类型 | 说明 |
``` |------|------|------|
巴西、阿根廷、秘鲁、智利、哥伦比亚 + 4 空位 | `type` | string | `continent` / `country` / `region` / `city-group` |
``` | `id` | string | 全局唯一标识 |
| `parentId` | string\|null | 父节点 id洲级为 null |
| `name` | string | 中文名 |
| `nameEn` | string | 英文名 |
| `sortOrder` | number | 同级排序 |
| `themeColor` | string | 主题色 HEX |
| `childType` | string\|null | 子节点类型:`country` / `region` / `city` / null |
| `childIds` | string[] | 当前可消费的子节点 id仅已启用的 |
| `pageSize` | number | 每页格子数,默认 9 |
| `isEnabled` | boolean | 当前版本是否启用 |
| `isUnlockedByDefault` | boolean | 是否默认解锁 |
#### 非洲 — 1 页 分页通过 `shared/pagination.js``paginate(childIds, pageSize)` 动态计算,不手写 page 常量。
```
埃及、南非、摩洛哥、肯尼亚 + 5 空位
```
#### 大洋洲 — 1 页 **数据文件分层:**
```
澳大利亚、新西兰 + 7 空位
```
### 2.4 中国地区选择页(第 3 级,仅中国) | 文件 | 用途 | 是否进入 runtime |
|------|------|-----------------|
| `navigation/runtime-nav.js` | 当前版本可消费的导航节点 | ✅ 是 |
| `navigation/future-catalog.js` | 未来版本城市/国家/地区规划 | ❌ 否 |
| `navigation/nav-schema.js` | NavNode schema 定义 + 验证 | ✅ 是(类型定义) |
| `docs/city-roadmap.md` | 全量 127 城市路线图 | ❌ 否 |
中国 40 个城市按地区分组1 页 3×3 ### 2.4 城市封面设计
```
┌────────┬────────┬────────┐
│ 华北 │ 东北 │ 华东 │
├────────┼────────┼────────┤
│ 华中 │ 华南 │ 西南 │
├────────┼────────┼────────┤
│ 西北 │ 港澳台 │ │
└────────┴────────┴────────┘
```
各地区包含城市:
| 地区 | 城市 | 数量 | 页数 |
|------|------|------|------|
| 华北 | 北京、天津、石家庄、太原、呼和浩特 | 5 | 1 |
| 东北 | 沈阳、长春、哈尔滨、大连 | 4 | 1 |
| 华东 | 上海、南京、杭州、合肥、福州、南昌、济南、苏州、厦门、青岛、宁波 | 11 | 2 |
| 华中 | 郑州、武汉、长沙 | 3 | 1 |
| 华南 | 广州、深圳、南宁、海口、三亚 | 5 | 1 |
| 西南 | 成都、重庆、贵阳、昆明、拉萨 | 5 | 1 |
| 西北 | 西安、兰州、西宁、银川、乌鲁木齐 | 5 | 1 |
| 港澳台 | 香港、澳门、台北 | 3 | 1 |
### 2.5 城市选择页(第 3/4 级)
#### 其他国家(非中国)— 直接展示城市
| 国家 | 城市数 | 页数 |
|------|--------|------|
| 日本 | 4东京、大阪、京都、札幌 | 1 |
| 韩国 | 3首尔、釜山、济州 | 1 |
| 泰国 | 3曼谷、清迈、普吉 | 1 |
| 美国 | 7纽约、洛杉矶、旧金山、拉斯维加斯、华盛顿、芝加哥、迈阿密 | 1 |
| 意大利 | 4罗马、米兰、威尼斯、佛罗伦萨 | 1 |
| 其他国家 | 1-3 | 1 |
所有国家的城市数均 ≤ 9无需分页除华东 11 城需 2 页外)。
### 2.6 城市封面设计
每个格子显示: 每个格子显示:
- 该城市/国家/洲的特色猫猫头像(已解锁:彩色,未解锁:灰色剪影) - 猫猫头像(已解锁:彩色,未解锁:灰色剪影)
- 名称(中文) - 名称(中文)
- 进度信息(已解锁:"X/6 关",未解锁:"🔒" - 进度(已解锁:"X/6 关",未解锁:"🔒"
### 2.7 分页交互 ### 2.5 分页交互
- 左右滑动翻页,底部圆点指示器 - 左右滑动翻页,底部圆点指示器
- 首次进入默认第 1 页
- 翻页动画 0.3s 弹性过渡 - 翻页动画 0.3s 弹性过渡
- MVP 只有 1 页 6 城市,无需翻页
### 2.8 解锁规则 ### 2.6 解锁规则
-一层级内顺序解锁(通关前一个解锁下一个) -顺序解锁(通关前一个解锁下一个)
- 第一个项目默认解锁 - 第一个项目默认解锁
- 跨层级:通关一个国家所有城市后,解锁下一个国家 - MVP北京默认解锁顺序通关解锁后续城市
- 中国地区内:通关华北所有城市后解锁东北,以此类推 - V1.1+:跨层级解锁(通关一个国家所有城市后解锁下一个国家)
- MVP 只开放亚洲洲,亚洲内只开放中国(北京)和已有 MVP 城市
--- ---

View File

@@ -1,9 +1,13 @@
module.exports = { import { createLevelPresets } from '../shared/level-presets.js'
export const bangkok = {
id: 'bangkok', id: 'bangkok',
contentVersion: 1, contentVersion: 1,
continentId: 'asia', continentId: 'asia',
countryId: 'thailand',
regionId: null,
sortOrder: 3, sortOrder: 3,
unlockAfterCityId: 'tokyo', unlockAfterCityId: 'seoul',
bundle: { bundle: {
packId: 'asia-rest', packId: 'asia-rest',
preload: 'on-city-page', preload: 'on-city-page',
@@ -11,54 +15,45 @@ module.exports = {
display: { display: {
name: '曼谷', name: '曼谷',
nameEn: 'Bangkok', nameEn: 'Bangkok',
bgColor: '#FFB347', bgColor: '#F9A826',
tagline: '湄南河畔追猫猫', tagline: '热带夜市看猫猫',
funFact: '曼谷的全名有168个字母是世界最长城市', funFact: '曼谷是东南亚热门旅行城市',
}, },
cover: { cover: {
catImage: 'images/cats/cat_bangkok.png', catImage: 'images/cats/cat_bangkok.png',
catThumb: 'images/cats/cat_bangkok_thumb.png', catThumb: 'images/cats/cat_bangkok_thumb.png',
shareAccent: '#C9A96E', shareAccent: '#FFD166',
}, },
cat: { cat: {
id: 'cat_bangkok', id: 'cat_bangkok',
name: '暹暹', name: '泰泰',
intro: '戴泰式花环的暹罗猫,最爱在水上市场闲逛', baseColor: '#D9C2A7',
baseColor: '#FAF0E6',
pattern: 'siamese', pattern: 'siamese',
patternColor: '#6B4226', accessory: '花环',
accessory: '泰式花环',
expression: 'curious',
}, },
passport: { passport: {
stampId: 'stamp_bangkok', stampId: 'stamp_bangkok',
stampLabel: '曼谷', stampLabel: '曼谷',
}, },
elements: [ elements: [
{ id: 'bangkok_01', name: '冬阴功', category: 'food', rarity: 'core', image: 'images/elements/bangkok/bangkok_01.png', tags: ['soup', 'spicy', 'shrimp'] }, { id: 'bangkok_01', name: '玉佛寺', category: 'landmark', rarity: 'core', image: 'images/elements/bangkok/bangkok_01.png', tags: ['temple'] },
{ id: 'bangkok_02', name: '嘟嘟车', category: 'item', rarity: 'core', image: 'images/elements/bangkok/bangkok_02.png', tags: ['vehicle', 'colorful', 'street'] }, { id: 'bangkok_02', name: '佛塔', category: 'landmark', rarity: 'core', image: 'images/elements/bangkok/bangkok_02.png', tags: ['tower'] },
{ id: 'bangkok_03', name: '大象', category: 'nature', rarity: 'core', image: 'images/elements/bangkok/bangkok_03.png', tags: ['animal', 'grey', 'gentle'] }, { id: 'bangkok_03', name: '冬阴功', category: 'food', rarity: 'core', image: 'images/elements/bangkok/bangkok_03.png', tags: ['soup'] },
{ id: 'bangkok_04', name: '泰拳手套', category: 'item', rarity: 'core', image: 'images/elements/bangkok/bangkok_04.png', tags: ['sport', 'red', 'fighting'] }, { id: 'bangkok_04', name: '芒果糯米饭', category: 'food', rarity: 'core', image: 'images/elements/bangkok/bangkok_04.png', tags: ['dessert'] },
{ id: 'bangkok_05', name: '芒果糯米饭', category: 'food', rarity: 'core', image: 'images/elements/bangkok/bangkok_05.png', tags: ['sweet', 'mango', 'sticky'] }, { id: 'bangkok_05', name: '船面', category: 'food', rarity: 'core', image: 'images/elements/bangkok/bangkok_05.png', tags: ['noodle'] },
{ id: 'bangkok_06', name: '金佛', category: 'landmark', rarity: 'core', image: 'images/elements/bangkok/bangkok_06.png', tags: ['buddha', 'golden', 'temple'] }, { id: 'bangkok_06', name: '泰丝', category: 'culture', rarity: 'core', image: 'images/elements/bangkok/bangkok_06.png', tags: ['fabric'] },
{ id: 'bangkok_07', name: '莲花', category: 'nature', rarity: 'core', image: 'images/elements/bangkok/bangkok_07.png', tags: ['flower', 'pink', 'water'] }, { id: 'bangkok_07', name: '泰拳手套', category: 'culture', rarity: 'core', image: 'images/elements/bangkok/bangkok_07.png', tags: ['sport'] },
{ id: 'bangkok_08', name: '榴莲', category: 'food', rarity: 'core', image: 'images/elements/bangkok/bangkok_08.png', tags: ['fruit', 'spiky', 'yellow'] }, { id: 'bangkok_08', name: '嘟嘟车', category: 'item', rarity: 'core', image: 'images/elements/bangkok/bangkok_08.png', tags: ['vehicle'] },
{ id: 'bangkok_09', name: '泰丝', category: 'culture', rarity: 'core', image: 'images/elements/bangkok/bangkok_09.png', tags: ['fabric', 'shimmer', 'craft'] }, { id: 'bangkok_09', name: '椰子杯', category: 'item', rarity: 'core', image: 'images/elements/bangkok/bangkok_09.png', tags: ['drink'] },
{ id: 'bangkok_10', name: '船面', category: 'food', rarity: 'core', image: 'images/elements/bangkok/bangkok_10.png', tags: ['noodle', 'boat', 'dark'] }, { id: 'bangkok_10', name: '莲花', category: 'nature', rarity: 'core', image: 'images/elements/bangkok/bangkok_10.png', tags: ['flower'] },
{ id: 'bangkok_11', name: '佛塔', category: 'landmark', rarity: 'core', image: 'images/elements/bangkok/bangkok_11.png', tags: ['stupa', 'golden', 'tall'] }, { id: 'bangkok_11', name: '大象', category: 'nature', rarity: 'core', image: 'images/elements/bangkok/bangkok_11.png', tags: ['animal'] },
{ id: 'bangkok_12', name: '椰子', category: 'nature', rarity: 'accent', image: 'images/elements/bangkok/bangkok_12.png', tags: ['fruit', 'tropical', 'green'] }, { id: 'bangkok_12', name: '榴莲', category: 'nature', rarity: 'core', image: 'images/elements/bangkok/bangkok_12.png', tags: ['fruit'] },
{ id: 'bangkok_13', name: '夜市灯笼', category: 'culture', rarity: 'accent', image: 'images/elements/bangkok/bangkok_13.png', tags: ['lantern', 'glow', 'night'] },
],
levelPresets: [
{ id: 1, difficultyTier: 'intro', seedBase: 13001, elementCount: 6, piecesPerElement: 3, layers: 2, density: 'low', targetPassRate: 0.95, targetDurationSec: [60, 120] },
{ id: 2, difficultyTier: 'easy', seedBase: 13002, elementCount: 7, piecesPerElement: 3, layers: 2, density: 'low', targetPassRate: 0.90, targetDurationSec: [60, 120] },
{ id: 3, difficultyTier: 'normal', seedBase: 13003, elementCount: 8, piecesPerElement: 3, layers: 3, density: 'medium', targetPassRate: 0.80, targetDurationSec: [90, 150] },
{ id: 4, difficultyTier: 'normal', seedBase: 13004, elementCount: 9, piecesPerElement: 3, layers: 3, density: 'medium', targetPassRate: 0.70, targetDurationSec: [90, 150] },
{ id: 5, difficultyTier: 'hard', seedBase: 13005, elementCount: 10, piecesPerElement: 3, layers: 4, density: 'medium_high', targetPassRate: 0.60, targetDurationSec: [120, 180] },
{ id: 6, difficultyTier: 'boss', seedBase: 13006, elementCount: 10, piecesPerElement: [3,3,3,3,3,6,3,3,3,3], layers: 4, density: 'high', targetPassRate: 0.55, targetDurationSec: [120, 240] },
], ],
levelPresets: createLevelPresets(13000),
shareCard: { shareCard: {
titleUnlocked: '我解锁了曼谷猫!', titleUnlocked: '我解锁了曼谷猫!',
titlePassport: '曼谷护照盖章完成', titlePassport: '曼谷护照盖章完成',
}, },
} }
export default bangkok

View File

@@ -1,9 +1,13 @@
module.exports = { import { createLevelPresets } from '../shared/level-presets.js'
export const beijing = {
id: 'beijing', id: 'beijing',
contentVersion: 1, contentVersion: 1,
continentId: 'asia', continentId: 'asia',
countryId: 'china',
regionId: 'north_china',
sortOrder: 1, sortOrder: 1,
unlockAfterCityId: null, unlockAfterCityId: 'tokyo',
bundle: { bundle: {
packId: 'main', packId: 'main',
preload: 'startup', preload: 'startup',
@@ -13,7 +17,7 @@ module.exports = {
nameEn: 'Beijing', nameEn: 'Beijing',
bgColor: '#CC2936', bgColor: '#CC2936',
tagline: '天安门前看猫猫', tagline: '天安门前看猫猫',
funFact: '北京是世界上拥有最多宫殿的城市', funFact: '北京是中国的历史文化名城',
}, },
cover: { cover: {
catImage: 'images/cats/cat_beijing.png', catImage: 'images/cats/cat_beijing.png',
@@ -23,43 +27,33 @@ module.exports = {
cat: { cat: {
id: 'cat_beijing', id: 'cat_beijing',
name: '京京', name: '京京',
intro: '戴虎头帽的橘猫,最爱在胡同里晒太阳',
baseColor: '#F28C28', baseColor: '#F28C28',
pattern: 'tabby', pattern: 'tabby',
patternColor: '#D4761A',
accessory: '虎头帽', accessory: '虎头帽',
expression: 'smile',
}, },
passport: { passport: {
stampId: 'stamp_beijing', stampId: 'stamp_beijing',
stampLabel: '北京', stampLabel: '北京',
}, },
elements: [ elements: [
{ id: 'beijing_01', name: '糖葫芦', category: 'food', rarity: 'core', image: 'images/elements/beijing/beijing_01.png', tags: ['sweet', 'street-food', 'red'] }, { id: 'beijing_01', name: '天安门', category: 'landmark', rarity: 'core', image: 'images/elements/beijing/beijing_01.png', tags: ['gate', 'red'] },
{ id: 'beijing_02', name: '京剧脸谱', category: 'culture', rarity: 'core', image: 'images/elements/beijing/beijing_02.png', tags: ['opera', 'mask', 'colorful'] }, { id: 'beijing_02', name: '鸟巢', category: 'landmark', rarity: 'core', image: 'images/elements/beijing/beijing_02.png', tags: ['stadium'] },
{ id: 'beijing_03', name: '天安门', category: 'landmark', rarity: 'core', image: 'images/elements/beijing/beijing_03.png', tags: ['building', 'red', 'iconic'] }, { id: 'beijing_03', name: '糖葫芦', category: 'food', rarity: 'core', image: 'images/elements/beijing/beijing_03.png', tags: ['sweet'] },
{ id: 'beijing_04', name: '烤鸭', category: 'food', rarity: 'core', image: 'images/elements/beijing/beijing_04.png', tags: ['meat', 'famous', 'golden'] }, { id: 'beijing_04', name: '烤鸭', category: 'food', rarity: 'core', image: 'images/elements/beijing/beijing_04.png', tags: ['duck'] },
{ id: 'beijing_05', name: '兔儿爷', category: 'culture', rarity: 'core', image: 'images/elements/beijing/beijing_05.png', tags: ['toy', 'festival', 'rabbit'] }, { id: 'beijing_05', name: '豆汁', category: 'food', rarity: 'core', image: 'images/elements/beijing/beijing_05.png', tags: ['drink'] },
{ id: 'beijing_06', name: '长城砖', category: 'landmark', rarity: 'core', image: 'images/elements/beijing/beijing_06.png', tags: ['wall', 'historic', 'grey'] }, { id: 'beijing_06', name: '京剧脸谱', category: 'culture', rarity: 'core', image: 'images/elements/beijing/beijing_06.png', tags: ['opera'] },
{ id: 'beijing_07', name: '故宫角楼', category: 'landmark', rarity: 'core', image: 'images/elements/beijing/beijing_07.png', tags: ['palace', 'golden', 'ancient'] }, { id: 'beijing_07', name: '兔儿爷', category: 'culture', rarity: 'core', image: 'images/elements/beijing/beijing_07.png', tags: ['folk'] },
{ id: 'beijing_08', name: '豆汁', category: 'food', rarity: 'core', image: 'images/elements/beijing/beijing_08.png', tags: ['drink', 'green', 'local'] }, { id: 'beijing_08', name: '毛笔', category: 'item', rarity: 'core', image: 'images/elements/beijing/beijing_08.png', tags: ['brush'] },
{ id: 'beijing_09', name: '铜锣', category: 'item', rarity: 'core', image: 'images/elements/beijing/beijing_09.png', tags: ['instrument', 'golden', 'round'] }, { id: 'beijing_09', name: '铜锣', category: 'item', rarity: 'core', image: 'images/elements/beijing/beijing_09.png', tags: ['instrument'] },
{ id: 'beijing_10', name: '毛笔', category: 'item', rarity: 'core', image: 'images/elements/beijing/beijing_10.png', tags: ['writing', 'black', 'thin'] }, { id: 'beijing_10', name: '银杏叶', category: 'nature', rarity: 'core', image: 'images/elements/beijing/beijing_10.png', tags: ['leaf'] },
{ id: 'beijing_11', name: '鸟巢', category: 'landmark', rarity: 'core', image: 'images/elements/beijing/beijing_11.png', tags: ['stadium', 'modern', 'steel'] }, { id: 'beijing_11', name: '玉兰花', category: 'nature', rarity: 'core', image: 'images/elements/beijing/beijing_11.png', tags: ['flower'] },
{ id: 'beijing_12', name: '冰糖葫芦', category: 'food', rarity: 'core', image: 'images/elements/beijing/beijing_12.png', tags: ['candy', 'stick', 'red'] }, { id: 'beijing_12', name: '白鸽', category: 'nature', rarity: 'core', image: 'images/elements/beijing/beijing_12.png', tags: ['bird'] },
{ id: 'beijing_13', name: '四合院门', category: 'culture', rarity: 'accent', image: 'images/elements/beijing/beijing_13.png', tags: ['door', 'red', 'traditional'] },
{ id: 'beijing_14', name: '景泰蓝', category: 'item', rarity: 'accent', image: 'images/elements/beijing/beijing_14.png', tags: ['craft', 'blue', 'enamel'] },
],
levelPresets: [
{ id: 1, difficultyTier: 'intro', seedBase: 11001, elementCount: 6, piecesPerElement: 3, layers: 2, density: 'low', targetPassRate: 0.95, targetDurationSec: [60, 120] },
{ id: 2, difficultyTier: 'easy', seedBase: 11002, elementCount: 7, piecesPerElement: 3, layers: 2, density: 'low', targetPassRate: 0.90, targetDurationSec: [60, 120] },
{ id: 3, difficultyTier: 'normal', seedBase: 11003, elementCount: 8, piecesPerElement: 3, layers: 3, density: 'medium', targetPassRate: 0.80, targetDurationSec: [90, 150] },
{ id: 4, difficultyTier: 'normal', seedBase: 11004, elementCount: 9, piecesPerElement: 3, layers: 3, density: 'medium', targetPassRate: 0.70, targetDurationSec: [90, 150] },
{ id: 5, difficultyTier: 'hard', seedBase: 11005, elementCount: 10, piecesPerElement: 3, layers: 4, density: 'medium_high', targetPassRate: 0.60, targetDurationSec: [120, 180] },
{ id: 6, difficultyTier: 'boss', seedBase: 11006, elementCount: 10, piecesPerElement: [3,3,3,3,3,6,3,3,3,3], layers: 4, density: 'high', targetPassRate: 0.55, targetDurationSec: [120, 240] },
], ],
levelPresets: createLevelPresets(11000),
shareCard: { shareCard: {
titleUnlocked: '我解锁了北京猫!', titleUnlocked: '我解锁了北京猫!',
titlePassport: '北京护照盖章完成', titlePassport: '北京护照盖章完成',
}, },
} }
export default beijing

View File

@@ -0,0 +1,10 @@
import bangkok from './bangkok.js'
import beijing from './beijing.js'
import istanbul from './istanbul.js'
import seoul from './seoul.js'
import singapore from './singapore.js'
import tokyo from './tokyo.js'
export const cities = [beijing, tokyo, bangkok, seoul, singapore, istanbul]
export default cities

View File

@@ -1,9 +1,13 @@
module.exports = { import { createLevelPresets } from '../shared/level-presets.js'
export const istanbul = {
id: 'istanbul', id: 'istanbul',
contentVersion: 1, contentVersion: 1,
continentId: 'asia', continentId: 'asia',
countryId: 'turkey',
regionId: null,
sortOrder: 6, sortOrder: 6,
unlockAfterCityId: 'singapore', unlockAfterCityId: null,
bundle: { bundle: {
packId: 'asia-rest', packId: 'asia-rest',
preload: 'on-city-page', preload: 'on-city-page',
@@ -11,55 +15,45 @@ module.exports = {
display: { display: {
name: '伊斯坦布尔', name: '伊斯坦布尔',
nameEn: 'Istanbul', nameEn: 'Istanbul',
bgColor: '#1A5276', bgColor: '#7B2CBF',
tagline: '博斯普鲁斯的猫', tagline: '海峡两岸看猫猫',
funFact: '伊斯坦布尔是唯一横跨两大洲的城市', funFact: '伊斯坦布尔横跨欧亚两洲',
}, },
cover: { cover: {
catImage: 'images/cats/cat_istanbul.png', catImage: 'images/cats/cat_istanbul.png',
catThumb: 'images/cats/cat_istanbul_thumb.png', catThumb: 'images/cats/cat_istanbul_thumb.png',
shareAccent: '#E8D5B7', shareAccent: '#9D4EDD',
}, },
cat: { cat: {
id: 'cat_istanbul', id: 'cat_istanbul',
name: '安安', name: '蓝眸',
intro: '佩戴恶魔之眼项圈的安哥拉猫,神秘优雅', baseColor: '#F5E6CC',
baseColor: '#F5F5F5', pattern: 'angora',
pattern: 'solid',
patternColor: '#E8E8E8',
accessory: '恶魔之眼项圈', accessory: '恶魔之眼项圈',
expression: 'mysterious',
}, },
passport: { passport: {
stampId: 'stamp_istanbul', stampId: 'stamp_istanbul',
stampLabel: '伊斯坦布尔', stampLabel: '伊斯坦布尔',
}, },
elements: [ elements: [
{ id: 'istanbul_01', name: '蓝色清真寺', category: 'landmark', rarity: 'core', image: 'images/elements/istanbul/istanbul_01.png', tags: ['mosque', 'blue', 'dome'] }, { id: 'istanbul_01', name: '蓝色清真寺', category: 'landmark', rarity: 'core', image: 'images/elements/istanbul/istanbul_01.png', tags: ['mosque'] },
{ id: 'istanbul_02', name: '土耳其红茶', category: 'food', rarity: 'core', image: 'images/elements/istanbul/istanbul_02.png', tags: ['tea', 'glass', 'red'] }, { id: 'istanbul_02', name: '加拉塔塔', category: 'landmark', rarity: 'core', image: 'images/elements/istanbul/istanbul_02.png', tags: ['tower'] },
{ id: 'istanbul_03', name: '热气球', category: 'item', rarity: 'core', image: 'images/elements/istanbul/istanbul_03.png', tags: ['balloon', 'sky', 'colorful'] }, { id: 'istanbul_03', name: '土耳其红茶', category: 'food', rarity: 'core', image: 'images/elements/istanbul/istanbul_03.png', tags: ['tea'] },
{ id: 'istanbul_04', name: '烤肉串', category: 'food', rarity: 'core', image: 'images/elements/istanbul/istanbul_04.png', tags: ['kebab', 'grill', 'meat'] }, { id: 'istanbul_04', name: '烤肉串', category: 'food', rarity: 'core', image: 'images/elements/istanbul/istanbul_04.png', tags: ['meat'] },
{ id: 'istanbul_05', name: '郁金香', category: 'nature', rarity: 'core', image: 'images/elements/istanbul/istanbul_05.png', tags: ['flower', 'red', 'spring'] }, { id: 'istanbul_05', name: '土耳其冰淇淋', category: 'food', rarity: 'core', image: 'images/elements/istanbul/istanbul_05.png', tags: ['dessert'] },
{ id: 'istanbul_06', name: '恶魔之眼', category: 'culture', rarity: 'core', image: 'images/elements/istanbul/istanbul_06.png', tags: ['amulet', 'blue', 'protection'] }, { id: 'istanbul_06', name: '旋转舞裙', category: 'culture', rarity: 'core', image: 'images/elements/istanbul/istanbul_06.png', tags: ['dance'] },
{ id: 'istanbul_07', name: '土耳其冰淇淋', category: 'food', rarity: 'core', image: 'images/elements/istanbul/istanbul_07.png', tags: ['dessert', 'stretchy', 'fun'] }, { id: 'istanbul_07', name: '地毯纹样', category: 'culture', rarity: 'core', image: 'images/elements/istanbul/istanbul_07.png', tags: ['pattern'] },
{ id: 'istanbul_08', name: '地毯', category: 'item', rarity: 'core', image: 'images/elements/istanbul/istanbul_08.png', tags: ['textile', 'pattern', 'woven'] }, { id: 'istanbul_08', name: '恶魔之眼', category: 'item', rarity: 'core', image: 'images/elements/istanbul/istanbul_08.png', tags: ['amulet'] },
{ id: 'istanbul_09', name: '石榴', category: 'nature', rarity: 'core', image: 'images/elements/istanbul/istanbul_09.png', tags: ['fruit', 'red', 'juicy'] }, { id: 'istanbul_09', name: '铜咖啡壶', category: 'item', rarity: 'core', image: 'images/elements/istanbul/istanbul_09.png', tags: ['pot'] },
{ id: 'istanbul_10', name: '旋转舞裙', category: 'culture', rarity: 'core', image: 'images/elements/istanbul/istanbul_10.png', tags: ['dance', 'whirl', 'white'] }, { id: 'istanbul_10', name: '郁金香', category: 'nature', rarity: 'core', image: 'images/elements/istanbul/istanbul_10.png', tags: ['flower'] },
{ id: 'istanbul_11', name: '圣索菲亚', category: 'landmark', rarity: 'core', image: 'images/elements/istanbul/istanbul_11.png', tags: ['cathedral', 'dome', 'historic'] }, { id: 'istanbul_11', name: '石榴', category: 'nature', rarity: 'core', image: 'images/elements/istanbul/istanbul_11.png', tags: ['fruit'] },
{ id: 'istanbul_12', name: '土耳其软糖', category: 'food', rarity: 'core', image: 'images/elements/istanbul/istanbul_12.png', tags: ['candy', 'sweet', 'colorful'] }, { id: 'istanbul_12', name: '海鸥', category: 'nature', rarity: 'core', image: 'images/elements/istanbul/istanbul_12.png', tags: ['bird'] },
{ id: 'istanbul_13', name: '波斯猫雕像', category: 'culture', rarity: 'core', image: 'images/elements/istanbul/istanbul_13.png', tags: ['statue', 'cat', 'elegant'] },
{ id: 'istanbul_14', name: '香料', category: 'item', rarity: 'core', image: 'images/elements/istanbul/istanbul_14.png', tags: ['spice', 'bazaar', 'aromatic'] },
],
levelPresets: [
{ id: 1, difficultyTier: 'intro', seedBase: 16001, elementCount: 6, piecesPerElement: 3, layers: 2, density: 'low', targetPassRate: 0.95, targetDurationSec: [60, 120] },
{ id: 2, difficultyTier: 'easy', seedBase: 16002, elementCount: 7, piecesPerElement: 3, layers: 2, density: 'low', targetPassRate: 0.90, targetDurationSec: [60, 120] },
{ id: 3, difficultyTier: 'normal', seedBase: 16003, elementCount: 8, piecesPerElement: 3, layers: 3, density: 'medium', targetPassRate: 0.80, targetDurationSec: [90, 150] },
{ id: 4, difficultyTier: 'normal', seedBase: 16004, elementCount: 9, piecesPerElement: 3, layers: 3, density: 'medium', targetPassRate: 0.70, targetDurationSec: [90, 150] },
{ id: 5, difficultyTier: 'hard', seedBase: 16005, elementCount: 10, piecesPerElement: 3, layers: 4, density: 'medium_high', targetPassRate: 0.60, targetDurationSec: [120, 180] },
{ id: 6, difficultyTier: 'boss', seedBase: 16006, elementCount: 10, piecesPerElement: [3,3,3,3,3,6,3,3,3,3], layers: 4, density: 'high', targetPassRate: 0.55, targetDurationSec: [120, 240] },
], ],
levelPresets: createLevelPresets(16000),
shareCard: { shareCard: {
titleUnlocked: '我解锁了伊斯坦布尔猫!', titleUnlocked: '我解锁了伊斯坦布尔猫!',
titlePassport: '伊斯坦布尔护照盖章完成', titlePassport: '伊斯坦布尔护照盖章完成',
}, },
} }
export default istanbul

View File

@@ -1,9 +1,13 @@
module.exports = { import { createLevelPresets } from '../shared/level-presets.js'
export const seoul = {
id: 'seoul', id: 'seoul',
contentVersion: 1, contentVersion: 1,
continentId: 'asia', continentId: 'asia',
countryId: 'korea',
regionId: null,
sortOrder: 4, sortOrder: 4,
unlockAfterCityId: 'bangkok', unlockAfterCityId: 'singapore',
bundle: { bundle: {
packId: 'asia-rest', packId: 'asia-rest',
preload: 'on-city-page', preload: 'on-city-page',
@@ -11,54 +15,45 @@ module.exports = {
display: { display: {
name: '首尔', name: '首尔',
nameEn: 'Seoul', nameEn: 'Seoul',
bgColor: '#4A90D9', bgColor: '#5C7AEA',
tagline: '景福宫前遇猫猫', tagline: '首尔夜景看猫猫',
funFact: '首尔的地铁系统覆盖率全球第一', funFact: '首尔是东亚流行文化重镇',
}, },
cover: { cover: {
catImage: 'images/cats/cat_seoul.png', catImage: 'images/cats/cat_seoul.png',
catThumb: 'images/cats/cat_seoul_thumb.png', catThumb: 'images/cats/cat_seoul_thumb.png',
shareAccent: '#F5E6CA', shareAccent: '#86A8E7',
}, },
cat: { cat: {
id: 'cat_seoul', id: 'cat_seoul',
name: '韩韩', name: '韩韩',
intro: '戴韩服小帽的韩国短尾猫,爱在景福宫前眨眼卖萌', baseColor: '#B0B7C3',
baseColor: '#F5E6CA', pattern: 'short-tail',
pattern: 'bicolor',
patternColor: '#D4A574',
accessory: '韩服小帽', accessory: '韩服小帽',
expression: 'wink',
}, },
passport: { passport: {
stampId: 'stamp_seoul', stampId: 'stamp_seoul',
stampLabel: '首尔', stampLabel: '首尔',
}, },
elements: [ elements: [
{ id: 'seoul_01', name: '泡菜坛', category: 'food', rarity: 'core', image: 'images/elements/seoul/seoul_01.png', tags: ['fermented', 'jar', 'red'] }, { id: 'seoul_01', name: '景福宫', category: 'landmark', rarity: 'core', image: 'images/elements/seoul/seoul_01.png', tags: ['palace'] },
{ id: 'seoul_02', name: '石锅拌饭', category: 'food', rarity: 'core', image: 'images/elements/seoul/seoul_02.png', tags: ['rice', 'hot', 'colorful'] }, { id: 'seoul_02', name: '南山塔', category: 'landmark', rarity: 'core', image: 'images/elements/seoul/seoul_02.png', tags: ['tower'] },
{ id: 'seoul_03', name: '韩服', category: 'culture', rarity: 'core', image: 'images/elements/seoul/seoul_03.png', tags: ['clothing', 'traditional', 'colorful'] }, { id: 'seoul_03', name: '石锅拌饭', category: 'food', rarity: 'core', image: 'images/elements/seoul/seoul_03.png', tags: ['rice'] },
{ id: 'seoul_04', name: '景福宫', category: 'landmark', rarity: 'core', image: 'images/elements/seoul/seoul_04.png', tags: ['palace', 'historic', 'grand'] }, { id: 'seoul_04', name: '韩式炸鸡', category: 'food', rarity: 'core', image: 'images/elements/seoul/seoul_04.png', tags: ['snack'] },
{ id: 'seoul_05', name: '烧酒瓶', category: 'item', rarity: 'core', image: 'images/elements/seoul/seoul_05.png', tags: ['drink', 'green', 'bottle'] }, { id: 'seoul_05', name: '海苔卷', category: 'food', rarity: 'core', image: 'images/elements/seoul/seoul_05.png', tags: ['roll'] },
{ id: 'seoul_06', name: '年糕', category: 'food', rarity: 'core', image: 'images/elements/seoul/seoul_06.png', tags: ['sweet', 'chewy', 'white'] }, { id: 'seoul_06', name: '韩服', category: 'culture', rarity: 'core', image: 'images/elements/seoul/seoul_06.png', tags: ['dress'] },
{ id: 'seoul_07', name: 'K-pop话筒', category: 'item', rarity: 'core', image: 'images/elements/seoul/seoul_07.png', tags: ['music', 'pop', 'shiny'] }, { id: 'seoul_07', name: 'K-pop话筒', category: 'culture', rarity: 'core', image: 'images/elements/seoul/seoul_07.png', tags: ['music'] },
{ id: 'seoul_08', name: '太极旗扇', category: 'culture', rarity: 'core', image: 'images/elements/seoul/seoul_08.png', tags: ['flag', 'fan', 'national'] }, { id: 'seoul_08', name: '烧酒瓶', category: 'item', rarity: 'core', image: 'images/elements/seoul/seoul_08.png', tags: ['drink'] },
{ id: 'seoul_09', name: '韩式炸鸡', category: 'food', rarity: 'core', image: 'images/elements/seoul/seoul_09.png', tags: ['fried', 'crispy', 'golden'] }, { id: 'seoul_09', name: '太极旗扇', category: 'item', rarity: 'core', image: 'images/elements/seoul/seoul_09.png', tags: ['fan'] },
{ id: 'seoul_10', name: '柿子', category: 'nature', rarity: 'core', image: 'images/elements/seoul/seoul_10.png', tags: ['fruit', 'orange', 'autumn'] }, { id: 'seoul_10', name: '银杏', category: 'nature', rarity: 'core', image: 'images/elements/seoul/seoul_10.png', tags: ['leaf'] },
{ id: 'seoul_11', name: '海苔卷', category: 'food', rarity: 'core', image: 'images/elements/seoul/seoul_11.png', tags: ['seaweed', 'roll', 'green'] }, { id: 'seoul_11', name: '柿子', category: 'nature', rarity: 'core', image: 'images/elements/seoul/seoul_11.png', tags: ['fruit'] },
{ id: 'seoul_12', name: '南山塔', category: 'landmark', rarity: 'core', image: 'images/elements/seoul/seoul_12.png', tags: ['tower', 'romantic', 'iconic'] }, { id: 'seoul_12', name: '松树', category: 'nature', rarity: 'core', image: 'images/elements/seoul/seoul_12.png', tags: ['tree'] },
{ id: 'seoul_13', name: '木槿花', category: 'nature', rarity: 'core', image: 'images/elements/seoul/seoul_13.png', tags: ['flower', 'pink', 'national'] },
],
levelPresets: [
{ id: 1, difficultyTier: 'intro', seedBase: 14001, elementCount: 6, piecesPerElement: 3, layers: 2, density: 'low', targetPassRate: 0.95, targetDurationSec: [60, 120] },
{ id: 2, difficultyTier: 'easy', seedBase: 14002, elementCount: 7, piecesPerElement: 3, layers: 2, density: 'low', targetPassRate: 0.90, targetDurationSec: [60, 120] },
{ id: 3, difficultyTier: 'normal', seedBase: 14003, elementCount: 8, piecesPerElement: 3, layers: 3, density: 'medium', targetPassRate: 0.80, targetDurationSec: [90, 150] },
{ id: 4, difficultyTier: 'normal', seedBase: 14004, elementCount: 9, piecesPerElement: 3, layers: 3, density: 'medium', targetPassRate: 0.70, targetDurationSec: [90, 150] },
{ id: 5, difficultyTier: 'hard', seedBase: 14005, elementCount: 10, piecesPerElement: 3, layers: 4, density: 'medium_high', targetPassRate: 0.60, targetDurationSec: [120, 180] },
{ id: 6, difficultyTier: 'boss', seedBase: 14006, elementCount: 10, piecesPerElement: [3,3,3,3,3,6,3,3,3,3], layers: 4, density: 'high', targetPassRate: 0.55, targetDurationSec: [120, 240] },
], ],
levelPresets: createLevelPresets(14000),
shareCard: { shareCard: {
titleUnlocked: '我解锁了首尔猫!', titleUnlocked: '我解锁了首尔猫!',
titlePassport: '首尔护照盖章完成', titlePassport: '首尔护照盖章完成',
}, },
} }
export default seoul

View File

@@ -1,9 +1,13 @@
module.exports = { import { createLevelPresets } from '../shared/level-presets.js'
export const singapore = {
id: 'singapore', id: 'singapore',
contentVersion: 1, contentVersion: 1,
continentId: 'asia', continentId: 'asia',
countryId: 'singapore_country',
regionId: null,
sortOrder: 5, sortOrder: 5,
unlockAfterCityId: 'seoul', unlockAfterCityId: 'istanbul',
bundle: { bundle: {
packId: 'asia-rest', packId: 'asia-rest',
preload: 'on-city-page', preload: 'on-city-page',
@@ -11,54 +15,45 @@ module.exports = {
display: { display: {
name: '新加坡', name: '新加坡',
nameEn: 'Singapore', nameEn: 'Singapore',
bgColor: '#2ECC71', bgColor: '#00A896',
tagline: '鱼尾狮旁撸猫猫', tagline: '海湾花园看猫猫',
funFact: '新加坡是全球绿化覆盖率最高的城市之一', funFact: '新加坡是花园城市国家',
}, },
cover: { cover: {
catImage: 'images/cats/cat_singapore.png', catImage: 'images/cats/cat_singapore.png',
catThumb: 'images/cats/cat_singapore_thumb.png', catThumb: 'images/cats/cat_singapore_thumb.png',
shareAccent: '#F28C28', shareAccent: '#02C39A',
}, },
cat: { cat: {
id: 'cat_singapore', id: 'cat_singapore',
name: '狮', name: '狮',
intro: '戴小狮子鬃毛的花猫,骄傲地守护鱼尾狮', baseColor: '#F2C078',
baseColor: '#FFFFFF',
pattern: 'calico', pattern: 'calico',
patternColor: '#F28C28', accessory: '小狮鬃',
accessory: '小狮子鬃毛',
expression: 'proud',
}, },
passport: { passport: {
stampId: 'stamp_singapore', stampId: 'stamp_singapore',
stampLabel: '新加坡', stampLabel: '新加坡',
}, },
elements: [ elements: [
{ id: 'singapore_01', name: '鱼尾狮', category: 'landmark', rarity: 'core', image: 'images/elements/singapore/singapore_01.png', tags: ['statue', 'water', 'iconic'] }, { id: 'singapore_01', name: '鱼尾狮', category: 'landmark', rarity: 'core', image: 'images/elements/singapore/singapore_01.png', tags: ['statue'] },
{ id: 'singapore_02', name: '辣椒螃蟹', category: 'food', rarity: 'core', image: 'images/elements/singapore/singapore_02.png', tags: ['seafood', 'spicy', 'red'] }, { id: 'singapore_02', name: '金沙酒店', category: 'landmark', rarity: 'core', image: 'images/elements/singapore/singapore_02.png', tags: ['hotel'] },
{ id: 'singapore_03', name: '榴莲建筑', category: 'landmark', rarity: 'core', image: 'images/elements/singapore/singapore_03.png', tags: ['theater', 'spiky', 'modern'] }, { id: 'singapore_03', name: '叻沙', category: 'food', rarity: 'core', image: 'images/elements/singapore/singapore_03.png', tags: ['noodle'] },
{ id: 'singapore_04', name: '叻沙', category: 'food', rarity: 'core', image: 'images/elements/singapore/singapore_04.png', tags: ['noodle', 'coconut', 'spicy'] }, { id: 'singapore_04', name: '辣椒螃蟹', category: 'food', rarity: 'core', image: 'images/elements/singapore/singapore_04.png', tags: ['crab'] },
{ id: 'singapore_05', name: '金沙酒店', category: 'landmark', rarity: 'core', image: 'images/elements/singapore/singapore_05.png', tags: ['hotel', 'pool', 'skyline'] }, { id: 'singapore_05', name: '咖椰吐司', category: 'food', rarity: 'core', image: 'images/elements/singapore/singapore_05.png', tags: ['toast'] },
{ id: 'singapore_06', name: '兰花', category: 'nature', rarity: 'core', image: 'images/elements/singapore/singapore_06.png', tags: ['flower', 'purple', 'national'] }, { id: 'singapore_06', name: '娘惹花砖', category: 'culture', rarity: 'core', image: 'images/elements/singapore/singapore_06.png', tags: ['tile'] },
{ id: 'singapore_07', name: '肉骨茶', category: 'food', rarity: 'core', image: 'images/elements/singapore/singapore_07.png', tags: ['soup', 'herbal', 'pork'] }, { id: 'singapore_07', name: '小红点贴纸', category: 'culture', rarity: 'core', image: 'images/elements/singapore/singapore_07.png', tags: ['badge'] },
{ id: 'singapore_08', name: '冰激凌三明治', category: 'food', rarity: 'core', image: 'images/elements/singapore/singapore_08.png', tags: ['dessert', 'cold', 'colorful'] }, { id: 'singapore_08', name: '冰淇淋三明治', category: 'item', rarity: 'core', image: 'images/elements/singapore/singapore_08.png', tags: ['cart'] },
{ id: 'singapore_09', name: '组屋', category: 'culture', rarity: 'core', image: 'images/elements/singapore/singapore_09.png', tags: ['housing', 'colorful', 'block'] }, { id: 'singapore_09', name: '组屋钥匙', category: 'item', rarity: 'core', image: 'images/elements/singapore/singapore_09.png', tags: ['key'] },
{ id: 'singapore_10', name: '咖椰吐司', category: 'food', rarity: 'core', image: 'images/elements/singapore/singapore_10.png', tags: ['toast', 'jam', 'breakfast'] }, { id: 'singapore_10', name: '兰花', category: 'nature', rarity: 'core', image: 'images/elements/singapore/singapore_10.png', tags: ['flower'] },
{ id: 'singapore_11', name: '摩天轮', category: 'item', rarity: 'core', image: 'images/elements/singapore/singapore_11.png', tags: ['wheel', 'night', 'view'] }, { id: 'singapore_11', name: '榴莲', category: 'nature', rarity: 'core', image: 'images/elements/singapore/singapore_11.png', tags: ['fruit'] },
{ id: 'singapore_12', name: '热带雨林', category: 'nature', rarity: 'core', image: 'images/elements/singapore/singapore_12.png', tags: ['forest', 'green', 'tropical'] }, { id: 'singapore_12', name: '雨树', category: 'nature', rarity: 'core', image: 'images/elements/singapore/singapore_12.png', tags: ['tree'] },
{ id: 'singapore_13', name: '娘惹瓷砖', category: 'culture', rarity: 'core', image: 'images/elements/singapore/singapore_13.png', tags: ['tile', 'pattern', 'peranakan'] },
],
levelPresets: [
{ id: 1, difficultyTier: 'intro', seedBase: 15001, elementCount: 6, piecesPerElement: 3, layers: 2, density: 'low', targetPassRate: 0.95, targetDurationSec: [60, 120] },
{ id: 2, difficultyTier: 'easy', seedBase: 15002, elementCount: 7, piecesPerElement: 3, layers: 2, density: 'low', targetPassRate: 0.90, targetDurationSec: [60, 120] },
{ id: 3, difficultyTier: 'normal', seedBase: 15003, elementCount: 8, piecesPerElement: 3, layers: 3, density: 'medium', targetPassRate: 0.80, targetDurationSec: [90, 150] },
{ id: 4, difficultyTier: 'normal', seedBase: 15004, elementCount: 9, piecesPerElement: 3, layers: 3, density: 'medium', targetPassRate: 0.70, targetDurationSec: [90, 150] },
{ id: 5, difficultyTier: 'hard', seedBase: 15005, elementCount: 10, piecesPerElement: 3, layers: 4, density: 'medium_high', targetPassRate: 0.60, targetDurationSec: [120, 180] },
{ id: 6, difficultyTier: 'boss', seedBase: 15006, elementCount: 10, piecesPerElement: [3,3,3,3,3,6,3,3,3,3], layers: 4, density: 'high', targetPassRate: 0.55, targetDurationSec: [120, 240] },
], ],
levelPresets: createLevelPresets(15000),
shareCard: { shareCard: {
titleUnlocked: '我解锁了新加坡猫!', titleUnlocked: '我解锁了新加坡猫!',
titlePassport: '新加坡护照盖章完成', titlePassport: '新加坡护照盖章完成',
}, },
} }
export default singapore

View File

@@ -1,9 +1,13 @@
module.exports = { import { createLevelPresets } from '../shared/level-presets.js'
export const tokyo = {
id: 'tokyo', id: 'tokyo',
contentVersion: 1, contentVersion: 1,
continentId: 'asia', continentId: 'asia',
countryId: 'japan',
regionId: null,
sortOrder: 2, sortOrder: 2,
unlockAfterCityId: 'beijing', unlockAfterCityId: 'bangkok',
bundle: { bundle: {
packId: 'asia-rest', packId: 'asia-rest',
preload: 'on-city-page', preload: 'on-city-page',
@@ -11,55 +15,45 @@ module.exports = {
display: { display: {
name: '东京', name: '东京',
nameEn: 'Tokyo', nameEn: 'Tokyo',
bgColor: '#E84057', bgColor: '#FF6F91',
tagline: '东京タワー下的喵', tagline: '樱花街头看猫猫',
funFact: '东京的铁路网是世界上最密集的', funFact: '东京拥有世界级的铁路网络',
}, },
cover: { cover: {
catImage: 'images/cats/cat_tokyo.png', catImage: 'images/cats/cat_tokyo.png',
catThumb: 'images/cats/cat_tokyo_thumb.png', catThumb: 'images/cats/cat_tokyo_thumb.png',
shareAccent: '#FFB7C5', shareAccent: '#FF9671',
}, },
cat: { cat: {
id: 'cat_tokyo', id: 'cat_tokyo',
name: '樱', name: '樱',
intro: '戴着招财猫铃铛的白猫,喜欢在樱花树下打盹', baseColor: '#F7F7F7',
baseColor: '#FFFFFF',
pattern: 'solid', pattern: 'solid',
patternColor: '#F5F5F5', accessory: '铃铛',
accessory: '招财猫铃铛',
expression: 'wink',
}, },
passport: { passport: {
stampId: 'stamp_tokyo', stampId: 'stamp_tokyo',
stampLabel: '东京', stampLabel: '东京',
}, },
elements: [ elements: [
{ id: 'tokyo_01', name: '寿司', category: 'food', rarity: 'core', image: 'images/elements/tokyo/tokyo_01.png', tags: ['fish', 'rice', 'fresh'] }, { id: 'tokyo_01', name: '东京塔', category: 'landmark', rarity: 'core', image: 'images/elements/tokyo/tokyo_01.png', tags: ['tower'] },
{ id: 'tokyo_02', name: '招财猫', category: 'culture', rarity: 'core', image: 'images/elements/tokyo/tokyo_02.png', tags: ['lucky', 'cat', 'golden'] }, { id: 'tokyo_02', name: '鸟居', category: 'landmark', rarity: 'core', image: 'images/elements/tokyo/tokyo_02.png', tags: ['gate'] },
{ id: 'tokyo_03', name: '富士山', category: 'landmark', rarity: 'core', image: 'images/elements/tokyo/tokyo_03.png', tags: ['mountain', 'snow', 'iconic'] }, { id: 'tokyo_03', name: '寿司', category: 'food', rarity: 'core', image: 'images/elements/tokyo/tokyo_03.png', tags: ['rice'] },
{ id: 'tokyo_04', name: '樱花', category: 'nature', rarity: 'core', image: 'images/elements/tokyo/tokyo_04.png', tags: ['flower', 'pink', 'spring'] }, { id: 'tokyo_04', name: '拉面', category: 'food', rarity: 'core', image: 'images/elements/tokyo/tokyo_04.png', tags: ['noodle'] },
{ id: 'tokyo_05', name: '鸟居', category: 'landmark', rarity: 'core', image: 'images/elements/tokyo/tokyo_05.png', tags: ['gate', 'red', 'shrine'] }, { id: 'tokyo_05', name: '章鱼烧', category: 'food', rarity: 'core', image: 'images/elements/tokyo/tokyo_05.png', tags: ['snack'] },
{ id: 'tokyo_06', name: '拉面', category: 'food', rarity: 'core', image: 'images/elements/tokyo/tokyo_06.png', tags: ['noodle', 'hot', 'bowl'] }, { id: 'tokyo_06', name: '招财猫', category: 'culture', rarity: 'core', image: 'images/elements/tokyo/tokyo_06.png', tags: ['lucky'] },
{ id: 'tokyo_07', name: '抹茶', category: 'food', rarity: 'core', image: 'images/elements/tokyo/tokyo_07.png', tags: ['tea', 'green', 'powder'] }, { id: 'tokyo_07', name: '浮世绘', category: 'culture', rarity: 'core', image: 'images/elements/tokyo/tokyo_07.png', tags: ['art'] },
{ id: 'tokyo_08', name: '浮世绘', category: 'culture', rarity: 'core', image: 'images/elements/tokyo/tokyo_08.png', tags: ['art', 'wave', 'woodblock'] }, { id: 'tokyo_08', name: '和服扇', category: 'item', rarity: 'core', image: 'images/elements/tokyo/tokyo_08.png', tags: ['fan'] },
{ id: 'tokyo_09', name: '新干线', category: 'item', rarity: 'core', image: 'images/elements/tokyo/tokyo_09.png', tags: ['train', 'fast', 'white'] }, { id: 'tokyo_09', name: '新干线', category: 'item', rarity: 'core', image: 'images/elements/tokyo/tokyo_09.png', tags: ['ticket'] },
{ id: 'tokyo_10', name: '达摩', category: 'culture', rarity: 'core', image: 'images/elements/tokyo/tokyo_10.png', tags: ['doll', 'red', 'wish'] }, { id: 'tokyo_10', name: '樱花', category: 'nature', rarity: 'core', image: 'images/elements/tokyo/tokyo_10.png', tags: ['flower'] },
{ id: 'tokyo_11', name: '和服扇', category: 'item', rarity: 'core', image: 'images/elements/tokyo/tokyo_11.png', tags: ['fan', 'elegant', 'pattern'] }, { id: 'tokyo_11', name: '富士山', category: 'nature', rarity: 'core', image: 'images/elements/tokyo/tokyo_11.png', tags: ['mountain'] },
{ id: 'tokyo_12', name: '章鱼烧', category: 'food', rarity: 'core', image: 'images/elements/tokyo/tokyo_12.png', tags: ['octopus', 'ball', 'street-food'] }, { id: 'tokyo_12', name: '锦鲤', category: 'nature', rarity: 'core', image: 'images/elements/tokyo/tokyo_12.png', tags: ['fish'] },
{ id: 'tokyo_13', name: '东京塔', category: 'landmark', rarity: 'accent', image: 'images/elements/tokyo/tokyo_13.png', tags: ['tower', 'orange', 'night'] },
{ id: 'tokyo_14', name: '柴犬', category: 'nature', rarity: 'accent', image: 'images/elements/tokyo/tokyo_14.png', tags: ['dog', 'cute', 'fluffy'] },
],
levelPresets: [
{ id: 1, difficultyTier: 'intro', seedBase: 12001, elementCount: 6, piecesPerElement: 3, layers: 2, density: 'low', targetPassRate: 0.95, targetDurationSec: [60, 120] },
{ id: 2, difficultyTier: 'easy', seedBase: 12002, elementCount: 7, piecesPerElement: 3, layers: 2, density: 'low', targetPassRate: 0.90, targetDurationSec: [60, 120] },
{ id: 3, difficultyTier: 'normal', seedBase: 12003, elementCount: 8, piecesPerElement: 3, layers: 3, density: 'medium', targetPassRate: 0.80, targetDurationSec: [90, 150] },
{ id: 4, difficultyTier: 'normal', seedBase: 12004, elementCount: 9, piecesPerElement: 3, layers: 3, density: 'medium', targetPassRate: 0.70, targetDurationSec: [90, 150] },
{ id: 5, difficultyTier: 'hard', seedBase: 12005, elementCount: 10, piecesPerElement: 3, layers: 4, density: 'medium_high', targetPassRate: 0.60, targetDurationSec: [120, 180] },
{ id: 6, difficultyTier: 'boss', seedBase: 12006, elementCount: 10, piecesPerElement: [3,3,3,3,3,6,3,3,3,3], layers: 4, density: 'high', targetPassRate: 0.55, targetDurationSec: [120, 240] },
], ],
levelPresets: createLevelPresets(12000),
shareCard: { shareCard: {
titleUnlocked: '我解锁了东京猫!', titleUnlocked: '我解锁了东京猫!',
titlePassport: '东京护照盖章完成', titlePassport: '东京护照盖章完成',
}, },
} }
export default tokyo

View File

@@ -1,32 +1,5 @@
// 亚洲国家列表 — 16 国,分 2 页 /**
* @deprecated 亚洲国家列表已移入 navigation/future-catalog.js
export const asiaCountries = [ * MVP 阶段亚洲直接列出城市,不经过国家层。
// Page 1 */
{ id: 'china', name: '中国', nameEn: 'China', sortOrder: 1, themeColor: '#CC2936', hasRegions: true, totalCities: 40 }, export { asiaCountryPlan as asiaCountries } from '../navigation/future-catalog.js'
{ id: 'japan', name: '日本', nameEn: 'Japan', sortOrder: 2, themeColor: '#E84057', hasRegions: false, totalCities: 4 },
{ id: 'korea', name: '韩国', nameEn: 'South Korea', sortOrder: 3, themeColor: '#4A90D9', hasRegions: false, totalCities: 3 },
{ id: 'thailand', name: '泰国', nameEn: 'Thailand', sortOrder: 4, themeColor: '#FFB347', hasRegions: false, totalCities: 3 },
{ id: 'singapore', name: '新加坡', nameEn: 'Singapore', sortOrder: 5, themeColor: '#00A896', hasRegions: false, totalCities: 1 },
{ id: 'vietnam', name: '越南', nameEn: 'Vietnam', sortOrder: 6, themeColor: '#E74C3C', hasRegions: false, totalCities: 2 },
{ id: 'malaysia', name: '马来西亚', nameEn: 'Malaysia', sortOrder: 7, themeColor: '#3498DB', hasRegions: false, totalCities: 2 },
{ id: 'indonesia', name: '印度尼西亚', nameEn: 'Indonesia', sortOrder: 8, themeColor: '#E67E22', hasRegions: false, totalCities: 2 },
{ id: 'philippines', name: '菲律宾', nameEn: 'Philippines', sortOrder: 9, themeColor: '#2ECC71', hasRegions: false, totalCities: 1 },
// Page 2
{ id: 'india', name: '印度', nameEn: 'India', sortOrder: 10, themeColor: '#FF9933', hasRegions: false, totalCities: 2 },
{ id: 'uae', name: '阿联酋', nameEn: 'UAE', sortOrder: 11, themeColor: '#C0A062', hasRegions: false, totalCities: 1 },
{ id: 'turkey', name: '土耳其', nameEn: 'Turkey', sortOrder: 12, themeColor: '#1A5276', hasRegions: false, totalCities: 1 },
{ id: 'israel', name: '以色列', nameEn: 'Israel', sortOrder: 13, themeColor: '#5DADE2', hasRegions: false, totalCities: 1 },
{ id: 'nepal', name: '尼泊尔', nameEn: 'Nepal', sortOrder: 14, themeColor: '#DC143C', hasRegions: false, totalCities: 1 },
{ id: 'cambodia', name: '柬埔寨', nameEn: 'Cambodia', sortOrder: 15, themeColor: '#1E8449', hasRegions: false, totalCities: 1 },
{ id: 'sri_lanka', name: '斯里兰卡', nameEn: 'Sri Lanka', sortOrder: 16, themeColor: '#8E44AD', hasRegions: false, totalCities: 1 },
{ id: 'myanmar', name: '缅甸', nameEn: 'Myanmar', sortOrder: 17, themeColor: '#F4D03F', hasRegions: false, totalCities: 1 },
{ id: 'mongolia', name: '蒙古', nameEn: 'Mongolia', sortOrder: 18, themeColor: '#2980B9', hasRegions: false, totalCities: 1 },
]
// 分页2 页
export const asiaCountryPages = [
asiaCountries.slice(0, 9),
asiaCountries.slice(9),
]
export default asiaCountries

View File

@@ -1,16 +1,5 @@
// 洲索引 — 3x3 棋盘第 1 级 /**
// 每页 9 格,当前 6 个洲 + 3 空位 * @deprecated 请使用 js/content/navigation/runtime-nav.js
* 此文件保留仅为向后兼容,内容从 runtime-nav 导出。
export const continents = [ */
{ id: 'asia', name: '亚洲', nameEn: 'Asia', sortOrder: 1, themeColor: '#FF6B6B', icon: '🏯', totalCities: 70 }, export { runtimeNavNodes as continents, getEnabledRoots, getNavNode } from '../navigation/runtime-nav.js'
{ id: 'europe', name: '欧洲', nameEn: 'Europe', sortOrder: 2, themeColor: '#6B8CFF', icon: '🏰', totalCities: 28 },
{ id: 'north_america', name: '北美洲', nameEn: 'North America', sortOrder: 3, themeColor: '#FFB347', icon: '🗽', totalCities: 12 },
{ id: 'south_america', name: '南美洲', nameEn: 'South America', sortOrder: 4, themeColor: '#4ECDC4', icon: '🌴', totalCities: 7 },
{ id: 'africa', name: '非洲', nameEn: 'Africa', sortOrder: 5, themeColor: '#F7DC6F', icon: '🦁', totalCities: 5 },
{ id: 'oceania', name: '大洋洲', nameEn: 'Oceania', sortOrder: 6, themeColor: '#82E0AA', icon: '🦘', totalCities: 4 },
]
// 分页1 页6 项 ≤ 9
export const continentPages = [continents]
export default continents

View File

@@ -1,7 +1,6 @@
export const japanCities = { /**
countryId: 'japan', * @deprecated 国家城市列表已移入 navigation/future-catalog.js
cityIds: ['tokyo', 'osaka', 'kyoto', 'sapporo'], */
// 4 城市1 页 import { asiaCountryPlan } from '../navigation/future-catalog.js'
} export const japanCities = asiaCountryPlan.find(c => c.id === 'japan')
export default japanCities export default japanCities

View File

@@ -0,0 +1,50 @@
/**
* 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'] },
]

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
}

View File

@@ -0,0 +1,110 @@
/**
* Runtime Navigation — MVP
*
* 只包含当前版本已启用、childIds 均指向已存在实体的节点。
* MVP 导航直入亚洲城市页6 城市平铺。
* V1.1+ 启用完整洲→国家→地区→城市多级导航时,在此文件追加节点。
*/
export const runtimeNavNodes = [
// ── MVP: 只有 1 个洲,直接列出 6 城市 ──
{
type: 'continent',
id: 'asia',
parentId: null,
name: '亚洲',
nameEn: 'Asia',
sortOrder: 1,
themeColor: '#FF6B6B',
childType: 'city', // MVP: 直接指向城市,跳过国家层
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,
},
]
// ── 便捷查询 ──
export function getNavNode(id) {
return runtimeNavNodes.find(n => n.id === id) || null
}
export function getEnabledRoots() {
return runtimeNavNodes.filter(n => n.parentId === null && n.isEnabled)
}
export function getChildren(parentId) {
return runtimeNavNodes.filter(n => n.parentId === parentId && n.isEnabled)
}

View File

@@ -1,73 +1,5 @@
// 中国地区列表 — 8 个地区1 页 /**
* @deprecated 中国地区数据已移入 navigation/future-catalog.js
export const chinaRegions = [ * 仅在 V1.1+ 启用国家→地区导航后使用。
{ */
id: 'north_china', export { chinaRegionPlan as chinaRegions } from '../navigation/future-catalog.js'
name: '华北',
nameEn: 'North China',
sortOrder: 1,
themeColor: '#CC2936',
cityIds: ['beijing', 'tianjin', 'shijiazhuang', 'taiyuan', 'hohhot'],
},
{
id: 'northeast',
name: '东北',
nameEn: 'Northeast',
sortOrder: 2,
themeColor: '#5DADE2',
cityIds: ['shenyang', 'changchun', 'harbin', 'dalian'],
},
{
id: 'east_china',
name: '华东',
nameEn: 'East China',
sortOrder: 3,
themeColor: '#2ECC71',
cityIds: ['shanghai', 'nanjing', 'hangzhou', 'hefei', 'fuzhou', 'nanchang', 'jinan', 'suzhou', 'xiamen', 'qingdao', 'ningbo'],
},
{
id: 'central_china',
name: '华中',
nameEn: 'Central China',
sortOrder: 4,
themeColor: '#E67E22',
cityIds: ['zhengzhou', 'wuhan', 'changsha'],
},
{
id: 'south_china',
name: '华南',
nameEn: 'South China',
sortOrder: 5,
themeColor: '#FF6B6B',
cityIds: ['guangzhou', 'shenzhen', 'nanning', 'haikou', 'sanya'],
},
{
id: 'southwest',
name: '西南',
nameEn: 'Southwest',
sortOrder: 6,
themeColor: '#9B59B6',
cityIds: ['chengdu', 'chongqing', 'guiyang', 'kunming', 'lhasa'],
},
{
id: 'northwest',
name: '西北',
nameEn: 'Northwest',
sortOrder: 7,
themeColor: '#F4D03F',
cityIds: ['xian', 'lanzhou', 'xining', 'yinchuan', 'urumqi'],
},
{
id: 'hk_macao_tw',
name: '港澳台',
nameEn: 'HK, Macao & Taiwan',
sortOrder: 8,
themeColor: '#E74C3C',
cityIds: ['hongkong', 'macao', 'taipei'],
},
]
// 分页1 页8 项 ≤ 9
export const chinaRegionPages = [chinaRegions]
export default chinaRegions