Explorar el Código

新增修改密码界面/帮助中心界面/关于我们界面

刘清 hace 2 semanas
padre
commit
70d52a8e7a

+ 3
- 0
.vscode/settings.json Ver fichero

@@ -0,0 +1,3 @@
1
+{
2
+    "cmake.ignoreCMakeListsMissing": true
3
+}

+ 1
- 1
lib/main.dart Ver fichero

@@ -24,7 +24,7 @@ class MyApp extends StatelessWidget {
24 24
         ChangeNotifierProvider(create: (_) => di.sl<UserProvider>()),
25 25
       ],
26 26
       child: MaterialApp(
27
-        title: 'Flutter App',
27
+        title: '中了么',
28 28
         theme: ThemeData(
29 29
           primarySwatch: Colors.blue,
30 30
           // fontFamily: 'Roboto',

+ 34
- 0
lib/presentation/providers/auth_provider.dart Ver fichero

@@ -76,6 +76,40 @@ class AuthProvider with ChangeNotifier {
76 76
     }
77 77
   }
78 78
   
79
+  Future<void> modifyPassword(String oldPassword, String newPassword) async {
80
+    _isLoading = true;
81
+    _error = null;
82
+    notifyListeners();
83
+    
84
+    try {
85
+      // 1. 调用API验证旧密码
86
+      // final isValidOldPassword = await authRepository.validatePassword(oldPassword);
87
+
88
+      // if (!isValidOldPassword) {
89
+      //   _error = '当前密码不正确';
90
+      //   return;
91
+      // }
92
+
93
+      // 2. 调用API更新密码
94
+      // final response = await authRepository.updatePassword(
95
+      //   LoginRequest(username: oldPassword, password: newPassword),
96
+      // );
97
+      
98
+      // if (response.success && response.data != null) {
99
+      //   // 可选:清除本地存储的token,让用户重新登录
100
+      //   // await _authService.logout();
101
+      //   _error = null;
102
+      // } else {
103
+      //   _error = response.message;
104
+      // }
105
+    } catch (e) {
106
+      _error = '密码修改失败: $e';
107
+    } finally {
108
+      _isLoading = false;
109
+      notifyListeners();
110
+    }
111
+  }
112
+  
79 113
   Future<void> logout() async {
80 114
     _isLoading = true;
81 115
     _error = null;

+ 715
- 0
lib/presentation/screens/profile/profile_about_screen.dart Ver fichero

@@ -0,0 +1,715 @@
1
+import 'package:flutter/material.dart';
2
+import 'package:url_launcher/url_launcher.dart';
3
+import 'package:package_info_plus/package_info_plus.dart';
4
+
5
+class AboutUsScreen extends StatefulWidget {
6
+  const AboutUsScreen({super.key});
7
+
8
+  @override
9
+  State<AboutUsScreen> createState() => _AboutUsScreenState();
10
+}
11
+
12
+class _AboutUsScreenState extends State<AboutUsScreen> {
13
+  PackageInfo _packageInfo = PackageInfo(
14
+    appName: '未知',
15
+    packageName: '未知',
16
+    version: '未知',
17
+    buildNumber: '未知',
18
+    buildSignature: '未知',
19
+  );
20
+  bool _isLoading = false;
21
+
22
+  @override
23
+  void initState() {
24
+    super.initState();
25
+    // _initPackageInfo();
26
+  }
27
+
28
+  Future<void> _initPackageInfo() async {
29
+    final info = await PackageInfo.fromPlatform();
30
+    setState(() {
31
+      _packageInfo = info;
32
+      _isLoading = false;
33
+    });
34
+  }
35
+
36
+  Future<void> _launchURL(String url) async {
37
+    final Uri uri = Uri.parse(url);
38
+    if (await canLaunchUrl(uri)) {
39
+      await launchUrl(uri);
40
+    } else {
41
+      ScaffoldMessenger.of(context).showSnackBar(
42
+        const SnackBar(
43
+          content: Text('无法打开链接'),
44
+          backgroundColor: Colors.red,
45
+        ),
46
+      );
47
+    }
48
+  }
49
+
50
+  void _showVersionInfo() {
51
+    showDialog(
52
+      context: context,
53
+      builder: (context) => AlertDialog(
54
+        title: const Text('版本信息'),
55
+        content: Column(
56
+          mainAxisSize: MainAxisSize.min,
57
+          crossAxisAlignment: CrossAxisAlignment.start,
58
+          children: [
59
+            _buildInfoItem('应用名称', _packageInfo.appName),
60
+            _buildInfoItem('版本号', _packageInfo.version),
61
+            _buildInfoItem('构建号', _packageInfo.buildNumber),
62
+            _buildInfoItem('包名', _packageInfo.packageName),
63
+          ],
64
+        ),
65
+        actions: [
66
+          TextButton(
67
+            onPressed: () => Navigator.of(context).pop(),
68
+            child: const Text('确定'),
69
+          ),
70
+        ],
71
+      ),
72
+    );
73
+  }
74
+
75
+  Widget _buildInfoItem(String label, String value) {
76
+    return Padding(
77
+      padding: const EdgeInsets.symmetric(vertical: 6),
78
+      child: Row(
79
+        crossAxisAlignment: CrossAxisAlignment.start,
80
+        children: [
81
+          SizedBox(
82
+            width: 80,
83
+            child: Text(
84
+              '$label:',
85
+              style: const TextStyle(
86
+                fontWeight: FontWeight.bold,
87
+                color: Colors.grey,
88
+              ),
89
+            ),
90
+          ),
91
+          Expanded(
92
+            child: Text(
93
+              value,
94
+              style: const TextStyle(color: Colors.black87),
95
+            ),
96
+          ),
97
+        ],
98
+      ),
99
+    );
100
+  }
101
+
102
+  @override
103
+  Widget build(BuildContext context) {
104
+    return Scaffold(
105
+      appBar: AppBar(
106
+        title: const Text('关于我们'),
107
+        centerTitle: true,
108
+      ),
109
+      body: _isLoading
110
+          ? const Center(child: CircularProgressIndicator())
111
+          : SingleChildScrollView(
112
+              child: Column(
113
+                children: [
114
+                  // 应用Logo和名称区域
115
+                  Container(
116
+                    padding: const EdgeInsets.symmetric(vertical: 40),
117
+                    decoration: BoxDecoration(
118
+                      gradient: LinearGradient(
119
+                        begin: Alignment.topCenter,
120
+                        end: Alignment.bottomCenter,
121
+                        colors: [
122
+                          Colors.red[50]!,
123
+                          Colors.orange[50]!,
124
+                        ],
125
+                      ),
126
+                    ),
127
+                    child: Column(
128
+                      children: [
129
+                        // 应用Logo
130
+                        Container(
131
+                          width: 120,
132
+                          height: 120,
133
+                          decoration: BoxDecoration(
134
+                            color: Colors.white,
135
+                            borderRadius: BorderRadius.circular(30),
136
+                            boxShadow: [
137
+                              BoxShadow(
138
+                                color: Colors.red.withOpacity(0.2),
139
+                                blurRadius: 20,
140
+                                offset: const Offset(0, 10),
141
+                              ),
142
+                            ],
143
+                          ),
144
+                          child: const Icon(
145
+                            Icons.emoji_events,
146
+                            size: 60,
147
+                            color: Colors.red,
148
+                          ),
149
+                        ),
150
+                        const SizedBox(height: 20),
151
+                        // 应用名称
152
+                        const Text(
153
+                          '中了么',
154
+                          style: TextStyle(
155
+                            fontSize: 32,
156
+                            fontWeight: FontWeight.bold,
157
+                            color: Colors.red,
158
+                          ),
159
+                        ),
160
+                        const SizedBox(height: 8),
161
+                        // 宣传语
162
+                        const Text(
163
+                          '专业彩票资讯,开启幸运之门',
164
+                          style: TextStyle(
165
+                            fontSize: 16,
166
+                            color: Colors.orange,
167
+                            fontStyle: FontStyle.italic,
168
+                          ),
169
+                        ),
170
+                        const SizedBox(height: 16),
171
+                        // 版本信息
172
+                        GestureDetector(
173
+                          onTap: _showVersionInfo,
174
+                          child: Container(
175
+                            padding: const EdgeInsets.symmetric(
176
+                              horizontal: 16,
177
+                              vertical: 6,
178
+                            ),
179
+                            decoration: BoxDecoration(
180
+                              color: Colors.white.withOpacity(0.8),
181
+                              borderRadius: BorderRadius.circular(20),
182
+                            ),
183
+                            child: Text(
184
+                              '版本 ${_packageInfo.version}',
185
+                              style: const TextStyle(
186
+                                color: Colors.red,
187
+                                fontWeight: FontWeight.w500,
188
+                              ),
189
+                            ),
190
+                          ),
191
+                        ),
192
+                      ],
193
+                    ),
194
+                  ),
195
+
196
+                  // 应用介绍卡片
197
+                  _buildSectionCard(
198
+                    title: '应用介绍',
199
+                    icon: Icons.info_outline,
200
+                    color: Colors.blue,
201
+                    child: const Column(
202
+                      crossAxisAlignment: CrossAxisAlignment.start,
203
+                      children: [
204
+                        Text(
205
+                          '「中了么」是一款专业的彩票资讯服务平台,致力于为用户提供全面、及时、准确的彩票信息。我们不是彩票销售平台,而是您获取彩票资讯、分析数据和社区交流的得力助手。',
206
+                          style: TextStyle(
207
+                            height: 1.6,
208
+                            color: Colors.black87,
209
+                          ),
210
+                        ),
211
+                        SizedBox(height: 12),
212
+                        Row(
213
+                          children: [
214
+                            Icon(Icons.check_circle, color: Colors.green, size: 16),
215
+                            SizedBox(width: 8),
216
+                            Expanded(child: Text('合法合规:严格遵守国家法律法规')),
217
+                          ],
218
+                        ),
219
+                        SizedBox(height: 6),
220
+                        Row(
221
+                          children: [
222
+                            Icon(Icons.check_circle, color: Colors.green, size: 16),
223
+                            SizedBox(width: 8),
224
+                            Expanded(child: Text('信息透明:所有数据公开可查')),
225
+                          ],
226
+                        ),
227
+                        SizedBox(height: 6),
228
+                        Row(
229
+                          children: [
230
+                            Icon(Icons.check_circle, color: Colors.green, size: 16),
231
+                            SizedBox(width: 8),
232
+                            Expanded(child: Text('理性推荐:倡导健康购彩理念')),
233
+                          ],
234
+                        ),
235
+                      ],
236
+                    ),
237
+                  ),
238
+
239
+                  // 核心功能
240
+                  _buildSectionCard(
241
+                    title: '核心功能',
242
+                    icon: Icons.stars,
243
+                    color: Colors.amber,
244
+                    child: Column(
245
+                      children: [
246
+                        _buildFeatureItem(
247
+                          icon: Icons.notifications_active,
248
+                          title: '开奖通知',
249
+                          description: '第一时间推送最新开奖结果',
250
+                          color: Colors.red,
251
+                        ),
252
+                        _buildFeatureItem(
253
+                          icon: Icons.analytics,
254
+                          title: '数据统计',
255
+                          description: '历史数据深度分析和趋势预测',
256
+                          color: Colors.blue,
257
+                        ),
258
+                        _buildFeatureItem(
259
+                          icon: Icons.article,
260
+                          title: '资讯快报',
261
+                          description: '行业动态和玩法技巧专业解读',
262
+                          color: Colors.green,
263
+                        ),
264
+                        _buildFeatureItem(
265
+                          icon: Icons.people,
266
+                          title: '彩民社区',
267
+                          description: '与千万彩民交流经验心得',
268
+                          color: Colors.purple,
269
+                        ),
270
+                        _buildFeatureItem(
271
+                          icon: Icons.security,
272
+                          title: '风险提示',
273
+                          description: '购彩风险提示和理性建议',
274
+                          color: Colors.orange,
275
+                        ),
276
+                      ],
277
+                    ),
278
+                  ),
279
+
280
+                  // 开发团队
281
+                  _buildSectionCard(
282
+                    title: '开发团队',
283
+                    icon: Icons.group,
284
+                    color: Colors.purple,
285
+                    child: Column(
286
+                      children: [
287
+                        _buildTeamMember(
288
+                          name: '技术团队',
289
+                          description: '来自一线互联网公司的技术专家,拥有丰富的移动开发经验',
290
+                          avatarColor: Colors.blue,
291
+                        ),
292
+                        _buildTeamMember(
293
+                          name: '数据分析团队',
294
+                          description: '统计学和数据分析专业人士,提供精准的数据支持',
295
+                          avatarColor: Colors.green,
296
+                        ),
297
+                        _buildTeamMember(
298
+                          name: '内容团队',
299
+                          description: '资深彩票行业编辑,确保资讯的专业性和时效性',
300
+                          avatarColor: Colors.orange,
301
+                        ),
302
+                        _buildTeamMember(
303
+                          name: '风控团队',
304
+                          description: '法律和风控专家,确保平台合规运营',
305
+                          avatarColor: Colors.red,
306
+                        ),
307
+                      ],
308
+                    ),
309
+                  ),
310
+
311
+                  // 联系我们
312
+                  _buildSectionCard(
313
+                    title: '联系我们',
314
+                    icon: Icons.contact_support,
315
+                    color: Colors.teal,
316
+                    child: Column(
317
+                      children: [
318
+                        _buildContactItem(
319
+                          icon: Icons.email,
320
+                          label: '商务合作',
321
+                          value: 'business@zhongleme.com',
322
+                          onTap: () => _launchURL('mailto:business@zhongleme.com'),
323
+                        ),
324
+                        _buildContactItem(
325
+                          icon: Icons.email,
326
+                          label: '用户反馈',
327
+                          value: 'feedback@zhongleme.com',
328
+                          onTap: () => _launchURL('mailto:feedback@zhongleme.com'),
329
+                        ),
330
+                        _buildContactItem(
331
+                          icon: Icons.phone,
332
+                          label: '客服热线',
333
+                          value: '400-888-8888',
334
+                          onTap: () => _launchURL('tel:4008888888'),
335
+                        ),
336
+                        _buildContactItem(
337
+                          icon: Icons.language,
338
+                          label: '官方网站',
339
+                          value: 'https://www.zhongleme.com',
340
+                          onTap: () => _launchURL('https://www.zhongleme.com'),
341
+                        ),
342
+                        const SizedBox(height: 16),
343
+                        const Text(
344
+                          '工作时间:周一至周五 9:00-18:00',
345
+                          style: TextStyle(
346
+                            color: Colors.grey,
347
+                            fontSize: 13,
348
+                          ),
349
+                        ),
350
+                      ],
351
+                    ),
352
+                  ),
353
+
354
+                  // 法律声明
355
+                  _buildSectionCard(
356
+                    title: '法律声明',
357
+                    icon: Icons.gavel,
358
+                    color: Colors.grey,
359
+                    child: const Column(
360
+                      crossAxisAlignment: CrossAxisAlignment.start,
361
+                      children: [
362
+                        Text(
363
+                          '1. 本应用提供的所有资讯仅供参考,不构成任何投注建议。',
364
+                          style: TextStyle(fontSize: 13, color: Colors.grey),
365
+                        ),
366
+                        SizedBox(height: 6),
367
+                        Text(
368
+                          '2. 彩票有风险,请理性购彩。未满18周岁不得购买彩票。',
369
+                          style: TextStyle(fontSize: 13, color: Colors.grey),
370
+                        ),
371
+                        SizedBox(height: 6),
372
+                        Text(
373
+                          '3. 我们严格遵守《互联网信息服务管理办法》等相关法律法规。',
374
+                          style: TextStyle(fontSize: 13, color: Colors.grey),
375
+                        ),
376
+                        SizedBox(height: 6),
377
+                        Text(
378
+                          '4. 用户在使用过程中应遵守当地法律法规,对自己的行为负责。',
379
+                          style: TextStyle(fontSize: 13, color: Colors.grey),
380
+                        ),
381
+                        SizedBox(height: 12),
382
+                        Center(
383
+                          child: Text(
384
+                            '© 2023 中了么 版权所有',
385
+                            style: TextStyle(
386
+                              fontSize: 12,
387
+                              color: Colors.grey,
388
+                              fontWeight: FontWeight.bold,
389
+                            ),
390
+                          ),
391
+                        ),
392
+                      ],
393
+                    ),
394
+                  ),
395
+
396
+                  // 社交平台
397
+                  Container(
398
+                    padding: const EdgeInsets.all(20),
399
+                    child: Column(
400
+                      children: [
401
+                        const Text(
402
+                          '关注我们',
403
+                          style: TextStyle(
404
+                            fontSize: 18,
405
+                            fontWeight: FontWeight.bold,
406
+                          ),
407
+                        ),
408
+                        const SizedBox(height: 16),
409
+                        Row(
410
+                          mainAxisAlignment: MainAxisAlignment.center,
411
+                          children: [
412
+                            _buildSocialButton(
413
+                              icon: Icons.wechat,
414
+                              label: '微信',
415
+                              onTap: () => _showWechatQRCode(context),
416
+                            ),
417
+                            _buildSocialButton(
418
+                              icon: Icons.camera_alt,
419
+                              label: '微博',
420
+                              onTap: () => _launchURL('https://weibo.com/zhongleme'),
421
+                            ),
422
+                            _buildSocialButton(
423
+                              icon: Icons.videocam,
424
+                              label: '抖音',
425
+                              onTap: () => _launchURL('https://www.douyin.com/zhongleme'),
426
+                            ),
427
+                          ],
428
+                        ),
429
+                      ],
430
+                    ),
431
+                  ),
432
+
433
+                  const SizedBox(height: 40),
434
+                ],
435
+              ),
436
+            ),
437
+    );
438
+  }
439
+
440
+  Widget _buildSectionCard({
441
+    required String title,
442
+    required IconData icon,
443
+    required Color color,
444
+    required Widget child,
445
+  }) {
446
+    return Container(
447
+      margin: const EdgeInsets.fromLTRB(16, 8, 16, 8),
448
+      padding: const EdgeInsets.all(20),
449
+      decoration: BoxDecoration(
450
+        color: Colors.white,
451
+        borderRadius: BorderRadius.circular(16),
452
+        boxShadow: [
453
+          BoxShadow(
454
+            color: Colors.grey.withOpacity(0.1),
455
+            blurRadius: 10,
456
+            offset: const Offset(0, 5),
457
+          ),
458
+        ],
459
+      ),
460
+      child: Column(
461
+        crossAxisAlignment: CrossAxisAlignment.start,
462
+        children: [
463
+          Row(
464
+            children: [
465
+              Container(
466
+                padding: const EdgeInsets.all(8),
467
+                decoration: BoxDecoration(
468
+                  color: color.withOpacity(0.1),
469
+                  borderRadius: BorderRadius.circular(10),
470
+                ),
471
+                child: Icon(icon, color: color, size: 24),
472
+              ),
473
+              const SizedBox(width: 12),
474
+              Text(
475
+                title,
476
+                style: TextStyle(
477
+                  fontSize: 20,
478
+                  fontWeight: FontWeight.bold,
479
+                  color: color,
480
+                ),
481
+              ),
482
+            ],
483
+          ),
484
+          const SizedBox(height: 16),
485
+          child,
486
+        ],
487
+      ),
488
+    );
489
+  }
490
+
491
+  Widget _buildFeatureItem({
492
+    required IconData icon,
493
+    required String title,
494
+    required String description,
495
+    required Color color,
496
+  }) {
497
+    return Container(
498
+      margin: const EdgeInsets.only(bottom: 12),
499
+      child: Row(
500
+        crossAxisAlignment: CrossAxisAlignment.start,
501
+        children: [
502
+          Container(
503
+            width: 40,
504
+            height: 40,
505
+            decoration: BoxDecoration(
506
+              color: color.withOpacity(0.1),
507
+              borderRadius: BorderRadius.circular(10),
508
+            ),
509
+            child: Icon(icon, color: color, size: 22),
510
+          ),
511
+          const SizedBox(width: 12),
512
+          Expanded(
513
+            child: Column(
514
+              crossAxisAlignment: CrossAxisAlignment.start,
515
+              children: [
516
+                Text(
517
+                  title,
518
+                  style: const TextStyle(
519
+                    fontWeight: FontWeight.bold,
520
+                    fontSize: 16,
521
+                  ),
522
+                ),
523
+                const SizedBox(height: 4),
524
+                Text(
525
+                  description,
526
+                  style: TextStyle(
527
+                    color: Colors.grey[700],
528
+                    fontSize: 14,
529
+                  ),
530
+                ),
531
+              ],
532
+            ),
533
+          ),
534
+        ],
535
+      ),
536
+    );
537
+  }
538
+
539
+  Widget _buildTeamMember({
540
+    required String name,
541
+    required String description,
542
+    required Color avatarColor,
543
+  }) {
544
+    return Container(
545
+      margin: const EdgeInsets.only(bottom: 12),
546
+      child: Row(
547
+        crossAxisAlignment: CrossAxisAlignment.start,
548
+        children: [
549
+          CircleAvatar(
550
+            backgroundColor: avatarColor.withOpacity(0.1),
551
+            radius: 20,
552
+            child: Icon(
553
+              Icons.person,
554
+              color: avatarColor,
555
+              size: 20,
556
+            ),
557
+          ),
558
+          const SizedBox(width: 12),
559
+          Expanded(
560
+            child: Column(
561
+              crossAxisAlignment: CrossAxisAlignment.start,
562
+              children: [
563
+                Text(
564
+                  name,
565
+                  style: const TextStyle(
566
+                    fontWeight: FontWeight.bold,
567
+                    fontSize: 16,
568
+                  ),
569
+                ),
570
+                const SizedBox(height: 4),
571
+                Text(
572
+                  description,
573
+                  style: TextStyle(
574
+                    color: Colors.grey[700],
575
+                    fontSize: 14,
576
+                  ),
577
+                ),
578
+              ],
579
+            ),
580
+          ),
581
+        ],
582
+      ),
583
+    );
584
+  }
585
+
586
+  Widget _buildContactItem({
587
+    required IconData icon,
588
+    required String label,
589
+    required String value,
590
+    required VoidCallback onTap,
591
+  }) {
592
+    return GestureDetector(
593
+      onTap: onTap,
594
+      child: Container(
595
+        margin: const EdgeInsets.only(bottom: 12),
596
+        child: Row(
597
+          children: [
598
+            Container(
599
+              width: 40,
600
+              height: 40,
601
+              decoration: BoxDecoration(
602
+                color: Colors.blue.withOpacity(0.1),
603
+                borderRadius: BorderRadius.circular(10),
604
+              ),
605
+              child: Icon(icon, color: Colors.blue, size: 22),
606
+            ),
607
+            const SizedBox(width: 12),
608
+            Expanded(
609
+              child: Column(
610
+                crossAxisAlignment: CrossAxisAlignment.start,
611
+                children: [
612
+                  Text(
613
+                    label,
614
+                    style: const TextStyle(
615
+                      fontSize: 14,
616
+                      color: Colors.grey,
617
+                    ),
618
+                  ),
619
+                  Text(
620
+                    value,
621
+                    style: const TextStyle(
622
+                      fontWeight: FontWeight.w500,
623
+                      fontSize: 16,
624
+                    ),
625
+                  ),
626
+                ],
627
+              ),
628
+            ),
629
+            const Icon(Icons.chevron_right, color: Colors.grey),
630
+          ],
631
+        ),
632
+      ),
633
+    );
634
+  }
635
+
636
+  Widget _buildSocialButton({
637
+    required IconData icon,
638
+    required String label,
639
+    required VoidCallback onTap,
640
+  }) {
641
+    return GestureDetector(
642
+      onTap: onTap,
643
+      child: Container(
644
+        margin: const EdgeInsets.symmetric(horizontal: 12),
645
+        child: Column(
646
+          children: [
647
+            Container(
648
+              width: 60,
649
+              height: 60,
650
+              decoration: BoxDecoration(
651
+                color: Colors.red.withOpacity(0.1),
652
+                borderRadius: BorderRadius.circular(30),
653
+              ),
654
+              child: Icon(icon, color: Colors.red, size: 30),
655
+            ),
656
+            const SizedBox(height: 8),
657
+            Text(label, style: const TextStyle(fontSize: 14)),
658
+          ],
659
+        ),
660
+      ),
661
+    );
662
+  }
663
+
664
+  void _showWechatQRCode(BuildContext context) {
665
+    showDialog(
666
+      context: context,
667
+      builder: (context) => Dialog(
668
+        shape: RoundedRectangleBorder(
669
+          borderRadius: BorderRadius.circular(20),
670
+        ),
671
+        child: Container(
672
+          padding: const EdgeInsets.all(24),
673
+          child: Column(
674
+            mainAxisSize: MainAxisSize.min,
675
+            children: [
676
+              const Text(
677
+                '关注微信公众号',
678
+                style: TextStyle(
679
+                  fontSize: 18,
680
+                  fontWeight: FontWeight.bold,
681
+                ),
682
+              ),
683
+              const SizedBox(height: 16),
684
+              Container(
685
+                width: 200,
686
+                height: 200,
687
+                decoration: BoxDecoration(
688
+                  color: Colors.white,
689
+                  borderRadius: BorderRadius.circular(10),
690
+                  border: Border.all(color: Colors.grey[300]!),
691
+                ),
692
+                child: const Icon(
693
+                  Icons.qr_code_scanner,
694
+                  size: 100,
695
+                  color: Colors.grey,
696
+                ),
697
+              ),
698
+              const SizedBox(height: 16),
699
+              const Text(
700
+                '扫描二维码关注「中了么」公众号',
701
+                textAlign: TextAlign.center,
702
+                style: TextStyle(color: Colors.grey),
703
+              ),
704
+              const SizedBox(height: 20),
705
+              TextButton(
706
+                onPressed: () => Navigator.of(context).pop(),
707
+                child: const Text('关闭'),
708
+              ),
709
+            ],
710
+          ),
711
+        ),
712
+      ),
713
+    );
714
+  }
715
+}

+ 538
- 0
lib/presentation/screens/profile/profile_help_screen.dart Ver fichero

@@ -0,0 +1,538 @@
1
+import 'package:flutter/material.dart';
2
+import 'package:url_launcher/url_launcher.dart';
3
+
4
+class HelpCenterScreen extends StatefulWidget {
5
+  const HelpCenterScreen({super.key});
6
+
7
+  @override
8
+  State<HelpCenterScreen> createState() => _HelpCenterScreenState();
9
+}
10
+
11
+class _HelpCenterScreenState extends State<HelpCenterScreen> {
12
+  final TextEditingController _searchController = TextEditingController();
13
+  final List<FAQItem> _allFAQs = [];
14
+  List<FAQItem> _filteredFAQs = [];
15
+  List<FAQCategory> _categories = [];
16
+  bool _isSearching = false;
17
+  String _selectedCategoryId = 'all';
18
+
19
+  @override
20
+  void initState() {
21
+    super.initState();
22
+    _initializeData();
23
+    _searchController.addListener(_onSearchChanged);
24
+  }
25
+
26
+  @override
27
+  void dispose() {
28
+    _searchController.dispose();
29
+    super.dispose();
30
+  }
31
+
32
+  void _initializeData() {
33
+    // 初始化分类数据
34
+    _categories = [
35
+      FAQCategory(id: 'all', name: '全部', icon: Icons.all_inclusive, color: Colors.blue),
36
+      FAQCategory(id: 'account', name: '账户问题', icon: Icons.person, color: Colors.purple),
37
+      FAQCategory(id: 'payment', name: '支付相关', icon: Icons.payment, color: Colors.green),
38
+      FAQCategory(id: 'order', name: '订单问题', icon: Icons.shopping_cart, color: Colors.orange),
39
+      FAQCategory(id: 'app', name: '应用使用', icon: Icons.phone_iphone, color: Colors.red),
40
+      FAQCategory(id: 'privacy', name: '隐私安全', icon: Icons.security, color: Colors.teal),
41
+      FAQCategory(id: 'other', name: '其他问题', icon: Icons.help_outline, color: Colors.grey),
42
+    ];
43
+
44
+    // 初始化FAQ数据
45
+    _allFAQs.addAll([
46
+      FAQItem(
47
+        id: '1',
48
+        question: '如何修改个人资料?',
49
+        answer: '您可以在"我的"页面点击头像或个人信息进入编辑页面进行修改。修改完成后记得点击保存按钮。',
50
+        categoryId: 'account',
51
+        isExpanded: false,
52
+      ),
53
+      FAQItem(
54
+        id: '2',
55
+        question: '忘记密码怎么办?',
56
+        answer: '在登录页面点击"忘记密码",按照提示输入注册邮箱或手机号,系统将发送重置密码链接到您的邮箱或手机。',
57
+        categoryId: 'account',
58
+        isExpanded: false,
59
+      ),
60
+      FAQItem(
61
+        id: '3',
62
+        question: '如何绑定/解绑第三方登录?',
63
+        answer: '进入"设置" -> "账户与安全" -> "第三方账号绑定",在这里可以绑定或解绑微信、Apple ID等第三方账号。',
64
+        categoryId: 'account',
65
+        isExpanded: false,
66
+      ),
67
+      FAQItem(
68
+        id: '4',
69
+        question: '支付方式有哪些?',
70
+        answer: '我们支持微信支付、支付宝、银联卡等多种支付方式。在支付时可以选择您方便的支付方式进行付款。',
71
+        categoryId: 'payment',
72
+        isExpanded: false,
73
+      ),
74
+      FAQItem(
75
+        id: '5',
76
+        question: '支付遇到问题怎么办?',
77
+        answer: '1. 检查网络连接\n2. 确认支付账户余额充足\n3. 检查银行卡是否过期\n4. 如仍无法解决,请联系客服提供订单号',
78
+        categoryId: 'payment',
79
+        isExpanded: false,
80
+      ),
81
+      FAQItem(
82
+        id: '6',
83
+        question: '如何查看订单状态?',
84
+        answer: '进入"我的订单"页面,可以查看所有订单的详细状态,包括待支付、进行中、已完成、已取消等状态。',
85
+        categoryId: 'order',
86
+        isExpanded: false,
87
+      ),
88
+      FAQItem(
89
+        id: '7',
90
+        question: '如何取消订单?',
91
+        answer: '在订单详情页面,如果订单状态允许取消,会出现"取消订单"按钮。请注意,部分订单在特定状态下不可取消。',
92
+        categoryId: 'order',
93
+        isExpanded: false,
94
+      ),
95
+      FAQItem(
96
+        id: '8',
97
+        question: '应用出现闪退怎么办?',
98
+        answer: '1. 尝试重启应用\n2. 检查应用是否为最新版本\n3. 清理手机缓存\n4. 重启手机设备\n5. 如问题持续,请反馈给我们',
99
+        categoryId: 'app',
100
+        isExpanded: false,
101
+      ),
102
+      FAQItem(
103
+        id: '9',
104
+        question: '如何更新应用到最新版本?',
105
+        answer: 'iOS用户:前往App Store搜索应用并更新\nAndroid用户:前往应用商店或应用内检查更新',
106
+        categoryId: 'app',
107
+        isExpanded: false,
108
+      ),
109
+      FAQItem(
110
+        id: '10',
111
+        question: '如何保护我的隐私信息?',
112
+        answer: '我们采用加密技术保护您的数据,不会向第三方泄露您的个人信息。您可以在隐私设置中管理数据权限。',
113
+        categoryId: 'privacy',
114
+        isExpanded: false,
115
+      ),
116
+      FAQItem(
117
+        id: '11',
118
+        question: '如何联系客服?',
119
+        answer: '1. 拨打电话:400-123-4567(工作日 9:00-18:00)\n2. 发送邮件:support@example.com\n3. 在线客服:应用内"我的客服"',
120
+        categoryId: 'other',
121
+        isExpanded: false,
122
+      ),
123
+      FAQItem(
124
+        id: '12',
125
+        question: '服务时间是什么时候?',
126
+        answer: '在线客服:每天 8:00-22:00\n电话客服:工作日 9:00-18:00\n节假日服务时间可能调整',
127
+        categoryId: 'other',
128
+        isExpanded: false,
129
+      ),
130
+    ]);
131
+
132
+    _filteredFAQs = List.from(_allFAQs);
133
+  }
134
+
135
+  void _onSearchChanged() {
136
+    final query = _searchController.text.trim();
137
+    setState(() {
138
+      _isSearching = query.isNotEmpty;
139
+      _filteredFAQs = _allFAQs.where((faq) {
140
+        final matchesSearch = faq.question.toLowerCase().contains(query.toLowerCase()) ||
141
+            (faq.answer?.toLowerCase().contains(query.toLowerCase()) ?? false);
142
+        
143
+        final matchesCategory = _selectedCategoryId == 'all' || 
144
+            faq.categoryId == _selectedCategoryId;
145
+        
146
+        return matchesSearch && matchesCategory;
147
+      }).toList();
148
+    });
149
+  }
150
+
151
+  void _selectCategory(String categoryId) {
152
+    setState(() {
153
+      _selectedCategoryId = categoryId;
154
+      _filteredFAQs = _allFAQs.where((faq) {
155
+        final matchesSearch = _isSearching
156
+            ? (faq.question.toLowerCase().contains(_searchController.text.toLowerCase()) ||
157
+                (faq.answer?.toLowerCase().contains(_searchController.text.toLowerCase()) ?? false))
158
+            : true;
159
+        
160
+        final matchesCategory = categoryId == 'all' || faq.categoryId == categoryId;
161
+        
162
+        return matchesSearch && matchesCategory;
163
+      }).toList();
164
+    });
165
+  }
166
+
167
+  void _toggleFAQ(int index) {
168
+    setState(() {
169
+      _filteredFAQs[index].isExpanded = !_filteredFAQs[index].isExpanded;
170
+    });
171
+  }
172
+
173
+  Future<void> _contactSupport(String method) async {
174
+    switch (method) {
175
+      case 'phone':
176
+        final Uri telUri = Uri.parse('tel:4001234567');
177
+        if (await canLaunchUrl(telUri)) {
178
+          await launchUrl(telUri);
179
+        }
180
+        break;
181
+      case 'email':
182
+        final Uri emailUri = Uri(
183
+          scheme: 'mailto',
184
+          path: 'support@example.com',
185
+          queryParameters: {'subject': '用户咨询'},
186
+        );
187
+        if (await canLaunchUrl(emailUri)) {
188
+          await launchUrl(emailUri);
189
+        }
190
+        break;
191
+    }
192
+  }
193
+
194
+  void _showFeedbackDialog(BuildContext context) {
195
+    showDialog(
196
+      context: context,
197
+      builder: (context) => AlertDialog(
198
+        title: const Text('问题反馈'),
199
+        content: const Text('您可以将遇到的问题详细描述并发送给我们,我们会尽快处理。'),
200
+        actions: [
201
+          TextButton(
202
+            onPressed: () => Navigator.of(context).pop(),
203
+            child: const Text('取消'),
204
+          ),
205
+          TextButton(
206
+            onPressed: () {
207
+              Navigator.of(context).pop();
208
+              _contactSupport('email');
209
+            },
210
+            child: const Text('去反馈'),
211
+          ),
212
+        ],
213
+      ),
214
+    );
215
+  }
216
+
217
+  @override
218
+  Widget build(BuildContext context) {
219
+    return Scaffold(
220
+      appBar: AppBar(
221
+        title: const Text('帮助中心'),
222
+        actions: [
223
+          IconButton(
224
+            icon: const Icon(Icons.feedback_outlined),
225
+            tooltip: '问题反馈',
226
+            onPressed: () => _showFeedbackDialog(context),
227
+          ),
228
+        ],
229
+      ),
230
+      body: Column(
231
+        children: [
232
+          // 搜索栏
233
+          Padding(
234
+            padding: const EdgeInsets.all(16),
235
+            child: Container(
236
+              decoration: BoxDecoration(
237
+                color: Colors.grey[100],
238
+                borderRadius: BorderRadius.circular(12),
239
+                border: Border.all(color: Colors.grey[300]!),
240
+              ),
241
+              child: TextField(
242
+                controller: _searchController,
243
+                decoration: InputDecoration(
244
+                  hintText: '搜索问题或关键词...',
245
+                  prefixIcon: const Icon(Icons.search, color: Colors.grey),
246
+                  border: InputBorder.none,
247
+                  suffixIcon: _searchController.text.isNotEmpty
248
+                      ? IconButton(
249
+                          icon: const Icon(Icons.clear, color: Colors.grey),
250
+                          onPressed: () {
251
+                            _searchController.clear();
252
+                            _onSearchChanged();
253
+                          },
254
+                        )
255
+                      : null,
256
+                  contentPadding: const EdgeInsets.symmetric(vertical: 14),
257
+                ),
258
+              ),
259
+            ),
260
+          ),
261
+
262
+          // 分类导航
263
+          SizedBox(
264
+            height: 100,
265
+            child: ListView.builder(
266
+              scrollDirection: Axis.horizontal,
267
+              padding: const EdgeInsets.symmetric(horizontal: 16),
268
+              itemCount: _categories.length,
269
+              itemBuilder: (context, index) {
270
+                final category = _categories[index];
271
+                final isSelected = _selectedCategoryId == category.id;
272
+                
273
+                return Padding(
274
+                  padding: const EdgeInsets.only(right: 12),
275
+                  child: Column(
276
+                    children: [
277
+                      GestureDetector(
278
+                        onTap: () => _selectCategory(category.id),
279
+                        child: Container(
280
+                          width: 70,
281
+                          height: 70,
282
+                          decoration: BoxDecoration(
283
+                            color: isSelected 
284
+                                ? category.color.withOpacity(0.2)
285
+                                : Colors.grey[50],
286
+                            borderRadius: BorderRadius.circular(16),
287
+                            border: Border.all(
288
+                              color: isSelected 
289
+                                  ? category.color 
290
+                                  : Colors.transparent,
291
+                              width: 2,
292
+                            ),
293
+                          ),
294
+                          child: Column(
295
+                            mainAxisAlignment: MainAxisAlignment.center,
296
+                            children: [
297
+                              Icon(category.icon, color: category.color, size: 28),
298
+                              const SizedBox(height: 4),
299
+                            ],
300
+                          ),
301
+                        ),
302
+                      ),
303
+                      const SizedBox(height: 4),
304
+                      Text(
305
+                        category.name,
306
+                        style: TextStyle(
307
+                          fontSize: 12,
308
+                          fontWeight: isSelected ? FontWeight.bold : FontWeight.normal,
309
+                          color: isSelected ? category.color : Colors.grey[700],
310
+                        ),
311
+                      ),
312
+                    ],
313
+                  ),
314
+                );
315
+              },
316
+            ),
317
+          ),
318
+
319
+          // 问题列表
320
+          Expanded(
321
+            child: _filteredFAQs.isEmpty
322
+                ? Center(
323
+                    child: Column(
324
+                      mainAxisAlignment: MainAxisAlignment.center,
325
+                      children: [
326
+                        Icon(
327
+                          Icons.search_off,
328
+                          size: 60,
329
+                          color: Colors.grey[400],
330
+                        ),
331
+                        const SizedBox(height: 16),
332
+                        Text(
333
+                          _isSearching
334
+                              ? '没有找到相关问题的解答'
335
+                              : '暂无问题',
336
+                          style: TextStyle(
337
+                            color: Colors.grey[600],
338
+                            fontSize: 16,
339
+                          ),
340
+                        ),
341
+                        if (_isSearching) ...[
342
+                          const SizedBox(height: 8),
343
+                          TextButton(
344
+                            onPressed: () => _showFeedbackDialog(context),
345
+                            child: const Text('反馈问题给我们'),
346
+                          ),
347
+                        ],
348
+                      ],
349
+                    ),
350
+                  )
351
+                : ListView.builder(
352
+                    padding: const EdgeInsets.all(16),
353
+                    itemCount: _filteredFAQs.length,
354
+                    itemBuilder: (context, index) {
355
+                      final faq = _filteredFAQs[index];
356
+                      final category = _categories.firstWhere(
357
+                        (cat) => cat.id == faq.categoryId,
358
+                        orElse: () => _categories.last,
359
+                      );
360
+                      
361
+                      return Card(
362
+                        margin: const EdgeInsets.only(bottom: 12),
363
+                        elevation: 1,
364
+                        shape: RoundedRectangleBorder(
365
+                          borderRadius: BorderRadius.circular(12),
366
+                          side: BorderSide(color: Colors.grey[200]!),
367
+                        ),
368
+                        child: ExpansionTile(
369
+                          key: Key(faq.id),
370
+                          initiallyExpanded: faq.isExpanded,
371
+                          onExpansionChanged: (_) => _toggleFAQ(index),
372
+                          leading: Container(
373
+                            width: 36,
374
+                            height: 36,
375
+                            decoration: BoxDecoration(
376
+                              color: category.color.withOpacity(0.1),
377
+                              borderRadius: BorderRadius.circular(8),
378
+                            ),
379
+                            child: Icon(
380
+                              category.icon,
381
+                              color: category.color,
382
+                              size: 20,
383
+                            ),
384
+                          ),
385
+                          title: Text(
386
+                            faq.question,
387
+                            style: const TextStyle(
388
+                              fontWeight: FontWeight.w500,
389
+                              fontSize: 15,
390
+                            ),
391
+                          ),
392
+                          children: [
393
+                            Padding(
394
+                              padding: const EdgeInsets.fromLTRB(68, 8, 16, 16),
395
+                              child: Column(
396
+                                crossAxisAlignment: CrossAxisAlignment.start,
397
+                                children: [
398
+                                  Text(
399
+                                    faq.answer ?? '',
400
+                                    style: TextStyle(
401
+                                      color: Colors.grey[700],
402
+                                      fontSize: 14,
403
+                                      height: 1.5,
404
+                                    ),
405
+                                  ),
406
+                                  if (faq.id == '11') ...[
407
+                                    const SizedBox(height: 16),
408
+                                    const Text(
409
+                                      '快速联系:',
410
+                                      style: TextStyle(
411
+                                        fontWeight: FontWeight.bold,
412
+                                        fontSize: 14,
413
+                                      ),
414
+                                    ),
415
+                                    const SizedBox(height: 8),
416
+                                    Row(
417
+                                      children: [
418
+                                        ElevatedButton.icon(
419
+                                          onPressed: () => _contactSupport('phone'),
420
+                                          icon: const Icon(Icons.phone, size: 16),
421
+                                          label: const Text('电话联系'),
422
+                                          style: ElevatedButton.styleFrom(
423
+                                            backgroundColor: Colors.green[50],
424
+                                            foregroundColor: Colors.green,
425
+                                            elevation: 0,
426
+                                          ),
427
+                                        ),
428
+                                        const SizedBox(width: 12),
429
+                                        ElevatedButton.icon(
430
+                                          onPressed: () => _contactSupport('email'),
431
+                                          icon: const Icon(Icons.email, size: 16),
432
+                                          label: const Text('发送邮件'),
433
+                                          style: ElevatedButton.styleFrom(
434
+                                            backgroundColor: Colors.blue[50],
435
+                                            foregroundColor: Colors.blue,
436
+                                            elevation: 0,
437
+                                          ),
438
+                                        ),
439
+                                      ],
440
+                                    ),
441
+                                  ],
442
+                                ],
443
+                              ),
444
+                            ),
445
+                          ],
446
+                        ),
447
+                      );
448
+                    },
449
+                  ),
450
+          ),
451
+
452
+          // 底部联系栏
453
+          Container(
454
+            padding: const EdgeInsets.all(16),
455
+            decoration: BoxDecoration(
456
+              color: Colors.grey[50],
457
+              border: Border(top: BorderSide(color: Colors.grey[300]!)),
458
+            ),
459
+            child: Column(
460
+              children: [
461
+                const Text(
462
+                  '没有找到您需要的答案?',
463
+                  style: TextStyle(
464
+                    fontWeight: FontWeight.w600,
465
+                    fontSize: 15,
466
+                  ),
467
+                ),
468
+                const SizedBox(height: 8),
469
+                const Text(
470
+                  '我们的客服团队随时为您提供帮助',
471
+                  style: TextStyle(color: Colors.grey, fontSize: 13),
472
+                ),
473
+                const SizedBox(height: 16),
474
+                Row(
475
+                  mainAxisAlignment: MainAxisAlignment.spaceEvenly,
476
+                  children: [
477
+                    Expanded(
478
+                      child: OutlinedButton.icon(
479
+                        onPressed: () => _contactSupport('phone'),
480
+                        icon: const Icon(Icons.phone, size: 18),
481
+                        label: const Text('电话客服'),
482
+                        style: OutlinedButton.styleFrom(
483
+                          padding: const EdgeInsets.symmetric(vertical: 12),
484
+                        ),
485
+                      ),
486
+                    ),
487
+                    const SizedBox(width: 12),
488
+                    Expanded(
489
+                      child: ElevatedButton.icon(
490
+                        onPressed: () => _showFeedbackDialog(context),
491
+                        icon: const Icon(Icons.chat, size: 18),
492
+                        label: const Text('在线反馈'),
493
+                        style: ElevatedButton.styleFrom(
494
+                          padding: const EdgeInsets.symmetric(vertical: 12),
495
+                        ),
496
+                      ),
497
+                    ),
498
+                  ],
499
+                ),
500
+              ],
501
+            ),
502
+          ),
503
+        ],
504
+      ),
505
+    );
506
+  }
507
+}
508
+
509
+// 数据模型
510
+class FAQItem {
511
+  final String id;
512
+  final String question;
513
+  final String? answer;
514
+  final String categoryId;
515
+  bool isExpanded;
516
+
517
+  FAQItem({
518
+    required this.id,
519
+    required this.question,
520
+    this.answer,
521
+    required this.categoryId,
522
+    required this.isExpanded,
523
+  });
524
+}
525
+
526
+class FAQCategory {
527
+  final String id;
528
+  final String name;
529
+  final IconData icon;
530
+  final Color color;
531
+
532
+  FAQCategory({
533
+    required this.id,
534
+    required this.name,
535
+    required this.icon,
536
+    required this.color,
537
+  });
538
+}

+ 486
- 0
lib/presentation/screens/profile/profile_modify_password_screen.dart Ver fichero

@@ -0,0 +1,486 @@
1
+import 'package:flutter/material.dart';
2
+import 'package:provider/provider.dart';
3
+import '../../providers/auth_provider.dart';
4
+import '../../widgets/common/app_button.dart';
5
+import '../../widgets/common/app_text_field.dart';
6
+
7
+class ChangePasswordScreen extends StatefulWidget {
8
+  const ChangePasswordScreen({super.key});
9
+
10
+  @override
11
+  State<ChangePasswordScreen> createState() => _ChangePasswordScreenState();
12
+}
13
+
14
+class _ChangePasswordScreenState extends State<ChangePasswordScreen> {
15
+  // 密码控制器
16
+  late TextEditingController _oldPasswordController;
17
+  late TextEditingController _newPasswordController;
18
+  late TextEditingController _confirmPasswordController;
19
+  
20
+  final _formKey = GlobalKey<FormState>();
21
+
22
+  // 密码是否可见
23
+  bool _isOldPasswordVisible = false;
24
+  bool _isNewPasswordVisible = false;
25
+  bool _isConfirmPasswordVisible = false;
26
+  
27
+  // 密码强度相关
28
+  String _passwordStrength = '';
29
+  Color _strengthColor = Colors.grey;
30
+  double _strengthValue = 0.0;
31
+  
32
+  @override
33
+  void initState() {
34
+    super.initState();
35
+    _oldPasswordController = TextEditingController();
36
+    _newPasswordController = TextEditingController();
37
+    _confirmPasswordController = TextEditingController();
38
+    
39
+    // 监听新密码输入,实时评估强度
40
+    _newPasswordController.addListener(_evaluatePasswordStrength);
41
+  }
42
+  
43
+  @override
44
+  void dispose() {
45
+    _oldPasswordController.dispose();
46
+    _newPasswordController.dispose();
47
+    _confirmPasswordController.dispose();
48
+    super.dispose();
49
+  }
50
+  
51
+  // 评估密码强度
52
+  void _evaluatePasswordStrength() {
53
+    final password = _newPasswordController.text;
54
+    
55
+    if (password.isEmpty) {
56
+      setState(() {
57
+        _passwordStrength = '';
58
+        _strengthValue = 0.0;
59
+        _strengthColor = Colors.grey;
60
+      });
61
+      return;
62
+    }
63
+    
64
+    // 强度评估逻辑
65
+    int score = 0;
66
+    if (password.length >= 8) score++;
67
+    if (RegExp(r'[A-Z]').hasMatch(password)) score++;
68
+    if (RegExp(r'[a-z]').hasMatch(password)) score++;
69
+    if (RegExp(r'[0-9]').hasMatch(password)) score++;
70
+    if (RegExp(r'[!@#$%^&*(),.?":{}|<>]').hasMatch(password)) score++;
71
+    
72
+    setState(() {
73
+      _strengthValue = score / 5.0;
74
+      
75
+      switch (score) {
76
+        case 0:
77
+        case 1:
78
+          _passwordStrength = '弱';
79
+          _strengthColor = Colors.red;
80
+          break;
81
+        case 2:
82
+        case 3:
83
+          _passwordStrength = '中';
84
+          _strengthColor = Colors.orange;
85
+          break;
86
+        case 4:
87
+        case 5:
88
+          _passwordStrength = '强';
89
+          _strengthColor = Colors.green;
90
+          break;
91
+        default:
92
+          _passwordStrength = '';
93
+          _strengthColor = Colors.grey;
94
+      }
95
+    });
96
+  }
97
+  
98
+  // 切换密码可见性
99
+  void _togglePasswordVisibility(String field) {
100
+    setState(() {
101
+      switch (field) {
102
+        case 'old':
103
+          _isOldPasswordVisible = !_isOldPasswordVisible;
104
+          break;
105
+        case 'new':
106
+          _isNewPasswordVisible = !_isNewPasswordVisible;
107
+          break;
108
+        case 'confirm':
109
+          _isConfirmPasswordVisible = !_isConfirmPasswordVisible;
110
+          break;
111
+      }
112
+    });
113
+  }
114
+  
115
+  // 提交修改密码
116
+  Future<void> _submitChangePassword(BuildContext context) async {
117
+    if (!_formKey.currentState!.validate()) {
118
+      return;
119
+    }
120
+    
121
+    final authProvider = Provider.of<AuthProvider>(context, listen: false);
122
+    
123
+    // 调用Provider中的修改密码方法
124
+    await authProvider.modifyPassword(
125
+      _oldPasswordController.text,
126
+      _newPasswordController.text,
127
+    );
128
+    
129
+    if (authProvider.error == null && mounted) {
130
+      // 成功:显示提示并返回
131
+      ScaffoldMessenger.of(context).showSnackBar(
132
+        const SnackBar(
133
+          content: Text('密码修改成功'),
134
+          backgroundColor: Colors.green,
135
+          duration: Duration(seconds: 2),
136
+        ),
137
+      );
138
+      
139
+      // 清空表单
140
+      _oldPasswordController.clear();
141
+      _newPasswordController.clear();
142
+      _confirmPasswordController.clear();
143
+      
144
+      // 延迟返回,让用户看到成功提示
145
+      Future.delayed(const Duration(milliseconds: 1500), () {
146
+        if (mounted) {
147
+          Navigator.of(context).pop();
148
+        }
149
+      });
150
+    } else if (mounted) {
151
+      // 失败:错误信息已在Provider中设置,这里会通过Consumer显示
152
+      ScaffoldMessenger.of(context).showSnackBar(
153
+        SnackBar(
154
+          content: Text(authProvider.error ?? '密码修改失败'),
155
+          backgroundColor: Colors.red,
156
+          duration: const Duration(seconds: 3),
157
+        ),
158
+      );
159
+    }
160
+  }
161
+
162
+  @override
163
+  Widget build(BuildContext context) {
164
+    return Scaffold(
165
+      appBar: AppBar(
166
+        title: const Text('修改密码'),
167
+        leading: IconButton(
168
+          icon: const Icon(Icons.arrow_back),
169
+          onPressed: () {
170
+            // 如果有未保存的更改,提示用户
171
+            if (_oldPasswordController.text.isNotEmpty ||
172
+                _newPasswordController.text.isNotEmpty ||
173
+                _confirmPasswordController.text.isNotEmpty) {
174
+              _showUnsavedChangesDialog(context);
175
+            } else {
176
+              Navigator.of(context).pop();
177
+            }
178
+          },
179
+        ),
180
+      ),
181
+      body: Consumer<AuthProvider>(
182
+        builder: (context, authProvider, _) {
183
+          return SingleChildScrollView(
184
+            padding: const EdgeInsets.all(20),
185
+            child: Form(
186
+              key: _formKey,
187
+              child: Column(
188
+                crossAxisAlignment: CrossAxisAlignment.start,
189
+                children: [
190
+                  // 提示信息
191
+                  Container(
192
+                    padding: const EdgeInsets.all(12),
193
+                    decoration: BoxDecoration(
194
+                      color: Colors.blue[50],
195
+                      borderRadius: BorderRadius.circular(8),
196
+                      border: Border.all(color: Colors.blue[100]!),
197
+                    ),
198
+                    child: Row(
199
+                      children: [
200
+                        Icon(Icons.info_outline, color: Colors.blue[700]),
201
+                        const SizedBox(width: 10),
202
+                        Expanded(
203
+                          child: Text(
204
+                            '为了账户安全,请定期修改密码',
205
+                            style: TextStyle(
206
+                              color: Colors.blue[800],
207
+                              fontSize: 14,
208
+                            ),
209
+                          ),
210
+                        ),
211
+                      ],
212
+                    ),
213
+                  ),
214
+                  const SizedBox(height: 30),
215
+                  
216
+                  // 旧密码输入
217
+                  AppTextField(
218
+                    controller: _oldPasswordController,
219
+                    labelText: '当前密码',
220
+                    hintText: '请输入当前使用的密码',
221
+                    obscureText: !_isOldPasswordVisible,
222
+                    suffixIcon: IconButton(
223
+                      icon: Icon(
224
+                        _isOldPasswordVisible
225
+                            ? Icons.visibility_off
226
+                            : Icons.visibility,
227
+                        color: Colors.grey[600],
228
+                      ),
229
+                      onPressed: () => _togglePasswordVisibility('old'),
230
+                    ),
231
+                    validator: (value) {
232
+                      if (value == null || value.isEmpty) {
233
+                        return '请输入当前密码';
234
+                      }
235
+                      if (value.length < 6) {
236
+                        return '密码长度至少6位';
237
+                      }
238
+                      return null;
239
+                    },
240
+                  ),
241
+                  const SizedBox(height: 20),
242
+                  
243
+                  // 新密码输入
244
+                  AppTextField(
245
+                    controller: _newPasswordController,
246
+                    labelText: '新密码',
247
+                    hintText: '请输入新密码',
248
+                    obscureText: !_isNewPasswordVisible,
249
+                    suffixIcon: IconButton(
250
+                      icon: Icon(
251
+                        _isNewPasswordVisible
252
+                            ? Icons.visibility_off
253
+                            : Icons.visibility,
254
+                        color: Colors.grey[600],
255
+                      ),
256
+                      onPressed: () => _togglePasswordVisibility('new'),
257
+                    ),
258
+                    validator: (value) {
259
+                      if (value == null || value.isEmpty) {
260
+                        return '请输入新密码';
261
+                      }
262
+                      if (value.length < 8) {
263
+                        return '密码长度至少8位';
264
+                      }
265
+                      if (value == _oldPasswordController.text) {
266
+                        return '新密码不能与旧密码相同';
267
+                      }
268
+                      return null;
269
+                    },
270
+                  ),
271
+                  
272
+                  // 密码强度指示器
273
+                  if (_newPasswordController.text.isNotEmpty) ...[
274
+                    const SizedBox(height: 8),
275
+                    Column(
276
+                      crossAxisAlignment: CrossAxisAlignment.start,
277
+                      children: [
278
+                        Row(
279
+                          children: [
280
+                            Text(
281
+                              '密码强度: ',
282
+                              style: TextStyle(
283
+                                fontSize: 13,
284
+                                color: Colors.grey[700],
285
+                              ),
286
+                            ),
287
+                            Text(
288
+                              _passwordStrength,
289
+                              style: TextStyle(
290
+                                fontSize: 13,
291
+                                fontWeight: FontWeight.bold,
292
+                                color: _strengthColor,
293
+                              ),
294
+                            ),
295
+                          ],
296
+                        ),
297
+                        const SizedBox(height: 4),
298
+                        LinearProgressIndicator(
299
+                          value: _strengthValue,
300
+                          backgroundColor: Colors.grey[200],
301
+                          color: _strengthColor,
302
+                          minHeight: 6,
303
+                          borderRadius: BorderRadius.circular(3),
304
+                        ),
305
+                        const SizedBox(height: 4),
306
+                        Text(
307
+                          '建议使用字母、数字和特殊符号的组合',
308
+                          style: TextStyle(
309
+                            fontSize: 12,
310
+                            color: Colors.grey[600],
311
+                          ),
312
+                        ),
313
+                      ],
314
+                    ),
315
+                  ],
316
+                  const SizedBox(height: 20),
317
+                  
318
+                  // 确认新密码输入
319
+                  AppTextField(
320
+                    controller: _confirmPasswordController,
321
+                    labelText: '确认新密码',
322
+                    hintText: '请再次输入新密码',
323
+                    obscureText: !_isConfirmPasswordVisible,
324
+                    suffixIcon: IconButton(
325
+                      icon: Icon(
326
+                        _isConfirmPasswordVisible
327
+                            ? Icons.visibility_off
328
+                            : Icons.visibility,
329
+                        color: Colors.grey[600],
330
+                      ),
331
+                      onPressed: () => _togglePasswordVisibility('confirm'),
332
+                    ),
333
+                    validator: (value) {
334
+                      if (value == null || value.isEmpty) {
335
+                        return '请确认新密码';
336
+                      }
337
+                      if (value != _newPasswordController.text) {
338
+                        return '两次输入的密码不一致';
339
+                      }
340
+                      return null;
341
+                    },
342
+                  ),
343
+                  const SizedBox(height: 30),
344
+                  
345
+                  // 密码要求说明
346
+                  Container(
347
+                    padding: const EdgeInsets.all(15),
348
+                    decoration: BoxDecoration(
349
+                      color: Colors.grey[50],
350
+                      borderRadius: BorderRadius.circular(8),
351
+                      border: Border.all(color: Colors.grey[300]!),
352
+                    ),
353
+                    child: Column(
354
+                      crossAxisAlignment: CrossAxisAlignment.start,
355
+                      children: [
356
+                        Text(
357
+                          '密码要求:',
358
+                          style: TextStyle(
359
+                            fontWeight: FontWeight.bold,
360
+                            color: Colors.grey[800],
361
+                          ),
362
+                        ),
363
+                        const SizedBox(height: 8),
364
+                        _buildRequirementItem('至少8个字符', true),
365
+                        _buildRequirementItem('包含大小写字母', false),
366
+                        _buildRequirementItem('包含数字', false),
367
+                        _buildRequirementItem('可包含特殊符号(如 ! @ # \$)', false),
368
+                      ],
369
+                    ),
370
+                  ),
371
+                  const SizedBox(height: 40),
372
+                  
373
+                  // 错误信息显示
374
+                  if (authProvider.error != null)
375
+                    Padding(
376
+                      padding: const EdgeInsets.only(bottom: 16),
377
+                      child: Container(
378
+                        padding: const EdgeInsets.all(12),
379
+                        decoration: BoxDecoration(
380
+                          color: Colors.red[50],
381
+                          borderRadius: BorderRadius.circular(8),
382
+                          border: Border.all(color: Colors.red[200]!),
383
+                        ),
384
+                        child: Row(
385
+                          children: [
386
+                            Icon(Icons.error_outline, color: Colors.red[700]),
387
+                            const SizedBox(width: 10),
388
+                            Expanded(
389
+                              child: Text(
390
+                                authProvider.error!,
391
+                                style: TextStyle(color: Colors.red[700]),
392
+                              ),
393
+                            ),
394
+                          ],
395
+                        ),
396
+                      ),
397
+                    ),
398
+                  
399
+                  // 保存按钮
400
+                  AppButton(
401
+                    text: '确认修改密码',
402
+                    isLoading: authProvider.isLoading,
403
+                    enabled: !authProvider.isLoading,
404
+                    onPressed: () => _submitChangePassword(context),
405
+                  ),
406
+                  const SizedBox(height: 20),
407
+                  
408
+                  // 取消按钮
409
+                  if (!authProvider.isLoading)
410
+                    Center(
411
+                      child: TextButton(
412
+                        onPressed: () {
413
+                          if (_oldPasswordController.text.isNotEmpty ||
414
+                              _newPasswordController.text.isNotEmpty ||
415
+                              _confirmPasswordController.text.isNotEmpty) {
416
+                            _showUnsavedChangesDialog(context);
417
+                          } else {
418
+                            Navigator.of(context).pop();
419
+                          }
420
+                        },
421
+                        child: const Text(
422
+                          '取消',
423
+                          style: TextStyle(color: Colors.grey),
424
+                        ),
425
+                      ),
426
+                    ),
427
+                ],
428
+              ),
429
+            ),
430
+          );
431
+        },
432
+      ),
433
+    );
434
+  }
435
+  
436
+  // 构建密码要求项
437
+  Widget _buildRequirementItem(String text, bool isRequired) {
438
+    return Padding(
439
+      padding: const EdgeInsets.only(bottom: 4),
440
+      child: Row(
441
+        children: [
442
+          Icon(
443
+            isRequired ? Icons.check_circle : Icons.info_outline,
444
+            size: 16,
445
+            color: isRequired ? Colors.green : Colors.grey[600],
446
+          ),
447
+          const SizedBox(width: 8),
448
+          Text(
449
+            text,
450
+            style: TextStyle(
451
+              fontSize: 13,
452
+              color: Colors.grey[700],
453
+            ),
454
+          ),
455
+        ],
456
+      ),
457
+    );
458
+  }
459
+  
460
+  // 显示未保存更改的对话框
461
+  void _showUnsavedChangesDialog(BuildContext context) {
462
+    showDialog(
463
+      context: context,
464
+      builder: (context) => AlertDialog(
465
+        title: const Text('放弃修改?'),
466
+        content: const Text('您有未保存的密码修改,确定要离开吗?'),
467
+        actions: [
468
+          TextButton(
469
+            onPressed: () => Navigator.of(context).pop(),
470
+            child: const Text('取消'),
471
+          ),
472
+          TextButton(
473
+            onPressed: () {
474
+              Navigator.of(context).pop(); // 关闭对话框
475
+              Navigator.of(context).pop(); // 返回上一页
476
+            },
477
+            child: const Text(
478
+              '确定',
479
+              style: TextStyle(color: Colors.red),
480
+            ),
481
+          ),
482
+        ],
483
+      ),
484
+    );
485
+  }
486
+}

+ 25
- 4
lib/presentation/screens/profile/profile_screen.dart Ver fichero

@@ -4,7 +4,10 @@ import '../../../core/constants/route_constants.dart';
4 4
 import '../../providers/auth_provider.dart';
5 5
 import '../../navigation/bottom_nav_bar.dart';
6 6
 import '../../widgets/common/app_button.dart';
7
+import 'profile_about_screen.dart';
7 8
 import 'profile_detail_screen.dart';
9
+import 'profile_help_screen.dart';
10
+import 'profile_modify_password_screen.dart';
8 11
 
9 12
 class ProfileScreen extends StatelessWidget {
10 13
   const ProfileScreen({super.key});
@@ -236,8 +239,14 @@ class _ProfileContentState extends State<_ProfileContent> {
236 239
                         _buildMenuItem(
237 240
                           icon: Icons.lock_outline,
238 241
                           title: '账号安全',
239
-                          subtitle: '修改密码和安全设置',
240
-                          onTap: () {},
242
+                          subtitle: '修改密码',
243
+                          onTap: () {
244
+                            Navigator.of(context).push(
245
+                              MaterialPageRoute(
246
+                                builder: (_) => const ChangePasswordScreen(),
247
+                              ),
248
+                            );
249
+                          },
241 250
                         ),
242 251
                         _buildMenuItem(
243 252
                           icon: Icons.notifications_none,
@@ -280,13 +289,25 @@ class _ProfileContentState extends State<_ProfileContent> {
280 289
                         icon: Icons.help_outline,
281 290
                         title: '帮助中心',
282 291
                         subtitle: '常见问题和帮助文档',
283
-                        onTap: () {},
292
+                        onTap: () {
293
+                          Navigator.of(context).push(
294
+                              MaterialPageRoute(
295
+                                builder: (_) => const HelpCenterScreen(),
296
+                              ),
297
+                            );
298
+                        },
284 299
                       ),
285 300
                       _buildMenuItem(
286 301
                         icon: Icons.info_outline,
287 302
                         title: '关于我们',
288 303
                         subtitle: '了解应用信息',
289
-                        onTap: () {},
304
+                        onTap: () {
305
+                          Navigator.of(context).push(
306
+                              MaterialPageRoute(
307
+                                builder: (_) => const AboutUsScreen(),
308
+                              ),
309
+                            );
310
+                        },
290 311
                       ),
291 312
                       if (authProvider.isAuthenticated) ... [
292 313
                         _buildMenuItem(

+ 1
- 1
lib/presentation/screens/splash_screen.dart Ver fichero

@@ -47,7 +47,7 @@ class SplashScreenState extends State<SplashScreen> {
47 47
             ),
48 48
             const SizedBox(height: 20),
49 49
             const Text(
50
-              '采油会',
50
+              '中了么',
51 51
               style: TextStyle(
52 52
                 fontSize: 32,
53 53
                 fontWeight: FontWeight.bold,

+ 92
- 4
pubspec.lock Ver fichero

@@ -148,10 +148,10 @@ packages:
148 148
     dependency: "direct main"
149 149
     description:
150 150
       name: get_it
151
-      sha256: d85128a5dae4ea777324730dc65edd9c9f43155c109d5cc0a69cab74139fbac1
151
+      sha256: "1d648d2dd2047d7f7450d5727ca24ee435f240385753d90b49650e3cdff32e56"
152 152
       url: "https://pub.flutter-io.cn"
153 153
     source: hosted
154
-    version: "7.7.0"
154
+    version: "9.2.0"
155 155
   http:
156 156
     dependency: "direct main"
157 157
     description:
@@ -172,10 +172,10 @@ packages:
172 172
     dependency: "direct main"
173 173
     description:
174 174
       name: intl
175
-      sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d"
175
+      sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5"
176 176
       url: "https://pub.flutter-io.cn"
177 177
     source: hosted
178
-    version: "0.18.1"
178
+    version: "0.20.2"
179 179
   js:
180 180
     dependency: transitive
181 181
     description:
@@ -248,6 +248,22 @@ packages:
248 248
       url: "https://pub.flutter-io.cn"
249 249
     source: hosted
250 250
     version: "1.0.0"
251
+  package_info_plus:
252
+    dependency: "direct main"
253
+    description:
254
+      name: package_info_plus
255
+      sha256: "7e76fad405b3e4016cd39d08f455a4eb5199723cf594cd1b8916d47140d93017"
256
+      url: "https://pub.flutter-io.cn"
257
+    source: hosted
258
+    version: "4.2.0"
259
+  package_info_plus_platform_interface:
260
+    dependency: transitive
261
+    description:
262
+      name: package_info_plus_platform_interface
263
+      sha256: "9bc8ba46813a4cc42c66ab781470711781940780fd8beddd0c3da62506d3a6c6"
264
+      url: "https://pub.flutter-io.cn"
265
+    source: hosted
266
+    version: "2.0.1"
251 267
   path:
252 268
     dependency: transitive
253 269
     description:
@@ -445,6 +461,70 @@ packages:
445 461
       url: "https://pub.flutter-io.cn"
446 462
     source: hosted
447 463
     version: "1.4.0"
464
+  url_launcher:
465
+    dependency: "direct main"
466
+    description:
467
+      name: url_launcher
468
+      sha256: f6a7e5c4835bb4e3026a04793a4199ca2d14c739ec378fdfe23fc8075d0439f8
469
+      url: "https://pub.flutter-io.cn"
470
+    source: hosted
471
+    version: "6.3.2"
472
+  url_launcher_android:
473
+    dependency: transitive
474
+    description:
475
+      name: url_launcher_android
476
+      sha256: "767344bf3063897b5cf0db830e94f904528e6dd50a6dfaf839f0abf509009611"
477
+      url: "https://pub.flutter-io.cn"
478
+    source: hosted
479
+    version: "6.3.28"
480
+  url_launcher_ios:
481
+    dependency: transitive
482
+    description:
483
+      name: url_launcher_ios
484
+      sha256: cfde38aa257dae62ffe79c87fab20165dfdf6988c1d31b58ebf59b9106062aad
485
+      url: "https://pub.flutter-io.cn"
486
+    source: hosted
487
+    version: "6.3.6"
488
+  url_launcher_linux:
489
+    dependency: transitive
490
+    description:
491
+      name: url_launcher_linux
492
+      sha256: d5e14138b3bc193a0f63c10a53c94b91d399df0512b1f29b94a043db7482384a
493
+      url: "https://pub.flutter-io.cn"
494
+    source: hosted
495
+    version: "3.2.2"
496
+  url_launcher_macos:
497
+    dependency: transitive
498
+    description:
499
+      name: url_launcher_macos
500
+      sha256: "368adf46f71ad3c21b8f06614adb38346f193f3a59ba8fe9a2fd74133070ba18"
501
+      url: "https://pub.flutter-io.cn"
502
+    source: hosted
503
+    version: "3.2.5"
504
+  url_launcher_platform_interface:
505
+    dependency: transitive
506
+    description:
507
+      name: url_launcher_platform_interface
508
+      sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029"
509
+      url: "https://pub.flutter-io.cn"
510
+    source: hosted
511
+    version: "2.3.2"
512
+  url_launcher_web:
513
+    dependency: transitive
514
+    description:
515
+      name: url_launcher_web
516
+      sha256: "4bd2b7b4dc4d4d0b94e5babfffbca8eac1a126c7f3d6ecbc1a11013faa3abba2"
517
+      url: "https://pub.flutter-io.cn"
518
+    source: hosted
519
+    version: "2.4.1"
520
+  url_launcher_windows:
521
+    dependency: transitive
522
+    description:
523
+      name: url_launcher_windows
524
+      sha256: "712c70ab1b99744ff066053cbe3e80c73332b38d46e5e945c98689b2e66fc15f"
525
+      url: "https://pub.flutter-io.cn"
526
+    source: hosted
527
+    version: "3.1.5"
448 528
   vector_graphics:
449 529
     dependency: transitive
450 530
     description:
@@ -493,6 +573,14 @@ packages:
493 573
       url: "https://pub.flutter-io.cn"
494 574
     source: hosted
495 575
     version: "1.1.1"
576
+  win32:
577
+    dependency: transitive
578
+    description:
579
+      name: win32
580
+      sha256: d7cb55e04cd34096cd3a79b3330245f54cb96a370a1c27adb3c84b917de8b08e
581
+      url: "https://pub.flutter-io.cn"
582
+    source: hosted
583
+    version: "5.15.0"
496 584
   xdg_directories:
497 585
     dependency: transitive
498 586
     description:

+ 4
- 2
pubspec.yaml Ver fichero

@@ -14,8 +14,10 @@ dependencies:
14 14
   http: ^1.1.0
15 15
   shared_preferences: ^2.2.2
16 16
   flutter_svg: ^2.0.9
17
-  intl: ^0.18.1
18
-  get_it: ^7.6.4
17
+  intl: ^0.20.2
18
+  get_it: ^9.2.0
19
+  url_launcher: ^6.1.0
20
+  package_info_plus: ^4.0.1
19 21
   # 安全相关依赖
20 22
   crypto: ^3.0.3        # SHA256等哈希算法
21 23
   encrypt: ^5.0.1       # AES加密