nav: 三级导航分页结构 + 中国地区索引

- 02-game-design 地图结构重写:洲→国家→城市,3x3棋盘分页
- 中国 40 城按 8 地区分组(华北/东北/华东/华中/华南/西南/西北/港澳)
- 导航索引数据:continents/index + asia + regions/china + pagination工具

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
manpengan
2026-03-28 23:20:58 +08:00
parent 2d960632d6
commit faeb453a64
7 changed files with 355 additions and 21 deletions

View File

@@ -75,38 +75,160 @@
## 2. 地图结构
### 2.1 导航层级MVP 简化为二级)
### 2.1 导航层级
```
MVP 首版:直接进入亚洲城市页(跳过洲选择页
城市选择3x3 棋盘 → 城市关卡(单局游戏
V1.1 扩展第二个洲后:
洲选择3x3 棋盘) → 城市选择3x3 棋盘) → 城市关卡
3x3 棋盘
└── 国家3x3 棋盘,超过 9 国分页
└── 城市3x3 棋盘,超过 9 城分页)
└── 关卡(单局游戏)
```
预留洲选择页的代码入口但 MVP 不显示。
中国因城市数量多40 城),额外增加"地区"层级:
### 2.2 主页面设计
```
亚洲 → 中国 → 地区3x3→ 城市3x3→ 关卡
亚洲 → 日本 → 城市3x3→ 关卡
```
- MVP 直接显示亚洲城市 3x3 棋盘,顶部标题"亚洲"
- V1.1 扩展第二个洲后再开启洲选择页
### 2.2 洲选择页(第 1 级)
### 2.3 洲内页面设计
1 页3×3 棋盘:
- 3x3 棋盘布局,每格代表一个城市
- 城市封面:特色猫猫头像 + 城市名
- 已通关城市:猫猫头像点亮 + 通关星级
- 未解锁城市:灰色剪影 + 锁图标
- 解锁规则:顺序解锁(通关前一个解锁下一个),第 1 个城市默认解锁
```
┌────────┬────────┬────────┐
│ 亚洲 │ 欧洲 │ 北美洲 │
├────────┼────────┼────────┤
│ 南美洲 │ 非洲 │ 大洋洲 │
├────────┼────────┼────────┤
│ │ │ │
└────────┴────────┴────────┘
```
### 2.4 城市关卡结构
6 个洲3 个空位留给未来扩展。MVP 只开放亚洲,其余显示锁定 + "即将开放"。
每个城市包含 **6 个基础关卡**,全部通关才算"通关该城市"
每个洲的封面:洲标志性图案 + 解锁进度(如"6/70 城市已解锁"
- 每关使用该城市 12-15 个特色元素中的 8-12 个
- 难度递增:关 1 用 6 种元素各 3 个,关 6 用 10 种元素,各 3 或 6 个(混合配置) + 更深堆叠
- 通关后获得该城市的猫猫收集卡
### 2.3 国家选择页(第 2 级)
#### 亚洲 — 2 页
Page 1:
```
┌────────┬────────┬────────┐
│ 中国 │ 日本 │ 韩国 │
├────────┼────────┼────────┤
│ 泰国 │ 新加坡 │ 越南 │
├────────┼────────┼────────┤
│马来西亚│印度尼西亚│ 菲律宾│
└────────┴────────┴────────┘
```
Page 2:
```
┌────────┬────────┬────────┐
│ 印度 │ 阿联酋 │ 土耳其 │
├────────┼────────┼────────┤
│ 以色列 │ 尼泊尔 │ 柬埔寨 │
├────────┼────────┼────────┤
│斯里兰卡│ 缅甸 │ 蒙古 │
└────────┴────────┴────────┘
```
#### 欧洲 — 2 页
Page 1:
```
法国、英国、意大利、西班牙、德国、荷兰、瑞士、奥地利、捷克
```
Page 2:
```
希腊、俄罗斯、葡萄牙、挪威、瑞典、丹麦、芬兰、冰岛、匈牙利
```
#### 北美洲 — 1 页
```
美国、加拿大、墨西哥 + 6 空位
```
#### 南美洲 — 1 页
```
巴西、阿根廷、秘鲁、智利、哥伦比亚 + 4 空位
```
#### 非洲 — 1 页
```
埃及、南非、摩洛哥、肯尼亚 + 5 空位
```
#### 大洋洲 — 1 页
```
澳大利亚、新西兰 + 7 空位
```
### 2.4 中国地区选择页(第 3 级,仅中国)
中国 40 个城市按地区分组1 页 3×3
```
┌────────┬────────┬────────┐
│ 华北 │ 东北 │ 华东 │
├────────┼────────┼────────┤
│ 华中 │ 华南 │ 西南 │
├────────┼────────┼────────┤
│ 西北 │ 港澳 │ │
└────────┴────────┴────────┘
```
各地区包含城市:
| 地区 | 城市 | 数量 | 页数 |
|------|------|------|------|
| 华北 | 北京、天津、石家庄、太原、呼和浩特 | 5 | 1 |
| 东北 | 沈阳、长春、哈尔滨、大连 | 4 | 1 |
| 华东 | 上海、南京、杭州、合肥、福州、南昌、济南、苏州、厦门、青岛、宁波 | 11 | 2 |
| 华中 | 郑州、武汉、长沙 | 3 | 1 |
| 华南 | 广州、深圳、南宁、海口、三亚 | 5 | 1 |
| 西南 | 成都、重庆、贵阳、昆明、拉萨 | 5 | 1 |
| 西北 | 西安、兰州、西宁、银川、乌鲁木齐 | 5 | 1 |
| 港澳 | 香港、澳门 | 2 | 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 关",未解锁:"🔒"
### 2.7 分页交互
- 左右滑动翻页,底部圆点指示器
- 首次进入默认第 1 页
- 翻页动画 0.3s 弹性过渡
### 2.8 解锁规则
- 同一层级内顺序解锁(通关前一个解锁下一个)
- 第一个项目默认解锁
- 跨层级:通关一个国家所有城市后,解锁下一个国家
- 中国地区内:通关华北所有城市后解锁东北,以此类推
- MVP 只开放亚洲洲,亚洲内只开放中国(北京)和已有 MVP 城市
---

View File

@@ -0,0 +1,32 @@
// 亚洲国家列表 — 16 国,分 2 页
export const asiaCountries = [
// Page 1
{ id: 'china', name: '中国', nameEn: 'China', sortOrder: 1, themeColor: '#CC2936', hasRegions: true, totalCities: 40 },
{ 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

@@ -0,0 +1,16 @@
// 洲索引 — 3x3 棋盘第 1 级
// 每页 9 格,当前 6 个洲 + 3 空位
export const continents = [
{ id: 'asia', name: '亚洲', nameEn: 'Asia', sortOrder: 1, themeColor: '#FF6B6B', icon: '🏯', totalCities: 70 },
{ 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

@@ -0,0 +1,7 @@
export const japanCities = {
countryId: 'japan',
cityIds: ['tokyo', 'osaka', 'kyoto', 'sapporo'],
// 4 城市1 页
}
export default japanCities

View File

@@ -0,0 +1,73 @@
// 中国地区列表 — 8 个地区1 页
export const chinaRegions = [
{
id: 'north_china',
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',
name: '港澳',
nameEn: 'HK & Macao',
sortOrder: 8,
themeColor: '#E74C3C',
cityIds: ['hongkong', 'macao'],
},
]
// 分页1 页8 项 ≤ 9
export const chinaRegionPages = [chinaRegions]
export default chinaRegions

View File

@@ -0,0 +1,70 @@
export function createLevelPresets(seedBase) {
return [
{
id: 1,
difficultyTier: 'intro',
seedBase: seedBase + 1,
elementCount: 6,
piecesPerElement: 3,
layers: 2,
density: 'low',
targetPassRate: 0.95,
targetDurationSec: [90, 150],
},
{
id: 2,
difficultyTier: 'warmup',
seedBase: seedBase + 2,
elementCount: 7,
piecesPerElement: 3,
layers: 2,
density: 'low',
targetPassRate: 0.9,
targetDurationSec: [100, 160],
},
{
id: 3,
difficultyTier: 'ramp',
seedBase: seedBase + 3,
elementCount: 8,
piecesPerElement: 3,
layers: 3,
density: 'medium',
targetPassRate: 0.8,
targetDurationSec: [120, 180],
},
{
id: 4,
difficultyTier: 'mid',
seedBase: seedBase + 4,
elementCount: 9,
piecesPerElement: 3,
layers: 3,
density: 'medium',
targetPassRate: 0.7,
targetDurationSec: [130, 190],
},
{
id: 5,
difficultyTier: 'hard',
seedBase: seedBase + 5,
elementCount: 10,
piecesPerElement: 3,
layers: 4,
density: 'medium_high',
targetPassRate: 0.6,
targetDurationSec: [150, 220],
},
{
id: 6,
difficultyTier: 'boss',
seedBase: seedBase + 6,
elementCount: 10,
piecesPerElement: [3, 3, 3, 3, 3, 6, 3, 3, 3, 3],
layers: 4,
density: 'high',
targetPassRate: 0.55,
targetDurationSec: [180, 260],
},
]
}

View File

@@ -0,0 +1,14 @@
// 通用分页工具 — 将数组按 pageSize 分页
// 默认 pageSize = 93x3 棋盘)
export function paginate(items, pageSize = 9) {
const pages = []
for (let i = 0; i < items.length; i += pageSize) {
pages.push(items.slice(i, i + pageSize))
}
return pages
}
export function getPageCount(totalItems, pageSize = 9) {
return Math.ceil(totalItems / pageSize)
}