Skip to content

Commit 1bbfbeb

Browse files
committed
feat(mobile): 动态化手机应用内容
将聊天、论坛、资讯应用的内容从静态占位符改为由游戏上下文(gameContext)动态生成。 - **聊天 (ChatApp)**: 门派群聊人数计算逻辑简化,暂时使用全部社交NPC近似。 - **论坛 (ForumApp)**: 帖子列表现在根据游戏世界中的“进行中事件”、“已结算事件”和“世界镜头”动态生成,取代了静态帖子。 - **资讯 (NewsApp)**: 新闻列表现在根据游戏中的“当前章节”、“下一章预告”和“世界事件/镜头”动态生成,取代了静态新闻。 - **通用**: 为论坛和资讯应用增加了数据为空时的占位符界面。 同时,新增了关于时代适配预设项的规划文档 `era-presets-plan.md`。
1 parent c6a9254 commit 1bbfbeb

4 files changed

Lines changed: 315 additions & 152 deletions

File tree

components/features/MobileDevice/apps/ChatApp.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,16 +36,15 @@ const ChatApp: React.FC<AppProps> = ({ eraId, mode, appId, onBack, gameContext }
3636
const ctx = gameContext;
3737
if (!ctx) return result;
3838

39-
// 门派群
39+
// 门派群 — 用全体NPC近似
4040
if (ctx.角色?.所属门派ID && ctx.角色.所属门派ID !== 'none') {
41-
const memberCount = ctx.社交.filter((npc) => npc.所属门派 === ctx.角色?.所属门派ID).length + 1;
4241
result.push({
4342
id: 'sect-group',
4443
name: '门派内堂',
4544
lastMessage: `${ctx.角色.姓名}:今日练功可曾懈怠?`,
4645
lastTime: '近日',
4746
unread: 0,
48-
members: memberCount,
47+
members: ctx.社交.length + 1,
4948
});
5049
}
5150

components/features/MobileDevice/apps/ForumApp.tsx

Lines changed: 125 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
1-
import React, { useState } from 'react';
2-
import { DeviceMode, MobileApp } from '../../../../models/mobileDevice';
1+
import React, { useState, useMemo } from 'react';
2+
import { DeviceMode, MobileApp, DeviceGameContext } from '../../../../models/mobileDevice';
33
import { getDeviceConfig, getAppName } from '../../../../models/eraDevice';
44

55
interface AppProps {
66
eraId: string;
77
mode: DeviceMode;
88
appId: MobileApp;
99
onBack: () => void;
10+
gameContext?: DeviceGameContext;
1011
}
1112

1213
interface ForumPost {
@@ -21,56 +22,80 @@ interface ForumPost {
2122
replyList: { author: string; content: string; time: string }[];
2223
}
2324

24-
const ForumApp: React.FC<AppProps> = ({ eraId, mode, appId, onBack }) => {
25+
const ForumApp: React.FC<AppProps> = ({ eraId, mode, appId, onBack, gameContext }) => {
2526
const config = getDeviceConfig(eraId);
2627
const appName = config ? getAppName(config, appId, mode) : '论坛';
2728
const [selectedPost, setSelectedPost] = useState<ForumPost | null>(null);
2829
const [activeCategory, setActiveCategory] = useState('全部');
2930

30-
const categories = ['全部', '武林杂谈', '求师问道', '江湖情报', '江湖趣事'];
31+
const posts: ForumPost[] = useMemo(() => {
32+
const result: ForumPost[] = [];
33+
const ctx = gameContext;
34+
if (!ctx?.世界) return result;
3135

32-
const placeholderPosts: ForumPost[] = [
33-
{
34-
id: '1', author: '华山剑客', title: '今日论剑,紫霞神功果然了得',
35-
content: '今日在华山之巅与几位同道切磋,紫霞神功内力绵长,后劲十足,实为正派内功中的上乘功夫。唯觉修炼门槛颇高,非十余年苦功不能窥其门径。',
36-
category: '武林杂谈', time: '申时', replies: 12, views: 342,
37-
replyList: [
38-
{ author: '嵩山弟子', content: '紫霞神功确是好功夫,但我嵩山派大阳神掌也不遑多让!', time: '申时一刻' },
39-
{ author: '无名散人', content: '内功之道,贵在专精。各家各派皆有绝学,不必厚此薄彼。', time: '申时二刻' },
40-
],
41-
},
42-
{
43-
id: '2', author: '丐帮长老', title: '求问降龙十八掌传人 whereabouts',
44-
content: '帮中古籍记载,降龙十八掌乃丐帮镇帮绝学。近年来却鲜有传人消息,哪位英雄知晓现任传人的下落?',
45-
category: '求师问道', time: '午时', replies: 8, views: 215,
46-
replyList: [
47-
{ author: '江湖百晓生', content: '传闻在襄阳一带出现过,郭大侠的后人应当还在坚守此地。', time: '午时一刻' },
48-
],
49-
},
50-
{
51-
id: '3', author: '暗桩密探', title: '【密报】明教总坛近日异动频繁',
52-
content: '据可靠消息,明教总坛近日有大批高手聚集,似在筹备某件大事。请各位同道提前做好准备,以免措手不及。',
53-
category: '江湖情报', time: '辰时', replies: 23, views: 567,
54-
replyList: [
55-
{ author: '六扇门捕头', content: '多谢提醒,已派人暗中查探。', time: '辰时一刻' },
56-
{ author: '峨眉女侠', content: '师太已经注意到了,命我等加强戒备。', time: '辰时二刻' },
57-
{ author: '武当道士', content: '师父说此事自有定数,不必惊慌。', time: '辰时三刻' },
58-
],
59-
},
60-
{
61-
id: '4', author: '茶楼说书人', title: '笑谈:某大侠夜奔客栈,误入女客房',
62-
content: '话说前日有位大侠月夜赶路,误入一家客栈,摸黑进了客房。次日天明才发现走错了,隔壁住的是一位峨眉女侠。那女侠拔剑就追,大侠连跑三条街……',
63-
category: '江湖趣事', time: '昨日', replies: 45, views: 1024,
64-
replyList: [
65-
{ author: '路人甲', content: '哈哈哈,这位大侠怕不是令狐冲?', time: '昨日酉时' },
66-
{ author: '知情者', content: '此事确有其事,我便是那客栈的店小二!', time: '昨日戌时' },
67-
],
68-
},
69-
];
36+
// 进行中事件 → 论坛热帖
37+
if (ctx.世界.进行中事件) {
38+
ctx.世界.进行中事件.forEach((event, idx) => {
39+
result.push({
40+
id: `ongoing-${idx}`,
41+
author: event.关联人物?.[0] || '江湖传闻',
42+
title: event.事件名,
43+
content: event.事件说明,
44+
category: '江湖情报',
45+
time: event.开始时间 || '近日',
46+
replies: event.关联人物?.length || 0,
47+
views: Math.floor(Math.random() * 500) + 50,
48+
replyList: event.关联人物.slice(1).map((person) => ({
49+
author: person,
50+
content: `涉及此事,${person}亦有参与。`,
51+
time: event.开始时间 || '近日',
52+
})),
53+
});
54+
});
55+
}
7056

57+
// 已结算事件 → 历史帖
58+
if (ctx.世界.已结算事件) {
59+
ctx.世界.已结算事件.slice(-5).forEach((event, idx) => {
60+
const 结果 = event.事件结果?.join('; ') || '已了结';
61+
result.push({
62+
id: `settled-${idx}`,
63+
author: event.关联人物?.[0] || '史官',
64+
title: `[已结案] ${event.事件名}`,
65+
content: `${event.事件说明}\n结果: ${结果}`,
66+
category: '武林旧事',
67+
time: event.结算时间 || '往昔',
68+
replies: event.关联人物?.length || 0,
69+
views: Math.floor(Math.random() * 300) + 30,
70+
replyList: [],
71+
});
72+
});
73+
}
74+
75+
// 世界镜头规划 → 讨论帖
76+
if (ctx.世界.世界镜头规划) {
77+
ctx.世界.世界镜头规划.slice(-3).forEach((lens, idx) => {
78+
result.push({
79+
id: `lens-${idx}`,
80+
author: lens.关联人物?.[0] || '说书人',
81+
title: lens.镜头标题,
82+
content: lens.镜头内容 || '',
83+
category: '武林杂谈',
84+
time: lens.触发时间 || '近日',
85+
replies: lens.关联人物?.length || 0,
86+
views: Math.floor(Math.random() * 200) + 20,
87+
replyList: [],
88+
});
89+
});
90+
}
91+
92+
return result;
93+
}, [gameContext?.世界]);
94+
95+
const categories = ['全部', ...Array.from(new Set(posts.map((p) => p.category)))];
7196
const filteredPosts = activeCategory === '全部'
72-
? placeholderPosts
73-
: placeholderPosts.filter((p) => p.category === activeCategory);
97+
? posts
98+
: posts.filter((p) => p.category === activeCategory);
7499

75100
if (selectedPost) {
76101
return (
@@ -88,22 +113,24 @@ const ForumApp: React.FC<AppProps> = ({ eraId, mode, appId, onBack }) => {
88113
<span className="text-[10px] text-wuxia-gold/60 bg-wuxia-gold/10 px-1.5 py-0.5 rounded">{selectedPost.category}</span>
89114
<span className="text-[10px] text-gray-500">{selectedPost.views} 浏览</span>
90115
</div>
91-
<p className="text-sm text-gray-200 leading-relaxed">{selectedPost.content}</p>
116+
<p className="text-sm text-gray-200 leading-relaxed whitespace-pre-wrap">{selectedPost.content}</p>
92117
</div>
93-
<div>
94-
<h4 className="text-xs font-semibold text-gray-400 mb-2">{selectedPost.replies} 条回复</h4>
95-
<div className="space-y-3">
96-
{selectedPost.replyList.map((reply, idx) => (
97-
<div key={idx} className="rounded-lg bg-gray-900/40 border border-gray-800/30 p-3">
98-
<div className="flex items-center justify-between mb-1">
99-
<span className="text-xs text-wuxia-gold/70">{reply.author}</span>
100-
<span className="text-[10px] text-gray-500">{reply.time}</span>
118+
{selectedPost.replyList.length > 0 && (
119+
<div>
120+
<h4 className="text-xs font-semibold text-gray-400 mb-2">{selectedPost.replyList.length} 条回复</h4>
121+
<div className="space-y-3">
122+
{selectedPost.replyList.map((reply, idx) => (
123+
<div key={idx} className="rounded-lg bg-gray-900/40 border border-gray-800/30 p-3">
124+
<div className="flex items-center justify-between mb-1">
125+
<span className="text-xs text-wuxia-gold/70">{reply.author}</span>
126+
<span className="text-[10px] text-gray-500">{reply.time}</span>
127+
</div>
128+
<p className="text-sm text-gray-300">{reply.content}</p>
101129
</div>
102-
<p className="text-sm text-gray-300">{reply.content}</p>
103-
</div>
104-
))}
130+
))}
131+
</div>
105132
</div>
106-
</div>
133+
)}
107134
</div>
108135
</div>
109136
);
@@ -115,39 +142,48 @@ const ForumApp: React.FC<AppProps> = ({ eraId, mode, appId, onBack }) => {
115142
<button onClick={onBack} className="text-gray-400 hover:text-white transition-colors"></button>
116143
<h3 className="font-semibold text-white">{appName}</h3>
117144
</div>
118-
<div className="flex gap-1 px-4 py-2 overflow-x-auto border-b border-gray-800/30">
119-
{categories.map((cat) => (
120-
<button
121-
key={cat}
122-
onClick={() => setActiveCategory(cat)}
123-
className={`px-3 py-1 rounded-full text-xs whitespace-nowrap transition-colors ${activeCategory === cat ? 'bg-wuxia-gold/20 text-wuxia-gold' : 'text-gray-400 hover:text-white'}`}
124-
>
125-
{cat}
126-
</button>
127-
))}
128-
</div>
129-
<div className="flex-1 overflow-y-auto">
130-
<ul className="divide-y divide-gray-800/50">
131-
{filteredPosts.map((post) => (
132-
<li key={post.id}>
133-
<button
134-
onClick={() => setSelectedPost(post)}
135-
className="w-full px-4 py-3 text-left hover:bg-gray-800/30 transition-colors"
136-
>
137-
<div className="flex items-center gap-2 mb-1">
138-
<span className="text-[10px] text-wuxia-gold/60 bg-wuxia-gold/10 px-1.5 py-0.5 rounded">{post.category}</span>
139-
<span className="text-sm text-white font-medium truncate">{post.title}</span>
140-
</div>
141-
<div className="flex items-center gap-3 text-[10px] text-gray-500">
142-
<span>{post.author}</span>
143-
<span>{post.time}</span>
144-
<span>回复 {post.replies}</span>
145-
<span>浏览 {post.views}</span>
146-
</div>
147-
</button>
148-
</li>
145+
{categories.length > 1 && (
146+
<div className="flex gap-1 px-4 py-2 overflow-x-auto border-b border-gray-800/30">
147+
{categories.map((cat) => (
148+
<button
149+
key={cat}
150+
onClick={() => setActiveCategory(cat)}
151+
className={`px-3 py-1 rounded-full text-xs whitespace-nowrap transition-colors ${activeCategory === cat ? 'bg-wuxia-gold/20 text-wuxia-gold' : 'text-gray-400 hover:text-white'}`}
152+
>
153+
{cat}
154+
</button>
149155
))}
150-
</ul>
156+
</div>
157+
)}
158+
<div className="flex-1 overflow-y-auto">
159+
{filteredPosts.length > 0 ? (
160+
<ul className="divide-y divide-gray-800/50">
161+
{filteredPosts.map((post) => (
162+
<li key={post.id}>
163+
<button
164+
onClick={() => setSelectedPost(post)}
165+
className="w-full px-4 py-3 text-left hover:bg-gray-800/30 transition-colors"
166+
>
167+
<div className="flex items-center gap-2 mb-1">
168+
<span className="text-[10px] text-wuxia-gold/60 bg-wuxia-gold/10 px-1.5 py-0.5 rounded">{post.category}</span>
169+
<span className="text-sm text-white font-medium truncate">{post.title}</span>
170+
</div>
171+
<div className="flex items-center gap-3 text-[10px] text-gray-500">
172+
<span>{post.author}</span>
173+
<span>{post.time}</span>
174+
<span>回复 {post.replies}</span>
175+
<span>浏览 {post.views}</span>
176+
</div>
177+
</button>
178+
</li>
179+
))}
180+
</ul>
181+
) : (
182+
<div className="flex flex-col items-center justify-center h-full text-center p-8">
183+
<span className="text-4xl text-gray-600 mb-3">📋</span>
184+
<p className="text-sm text-gray-400">暂无帖子</p>
185+
</div>
186+
)}
151187
</div>
152188
</div>
153189
);

0 commit comments

Comments
 (0)