zhouxiantao
8 days ago 03193b2a27a2c23e10f3a2f298de9c1142116780
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
/**
 * @author 韩天尊
 * @time 2024-01-15
 * @version 1.0.0
 * @description 积分查询页面组件
 */
import React, { useState, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import { useAppContext } from '../context/AppContext';
import { pointsAPI } from '../services/api';
import { PointsOverview, RedemptionRecord, CommunityPoints, CommunityPointsOverview } from '../types';
import PageHeader from '../components/PageHeader';
 
const PointsQueryPage: React.FC = () => {
    const navigate = useNavigate();
    const { state, loadDeclarationRecords } = useAppContext();
    const [activeTab, setActiveTab] = useState<'declarations' | 'redemptions'>('declarations');
    const [pointsData, setPointsData] = useState<PointsOverview>({
        available: 0,
        redeemed: 0,
        total: 0
    });
    const [communityPointsData, setCommunityPointsData] = useState<CommunityPoints[]>([]);
    const [selectedCommunityIndex, setSelectedCommunityIndex] = useState<number>(0);
    const [redemptionRecords, setRedemptionRecords] = useState<RedemptionRecord[]>([]);
    const [loading, setLoading] = useState(false);
 
    // 加载积分数据
    useEffect(() => {
        const loadPointsData = async () => {
            setLoading(true);
            try {
                const response = await pointsAPI.getOverview();
                // 检查返回的数据结构
                if ('communityList' in response.data && Array.isArray(response.data.communityList)) {
                    // 新的社区积分数据结构
                    const communityData = response.data as CommunityPointsOverview;
                    setCommunityPointsData(communityData.communityList);
                    // 设置默认选中第一个社区
                    if (communityData.communityList.length > 0) {
                        const firstCommunity = communityData.communityList[0];
                        setPointsData({
                            available: firstCommunity.points,
                            redeemed: firstCommunity.redeemedPoints,
                            total: firstCommunity.totalPoints
                        });
                    }
                } else {
                    // 兼容旧的数据结构
                    const pointsData = response.data as PointsOverview;
                    setPointsData(pointsData);
                }
                await loadDeclarationRecords();
            } catch (error) {
                console.error('加载积分数据失败:', error);
            } finally {
                setLoading(false);
            }
        };
 
        loadPointsData();
    }, []); // 修改:改为空依赖数组,确保每次组件挂载都调用接口
 
    // 加载核销记录
    const loadRedemptionRecords = async () => {
        try {
            const response = await pointsAPI.getRedemptionRecords({
                page: 1,
                size: 50
            });
            setRedemptionRecords(response.data.list || []);
        } catch (error) {
            console.error('加载核销记录失败:', error);
        }
    };
 
    // 处理Tab切换
    const handleTabChange = (tab: 'declarations' | 'redemptions') => {
        setActiveTab(tab);
        // 当切换到核销历史时,加载核销记录
        if (tab === 'redemptions' && redemptionRecords.length === 0) {
            loadRedemptionRecords();
        }
    };
 
    // 处理社区切换
    const handleCommunityChange = (index: number) => {
        setSelectedCommunityIndex(index);
        if (communityPointsData[index]) {
            const community = communityPointsData[index];
            setPointsData({
                available: community.points,
                redeemed: community.redeemedPoints,
                total: community.totalPoints
            });
        }
    };
 
    // 获取状态文本
    const getStatusText = (status: string | number) => {
        switch (String(status)) {
            case '1':
            case 'approved': return '审核通过';
            case '0':
            case 'pending': return '待审核';
            case '2':
            case 'rejected': return '审核拒绝';
            default: return '未知';
        }
    };
 
    // 格式化时间显示
    const formatTimeDisplay = (time?: string, startTime?: string, endTime?: string): string => {
        // 如果有单独的时间字段,直接格式化
        if (time) {
            return formatSingleTime(time);
        }
        
        // 如果有开始时间和结束时间
        if (startTime && endTime) {
            try {
                const startDate = new Date(startTime);
                const endDate = new Date(endTime);
                
                // 检查日期是否有效
                if (isNaN(startDate.getTime()) || isNaN(endDate.getTime())) {
                    return `${startTime} - ${endTime}`;
                }
                
                // 检查是否在同一天
                const isSameDay = 
                    startDate.getFullYear() === endDate.getFullYear() &&
                    startDate.getMonth() === endDate.getMonth() &&
                    startDate.getDate() === endDate.getDate();
                
                if (isSameDay) {
                    // 同一天:yyyy-MM-dd HH:mm - HH:mm
                    const dateStr = formatSingleTime(startTime).split(' ')[0];
                    const startTimeStr = formatTimeOnly(startTime);
                    const endTimeStr = formatTimeOnly(endTime);
                    return `${dateStr} ${startTimeStr} - ${endTimeStr}`;
                } else {
                    // 不同天:yyyy-MM-dd HH:mm - yyyy-MM-dd HH:mm
                    return `${formatSingleTime(startTime)} - ${formatSingleTime(endTime)}`;
                }
            } catch (error) {
                return `${startTime} - ${endTime}`;
            }
        }
        
        // 如果只有开始时间
        if (startTime) {
            return formatSingleTime(startTime);
        }
        
        // 如果只有结束时间
        if (endTime) {
            return formatSingleTime(endTime);
        }
        
        return '';
    };
 
    // 格式化单个时间为 yyyy-MM-dd HH:mm
    const formatSingleTime = (timeString: string): string => {
        try {
            const date = new Date(timeString);
            if (isNaN(date.getTime())) {
                return timeString;
            }
            
            const year = date.getFullYear();
            const month = String(date.getMonth() + 1).padStart(2, '0');
            const day = String(date.getDate()).padStart(2, '0');
            const hours = String(date.getHours()).padStart(2, '0');
            const minutes = String(date.getMinutes()).padStart(2, '0');
            
            return `${year}-${month}-${day} ${hours}:${minutes}`;
        } catch (error) {
            return timeString;
        }
    };
 
    // 格式化时间为 HH:mm
    const formatTimeOnly = (timeString: string): string => {
        try {
            const date = new Date(timeString);
            if (isNaN(date.getTime())) {
                return timeString;
            }
            
            const hours = String(date.getHours()).padStart(2, '0');
            const minutes = String(date.getMinutes()).padStart(2, '0');
            
            return `${hours}:${minutes}`;
        } catch (error) {
            return timeString;
        }
    };
 
 
    // 点击积分申报卡片,跳转到详情页面
    const handleDeclarationClick = (recordId: number) => {
        navigate(`/declaration-detail/${recordId}`);
    };
 
    // 点击核销卡片,跳转到详情页面
    const handleRedemptionClick = (recordId: number) => {
        navigate(`/redemption-detail/${recordId}`);
    };
 
    return (
        <div className="page">
            <PageHeader title="我的积分" />
 
            {/* 积分总览卡片 */}
            <div className="points-overview">
                <div className="overview-card">
                    <div className="overview-header">
                        <i className="fas fa-coins"></i>
                        <span>积分总览</span>
                        {communityPointsData.length > 1 && (
                            <div className="community-selector">
                                <select 
                                    value={selectedCommunityIndex}
                                    onChange={(e) => handleCommunityChange(Number(e.target.value))}
                                    className="community-select"
                                >
                                    {communityPointsData.map((community, index) => (
                                        <option key={index} value={index}>
                                            {community.communityName}
                                        </option>
                                    ))}
                                </select>
                            </div>
                        )}
                    </div>
                    <div className="overview-content">
                        <div className="overview-item">
                            <div className="overview-label">可用积分</div>
                            <div className="overview-value" id="available-points">{pointsData.available}</div>
                            <div className="overview-icon">
                                <i className="fas fa-wallet"></i>
                            </div>
                        </div>
                        <div className="overview-item">
                            <div className="overview-label">已核销积分</div>
                            <div className="overview-value" id="redeemed-points">{pointsData.redeemed}</div>
                            <div className="overview-icon">
                                <i className="fas fa-exchange-alt"></i>
                            </div>
                        </div>
                        <div className="overview-item">
                            <div className="overview-label">累计积分</div>
                            <div className="overview-value" id="total-points">{pointsData.total}</div>
                            <div className="overview-icon">
                                <i className="fas fa-chart-line"></i>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
 
 
            {/* 积分记录 */}
            <div className="points-records">
                <div className="records-header">
                    <h3>积分记录</h3>
                </div>
 
                {/* Tab切换 */}
                <div className="tab-section">
                    <button 
                        className={`tab-btn ${activeTab === 'declarations' ? 'active' : ''}`}
                        onClick={() => handleTabChange('declarations')}
                    >
                        积分申报
                    </button>
                    <button 
                        className={`tab-btn ${activeTab === 'redemptions' ? 'active' : ''}`}
                        onClick={() => handleTabChange('redemptions')}
                    >
                        核销历史
                    </button>
                </div>
 
                {/* 列表内容 */}
                <div className="list-content">
                    <div className={`list-tab ${activeTab === 'declarations' ? 'active' : ''}`}>
                        <div className="declarations-list">
                            {/* 积分申报记录 */}
                            {state.declarationRecords.map((record) => (
                                <div 
                                    key={record.id} 
                                    className="declaration-card"
                                    onClick={() => handleDeclarationClick(record.id)}
                                >
                                    <div className="declaration-header">
                                        <div className="declaration-title">{record.title || record.activityName}</div>
                                        <div className={`declaration-status ${String(record.status)}`}>
                                            {getStatusText(record.status)}
                                        </div>
                                    </div>
                                    <div className="declaration-info">
                                        <div className="declaration-info-row">
                                            <i className="fas fa-tag"></i>
                                            <span>申报类型:{record.categoryDesc || record.category || '未知'}</span>
                                        </div>
                                        <div className="declaration-info-row">
                                            <i className="fas fa-clock"></i>
                                            <span>时间:{formatTimeDisplay(record.time, record.startTime, record.endTime)}</span>
                                        </div>
                                    </div>
                                    <div className="declaration-content">{record.content}</div>
                                    <div className="declaration-footer">
                                        <div className="points-info">
                                            <i className="fas fa-coins"></i>
                                            <span>+{record.points || 0} 积分</span>
                                        </div>
                                        <div className="declaration-arrow">
                                            <i className="fas fa-chevron-right"></i>
                                        </div>
                                    </div>
                                </div>
                            ))}
                        </div>
                    </div>
                    <div className={`list-tab ${activeTab === 'redemptions' ? 'active' : ''}`}>
                        <div className="redemptions-list">
                            {/* 核销历史记录 */}
                            {redemptionRecords.length > 0 ? (
                                redemptionRecords.map((record) => (
                                    <div 
                                        key={record.id} 
                                        className="redemption-card"
                                        onClick={() => handleRedemptionClick(record.id)}
                                    >
                                        <div className="redemption-header">
                                            <div className="redemption-title">{record.productName || '积分核销'}</div>
                                            <div className={`redemption-status ${record.status}`}>
                                                {record.status === 'completed' ? '已完成' : '已核销'}
                                            </div>
                                        </div>
                                        <div className="redemption-info">
                                            <div className="redemption-info-row">
                                                <i className="fas fa-clock"></i>
                                                <span>核销时间:{formatSingleTime(record.createTime || record.redemptionTime)}</span>
                                            </div>
                                            <div className="redemption-info-row">
                                                <i className="fas fa-store"></i>
                                                <span>核销地点:社区服务中心</span>
                                            </div>
                                        </div>
                                        <div className="redemption-content">{record.description}</div>
                                        <div className="redemption-footer">
                                            <div className="points-info">
                                                <i className="fas fa-coins"></i>
                                                <span>-{record.points} 积分</span>
                                            </div>
                                            <div className="redemption-arrow">
                                                <i className="fas fa-chevron-right"></i>
                                            </div>
                                        </div>
                                    </div>
                                ))
                            ) : (
                                <div className="empty-state">
                                    <p>暂无核销记录</p>
                                </div>
                            )}
                        </div>
                    </div>
                </div>
            </div>
        </div>
    );
};
 
export default PointsQueryPage;