diff --git a/app/lib/app.dart b/app/lib/app.dart index 2f9bd0d..e3dbb81 100644 --- a/app/lib/app.dart +++ b/app/lib/app.dart @@ -58,6 +58,8 @@ class NuanjiApp extends StatelessWidget { // 异步恢复 SyncEngine 持久化队列(fire-and-forget,不阻塞 UI) syncEngine.restorePendingQueue(); + // 启动网络监听 — 网络恢复时自动触发 trySync() + syncEngine.startAutoSync(); // 认证状态监听:登出时清除 token // 注意:登录时 token 由 AuthRepository.login() 直接注入 ApiClient diff --git a/app/lib/data/local/collections/journal_element_collection.g.dart b/app/lib/data/local/collections/journal_element_collection.g.dart index b03676c..c3532f7 100644 --- a/app/lib/data/local/collections/journal_element_collection.g.dart +++ b/app/lib/data/local/collections/journal_element_collection.g.dart @@ -16,7 +16,7 @@ extension GetJournalElementCollectionCollection on Isar { const JournalElementCollectionSchema = CollectionSchema( name: r'JournalElementCollection', - id: 5678901234567001, + id: -1002, properties: { r'contentJson': PropertySchema( id: 0, @@ -96,7 +96,7 @@ const JournalElementCollectionSchema = CollectionSchema( idName: r'isarId', indexes: { r'id': IndexSchema( - id: 5678901234567002, + id: -2002, name: r'id', unique: false, replace: false, @@ -109,7 +109,7 @@ const JournalElementCollectionSchema = CollectionSchema( ], ), r'journalId': IndexSchema( - id: 5678901234567003, + id: 3001, name: r'journalId', unique: false, replace: false, diff --git a/app/lib/data/local/collections/journal_entry_collection.dart b/app/lib/data/local/collections/journal_entry_collection.dart index e278e47..55a1c20 100644 --- a/app/lib/data/local/collections/journal_entry_collection.dart +++ b/app/lib/data/local/collections/journal_entry_collection.dart @@ -46,6 +46,9 @@ class JournalEntryCollection { /// 关联主题 ID(可选) String? assignedTopicId; + /// 内容摘要(自动从文本元素提取) + String? contentExcerpt; + /// 版本号(乐观锁) int version = 1; diff --git a/app/lib/data/local/collections/journal_entry_collection.g.dart b/app/lib/data/local/collections/journal_entry_collection.g.dart index d125668..05ed74e 100644 --- a/app/lib/data/local/collections/journal_entry_collection.g.dart +++ b/app/lib/data/local/collections/journal_entry_collection.g.dart @@ -16,7 +16,7 @@ extension GetJournalEntryCollectionCollection on Isar { const JournalEntryCollectionSchema = CollectionSchema( name: r'JournalEntryCollection', - id: 5678901234567004, + id: -1001, properties: { r'assignedTopicId': PropertySchema( id: 0, @@ -33,63 +33,68 @@ const JournalEntryCollectionSchema = CollectionSchema( name: r'classId', type: IsarType.string, ), - r'createdAtEpoch': PropertySchema( + r'contentExcerpt': PropertySchema( id: 3, + name: r'contentExcerpt', + type: IsarType.string, + ), + r'createdAtEpoch': PropertySchema( + id: 4, name: r'createdAtEpoch', type: IsarType.long, ), r'dateEpoch': PropertySchema( - id: 4, + id: 5, name: r'dateEpoch', type: IsarType.long, ), r'id': PropertySchema( - id: 5, + id: 6, name: r'id', type: IsarType.string, ), r'isDeleted': PropertySchema( - id: 6, + id: 7, name: r'isDeleted', type: IsarType.bool, ), r'isPrivate': PropertySchema( - id: 7, + id: 8, name: r'isPrivate', type: IsarType.bool, ), r'mood': PropertySchema( - id: 8, + id: 9, name: r'mood', type: IsarType.string, ), r'sharedToClass': PropertySchema( - id: 9, + id: 10, name: r'sharedToClass', type: IsarType.bool, ), r'tagsJson': PropertySchema( - id: 10, + id: 11, name: r'tagsJson', type: IsarType.string, ), r'title': PropertySchema( - id: 11, + id: 12, name: r'title', type: IsarType.string, ), r'updatedAtEpoch': PropertySchema( - id: 12, + id: 13, name: r'updatedAtEpoch', type: IsarType.long, ), r'version': PropertySchema( - id: 13, + id: 14, name: r'version', type: IsarType.long, ), r'weather': PropertySchema( - id: 14, + id: 15, name: r'weather', type: IsarType.string, ) @@ -101,7 +106,7 @@ const JournalEntryCollectionSchema = CollectionSchema( idName: r'isarId', indexes: { r'id': IndexSchema( - id: 5678901234567002, + id: -2001, name: r'id', unique: false, replace: false, @@ -141,6 +146,12 @@ int _journalEntryCollectionEstimateSize( bytesCount += 3 + value.length * 3; } } + { + final value = object.contentExcerpt; + if (value != null) { + bytesCount += 3 + value.length * 3; + } + } bytesCount += 3 + object.id.length * 3; bytesCount += 3 + object.mood.length * 3; bytesCount += 3 + object.tagsJson.length * 3; @@ -158,18 +169,19 @@ void _journalEntryCollectionSerialize( writer.writeString(offsets[0], object.assignedTopicId); writer.writeString(offsets[1], object.authorId); writer.writeString(offsets[2], object.classId); - writer.writeLong(offsets[3], object.createdAtEpoch); - writer.writeLong(offsets[4], object.dateEpoch); - writer.writeString(offsets[5], object.id); - writer.writeBool(offsets[6], object.isDeleted); - writer.writeBool(offsets[7], object.isPrivate); - writer.writeString(offsets[8], object.mood); - writer.writeBool(offsets[9], object.sharedToClass); - writer.writeString(offsets[10], object.tagsJson); - writer.writeString(offsets[11], object.title); - writer.writeLong(offsets[12], object.updatedAtEpoch); - writer.writeLong(offsets[13], object.version); - writer.writeString(offsets[14], object.weather); + writer.writeString(offsets[3], object.contentExcerpt); + writer.writeLong(offsets[4], object.createdAtEpoch); + writer.writeLong(offsets[5], object.dateEpoch); + writer.writeString(offsets[6], object.id); + writer.writeBool(offsets[7], object.isDeleted); + writer.writeBool(offsets[8], object.isPrivate); + writer.writeString(offsets[9], object.mood); + writer.writeBool(offsets[10], object.sharedToClass); + writer.writeString(offsets[11], object.tagsJson); + writer.writeString(offsets[12], object.title); + writer.writeLong(offsets[13], object.updatedAtEpoch); + writer.writeLong(offsets[14], object.version); + writer.writeString(offsets[15], object.weather); } JournalEntryCollection _journalEntryCollectionDeserialize( @@ -182,19 +194,20 @@ JournalEntryCollection _journalEntryCollectionDeserialize( object.assignedTopicId = reader.readStringOrNull(offsets[0]); object.authorId = reader.readString(offsets[1]); object.classId = reader.readStringOrNull(offsets[2]); - object.createdAtEpoch = reader.readLong(offsets[3]); - object.dateEpoch = reader.readLong(offsets[4]); - object.id = reader.readString(offsets[5]); - object.isDeleted = reader.readBool(offsets[6]); - object.isPrivate = reader.readBool(offsets[7]); + object.contentExcerpt = reader.readStringOrNull(offsets[3]); + object.createdAtEpoch = reader.readLong(offsets[4]); + object.dateEpoch = reader.readLong(offsets[5]); + object.id = reader.readString(offsets[6]); + object.isDeleted = reader.readBool(offsets[7]); + object.isPrivate = reader.readBool(offsets[8]); object.isarId = id; - object.mood = reader.readString(offsets[8]); - object.sharedToClass = reader.readBool(offsets[9]); - object.tagsJson = reader.readString(offsets[10]); - object.title = reader.readString(offsets[11]); - object.updatedAtEpoch = reader.readLong(offsets[12]); - object.version = reader.readLong(offsets[13]); - object.weather = reader.readString(offsets[14]); + object.mood = reader.readString(offsets[9]); + object.sharedToClass = reader.readBool(offsets[10]); + object.tagsJson = reader.readString(offsets[11]); + object.title = reader.readString(offsets[12]); + object.updatedAtEpoch = reader.readLong(offsets[13]); + object.version = reader.readLong(offsets[14]); + object.weather = reader.readString(offsets[15]); return object; } @@ -212,28 +225,30 @@ P _journalEntryCollectionDeserializeProp

( case 2: return (reader.readStringOrNull(offset)) as P; case 3: - return (reader.readLong(offset)) as P; + return (reader.readStringOrNull(offset)) as P; case 4: return (reader.readLong(offset)) as P; case 5: - return (reader.readString(offset)) as P; + return (reader.readLong(offset)) as P; case 6: - return (reader.readBool(offset)) as P; + return (reader.readString(offset)) as P; case 7: return (reader.readBool(offset)) as P; case 8: - return (reader.readString(offset)) as P; - case 9: return (reader.readBool(offset)) as P; - case 10: + case 9: return (reader.readString(offset)) as P; + case 10: + return (reader.readBool(offset)) as P; case 11: return (reader.readString(offset)) as P; case 12: - return (reader.readLong(offset)) as P; + return (reader.readString(offset)) as P; case 13: return (reader.readLong(offset)) as P; case 14: + return (reader.readLong(offset)) as P; + case 15: return (reader.readString(offset)) as P; default: throw IsarError('Unknown property with id $propertyId'); @@ -832,6 +847,162 @@ extension JournalEntryCollectionQueryFilter on QueryBuilder< }); } + QueryBuilder contentExcerptIsNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(const FilterCondition.isNull( + property: r'contentExcerpt', + )); + }); + } + + QueryBuilder contentExcerptIsNotNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(const FilterCondition.isNotNull( + property: r'contentExcerpt', + )); + }); + } + + QueryBuilder contentExcerptEqualTo( + String? value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'contentExcerpt', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder contentExcerptGreaterThan( + String? value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'contentExcerpt', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder contentExcerptLessThan( + String? value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'contentExcerpt', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder contentExcerptBetween( + String? lower, + String? upper, { + bool includeLower = true, + bool includeUpper = true, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'contentExcerpt', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder contentExcerptStartsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.startsWith( + property: r'contentExcerpt', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder contentExcerptEndsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.endsWith( + property: r'contentExcerpt', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + contentExcerptContains(String value, {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.contains( + property: r'contentExcerpt', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + contentExcerptMatches(String pattern, {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.matches( + property: r'contentExcerpt', + wildcard: pattern, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder contentExcerptIsEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'contentExcerpt', + value: '', + )); + }); + } + + QueryBuilder contentExcerptIsNotEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + property: r'contentExcerpt', + value: '', + )); + }); + } + QueryBuilder createdAtEpochEqualTo(int value) { return QueryBuilder.apply(this, (query) { @@ -1883,6 +2054,20 @@ extension JournalEntryCollectionQuerySortBy }); } + QueryBuilder + sortByContentExcerpt() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'contentExcerpt', Sort.asc); + }); + } + + QueryBuilder + sortByContentExcerptDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'contentExcerpt', Sort.desc); + }); + } + QueryBuilder sortByCreatedAtEpoch() { return QueryBuilder.apply(this, (query) { @@ -2096,6 +2281,20 @@ extension JournalEntryCollectionQuerySortThenBy on QueryBuilder< }); } + QueryBuilder + thenByContentExcerpt() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'contentExcerpt', Sort.asc); + }); + } + + QueryBuilder + thenByContentExcerptDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'contentExcerpt', Sort.desc); + }); + } + QueryBuilder thenByCreatedAtEpoch() { return QueryBuilder.apply(this, (query) { @@ -2303,6 +2502,14 @@ extension JournalEntryCollectionQueryWhereDistinct }); } + QueryBuilder + distinctByContentExcerpt({bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'contentExcerpt', + caseSensitive: caseSensitive); + }); + } + QueryBuilder distinctByCreatedAtEpoch() { return QueryBuilder.apply(this, (query) { @@ -2417,6 +2624,13 @@ extension JournalEntryCollectionQueryProperty on QueryBuilder< }); } + QueryBuilder + contentExcerptProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'contentExcerpt'); + }); + } + QueryBuilder createdAtEpochProperty() { return QueryBuilder.apply(this, (query) { diff --git a/app/lib/data/local/collections/pending_operation_collection.g.dart b/app/lib/data/local/collections/pending_operation_collection.g.dart index bc1f096..18e57b2 100644 --- a/app/lib/data/local/collections/pending_operation_collection.g.dart +++ b/app/lib/data/local/collections/pending_operation_collection.g.dart @@ -16,7 +16,7 @@ extension GetPendingOperationCollectionCollection on Isar { const PendingOperationCollectionSchema = CollectionSchema( name: r'PendingOperationCollection', - id: 5678901234567005, + id: -1003, properties: { r'createdAtEpoch': PropertySchema( id: 0, @@ -61,7 +61,7 @@ const PendingOperationCollectionSchema = CollectionSchema( idName: r'isarId', indexes: { r'id': IndexSchema( - id: 5678901234567002, + id: -2003, name: r'id', unique: false, replace: false, diff --git a/app/lib/data/models/journal_entry.dart b/app/lib/data/models/journal_entry.dart index 27899d8..54aea8d 100644 --- a/app/lib/data/models/journal_entry.dart +++ b/app/lib/data/models/journal_entry.dart @@ -42,6 +42,10 @@ class JournalEntry { final bool isPrivate; final bool sharedToClass; final String? assignedTopicId; + + /// 内容摘要 — 自动从文本元素提取,用于列表预览 + final String? contentExcerpt; + final int version; final DateTime createdAt; final DateTime updatedAt; @@ -58,6 +62,7 @@ class JournalEntry { this.isPrivate = true, this.sharedToClass = false, this.assignedTopicId, + this.contentExcerpt, this.version = 1, required this.createdAt, required this.updatedAt, @@ -77,6 +82,8 @@ class JournalEntry { bool? sharedToClass, String? assignedTopicId, bool clearAssignedTopicId = false, + String? contentExcerpt, + bool clearContentExcerpt = false, int? version, DateTime? createdAt, DateTime? updatedAt, @@ -94,6 +101,9 @@ class JournalEntry { sharedToClass: sharedToClass ?? this.sharedToClass, assignedTopicId: clearAssignedTopicId ? null : (assignedTopicId ?? this.assignedTopicId), + contentExcerpt: clearContentExcerpt + ? null + : (contentExcerpt ?? this.contentExcerpt), version: version ?? this.version, createdAt: createdAt ?? this.createdAt, updatedAt: updatedAt ?? this.updatedAt, @@ -111,6 +121,7 @@ class JournalEntry { 'is_private': isPrivate, 'shared_to_class': sharedToClass, 'assigned_topic_id': assignedTopicId, + 'content_excerpt': contentExcerpt, 'version': version, 'created_at': createdAt.toIso8601String(), 'updated_at': updatedAt.toIso8601String(), @@ -134,6 +145,7 @@ class JournalEntry { isPrivate: (json['is_private'] as bool?) ?? true, sharedToClass: (json['shared_to_class'] as bool?) ?? false, assignedTopicId: json['assigned_topic_id'] as String?, + contentExcerpt: json['content_excerpt'] as String?, version: (json['version'] as int?) ?? 1, createdAt: DateTime.parse(json['created_at'] as String), updatedAt: DateTime.parse(json['updated_at'] as String), diff --git a/app/lib/data/services/sse_notification_service.dart b/app/lib/data/services/sse_notification_service.dart index d01bbc8..315bf2b 100644 --- a/app/lib/data/services/sse_notification_service.dart +++ b/app/lib/data/services/sse_notification_service.dart @@ -39,7 +39,7 @@ class SseNotificationService { SseNotificationService({ required String token, - String baseUrl = 'http://localhost:3000/api/v1', + required String baseUrl, }) : _token = token, _baseUrl = baseUrl; diff --git a/app/lib/data/services/sync_engine.dart b/app/lib/data/services/sync_engine.dart index 0afbce8..b77bebc 100644 --- a/app/lib/data/services/sync_engine.dart +++ b/app/lib/data/services/sync_engine.dart @@ -12,10 +12,12 @@ // - 联网后自动推送待同步操作 // - 版本冲突时本地版本覆盖远端(简单策略) +import 'dart:async'; import 'dart:convert'; import 'dart:collection'; import 'package:connectivity_plus/connectivity_plus.dart'; +import 'package:flutter/foundation.dart'; import 'package:isar/isar.dart'; import '../local/isar_database.dart'; @@ -114,6 +116,7 @@ class PendingOperation { class SyncEngine { final ApiClient _apiClient; final Queue _pendingQueue = Queue(); + StreamSubscription>? _connectivitySub; SyncStatus _status = SyncStatus.idle; String? _lastError; @@ -289,6 +292,26 @@ class SyncEngine { } } + /// 启动网络监听 — 网络恢复时自动触发同步 + /// + /// 在 app.dart 中创建 SyncEngine 后调用一次。 + /// 调用 [dispose] 停止监听。 + void startAutoSync() { + _connectivitySub = Connectivity().onConnectivityChanged.listen((result) { + final isOnline = result.any((r) => r != ConnectivityResult.none); + if (isOnline && _pendingQueue.isNotEmpty && _status != SyncStatus.syncing) { + debugPrint('SyncEngine: 网络恢复,开始同步 ${_pendingQueue.length} 个操作'); + trySync(); + } + }); + } + + /// 停止网络监听并清理资源 + void dispose() { + _connectivitySub?.cancel(); + _connectivitySub = null; + } + // ============================================================ // 转换函数 // ============================================================ diff --git a/app/lib/features/auth/views/login_page.dart b/app/lib/features/auth/views/login_page.dart index 01b5273..7152602 100644 --- a/app/lib/features/auth/views/login_page.dart +++ b/app/lib/features/auth/views/login_page.dart @@ -11,6 +11,7 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:go_router/go_router.dart'; import '../../../core/constants/design_tokens.dart'; +import '../../../core/theme/app_colors.dart'; import '../../../core/theme/app_radius.dart'; import '../bloc/auth_bloc.dart'; @@ -30,6 +31,7 @@ class _LoginPageState extends State with SingleTickerProviderStateMix bool _isRegister = false; bool _obscurePassword = true; + bool _agreedToTerms = false; late final AnimationController _animController; late final Animation _fadeAnim; @@ -60,6 +62,13 @@ class _LoginPageState extends State with SingleTickerProviderStateMix void _submit() { if (!_formKey.currentState!.validate()) return; + if (_isRegister && !_agreedToTerms) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('请先阅读并同意用户协议和隐私政策')), + ); + return; + } + if (_isRegister) { context.read().add(RegisterRequested( username: _usernameController.text.trim(), @@ -106,13 +115,20 @@ class _LoginPageState extends State with SingleTickerProviderStateMix mainAxisAlignment: MainAxisAlignment.center, children: [ _buildHeader(context, colorScheme), - const SizedBox(height: DesignTokens.spacing48), + const SizedBox(height: DesignTokens.spacing32), _buildForm(context, theme, colorScheme), const SizedBox(height: DesignTokens.spacing24), _buildSubmitButton(context, colorScheme), const SizedBox(height: DesignTokens.spacing16), _buildModeToggle(context, colorScheme), - const SizedBox(height: DesignTokens.spacing32), + const SizedBox(height: DesignTokens.spacing24), + + // 协议复选框(注册模式下显示) + if (_isRegister) ...[ + _buildAgreementRow(context, colorScheme), + const SizedBox(height: DesignTokens.spacing16), + ], + BlocBuilder( builder: (context, state) { if (state is AuthError) { @@ -121,6 +137,13 @@ class _LoginPageState extends State with SingleTickerProviderStateMix return const SizedBox.shrink(); }, ), + + // 社交登录分割线 + const SizedBox(height: DesignTokens.spacing24), + _buildSocialLoginDivider(context, colorScheme), + const SizedBox(height: DesignTokens.spacing16), + _buildSocialLoginButtons(context, colorScheme), + const SizedBox(height: DesignTokens.spacing32), ], ), ), @@ -132,37 +155,83 @@ class _LoginPageState extends State with SingleTickerProviderStateMix } Widget _buildHeader(BuildContext context, ColorScheme colorScheme) { - return Column( - children: [ - Container( - width: 80, - height: 80, - decoration: BoxDecoration( - color: colorScheme.primaryContainer, - borderRadius: AppRadius.lgBorder, - ), - child: Icon( - Icons.edit_note_rounded, - size: 44, - color: colorScheme.primary, - ), + final isDark = Theme.of(context).brightness == Brightness.dark; + final bgColor = isDark ? const Color(0xFF1A1614) : const Color(0xFFFFF8F0); + final tertiarySoft = isDark ? AppColors.tertiarySoftDark : AppColors.tertiarySoftLight; + + return Container( + width: double.infinity, + padding: const EdgeInsets.symmetric(vertical: 40), + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [bgColor, tertiarySoft], ), - const SizedBox(height: DesignTokens.spacing16), - Text( - '暖记', - style: Theme.of(context).textTheme.headlineLarge?.copyWith( - fontWeight: FontWeight.bold, - color: colorScheme.primary, + ), + child: Stack( + clipBehavior: Clip.none, + children: [ + // 装饰圆圈 + Positioned(left: 20, top: -10, child: _decorCircle(60, AppColors.accent, 0.15)), + Positioned(right: 30, top: 20, child: _decorCircle(40, AppColors.secondary, 0.12)), + Positioned(left: 80, bottom: -20, child: _decorCircle(30, AppColors.tertiary, 0.18)), + Positioned(right: 60, bottom: 10, child: _decorCircle(20, AppColors.accent, 0.10)), + Positioned(left: -10, top: 50, child: _decorCircle(25, AppColors.secondary, 0.13)), + + Column( + children: [ + // Logo — 自定义笔记本图标 + Container( + width: 80, + height: 80, + decoration: BoxDecoration( + border: Border.all(color: AppColors.accent, width: 3), + borderRadius: AppRadius.lgBorder, + color: colorScheme.surface.withValues(alpha: 0.5), + ), + child: const Icon( + Icons.edit_note_rounded, + size: 44, + color: AppColors.accent, + ), ), - ), - const SizedBox(height: DesignTokens.spacing4), - Text( - '记录温暖,书写成长', - style: Theme.of(context).textTheme.bodyMedium?.copyWith( - color: colorScheme.onSurface.withValues(alpha: 0.6), + const SizedBox(height: DesignTokens.spacing16), + // 品牌名 + Text( + '暖记', + style: Theme.of(context).textTheme.headlineLarge?.copyWith( + fontWeight: FontWeight.w700, + color: AppColors.accent, + ), ), - ), - ], + const SizedBox(height: DesignTokens.spacing4), + // 标语 — Caveat 手写风格 + Text( + '记录温暖,书写成长', + style: const TextStyle( + fontSize: 18, + fontWeight: FontWeight.w500, + color: AppColors.accent, + fontFamily: 'Caveat', + ), + ), + ], + ), + ], + ), + ); + } + + /// 装饰圆圈 + Widget _decorCircle(double size, Color color, double opacity) { + return Container( + width: size, + height: size, + decoration: BoxDecoration( + color: color.withValues(alpha: opacity), + shape: BoxShape.circle, + ), ); } @@ -319,4 +388,160 @@ class _LoginPageState extends State with SingleTickerProviderStateMix ), ); } + + /// 社交登录分割线 + Widget _buildSocialLoginDivider(BuildContext context, ColorScheme colorScheme) { + final dividerColor = colorScheme.onSurface.withValues(alpha: 0.15); + return Row( + children: [ + Expanded(child: Divider(color: dividerColor)), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: Text('其他登录方式', style: Theme.of(context).textTheme.bodySmall?.copyWith( + color: colorScheme.onSurface.withValues(alpha: 0.4), + )), + ), + Expanded(child: Divider(color: dividerColor)), + ], + ); + } + + /// 社交登录按钮行 + Widget _buildSocialLoginButtons(BuildContext context, ColorScheme colorScheme) { + return Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + // 微信 + _SocialButton( + bgColor: const Color(0xFF07C160), + icon: Icons.chat_bubble, + semanticLabel: '微信登录', + onTap: () { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('微信登录即将支持')), + ); + }, + ), + const SizedBox(width: 24), + // Apple + _SocialButton( + bgColor: const Color(0xFF1D1D1F), + icon: Icons.apple, + semanticLabel: 'Apple 登录', + onTap: () { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('Apple 登录即将支持')), + ); + }, + ), + const SizedBox(width: 24), + // Google + _SocialButton( + bgColor: colorScheme.surface, + borderColor: colorScheme.outlineVariant, + child: const Text('G', style: TextStyle( + fontSize: 24, fontWeight: FontWeight.w700, color: Color(0xFF4285F4), + )), + semanticLabel: 'Google 登录', + onTap: () { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('Google 登录即将支持')), + ); + }, + ), + ], + ); + } + + /// 协议复选框行 + Widget _buildAgreementRow(BuildContext context, ColorScheme colorScheme) { + return Row( + children: [ + SizedBox( + width: 24, + height: 24, + child: Checkbox( + value: _agreedToTerms, + onChanged: (v) => setState(() => _agreedToTerms = v ?? false), + activeColor: AppColors.accent, + materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, + ), + ), + const SizedBox(width: 8), + Expanded( + child: Wrap( + children: [ + Text('我已阅读并同意', style: Theme.of(context).textTheme.bodySmall?.copyWith( + color: colorScheme.onSurface.withValues(alpha: 0.6), + )), + GestureDetector( + onTap: () { + // TODO: 打开用户协议 + }, + child: Text('《用户协议》', style: Theme.of(context).textTheme.bodySmall?.copyWith( + color: AppColors.accent, fontWeight: FontWeight.w500, + )), + ), + Text('和', style: Theme.of(context).textTheme.bodySmall?.copyWith( + color: colorScheme.onSurface.withValues(alpha: 0.6), + )), + GestureDetector( + onTap: () { + // TODO: 打开隐私政策 + }, + child: Text('《隐私政策》', style: Theme.of(context).textTheme.bodySmall?.copyWith( + color: AppColors.accent, fontWeight: FontWeight.w500, + )), + ), + ], + ), + ), + ], + ); + } +} + +/// 社交登录圆形按钮 +class _SocialButton extends StatelessWidget { + const _SocialButton({ + required this.bgColor, + required this.semanticLabel, + required this.onTap, + this.icon, + this.child, + this.borderColor, + }); + final Color bgColor; + final Color? borderColor; + final IconData? icon; + final Widget? child; + final String semanticLabel; + final VoidCallback onTap; + + @override + Widget build(BuildContext context) { + return SizedBox( + width: 56, + height: 56, + child: Material( + color: bgColor, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(28), + side: borderColor != null + ? BorderSide(color: borderColor!, width: 1.5) + : BorderSide.none, + ), + child: InkWell( + onTap: onTap, + customBorder: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(28), + ), + child: Center( + child: child ?? + Icon(icon, size: 28, color: Colors.white), + ), + ), + ), + ); + } } diff --git a/app/lib/features/calendar/views/monthly_page.dart b/app/lib/features/calendar/views/monthly_page.dart index c98bf72..e3697ab 100644 --- a/app/lib/features/calendar/views/monthly_page.dart +++ b/app/lib/features/calendar/views/monthly_page.dart @@ -50,7 +50,8 @@ class _MonthlyPageState extends State { try { final elements = await _repo.getElements(journal.id); photoCount += elements.where((e) => e.elementType == ElementType.image).length; - } catch (_) { + } catch (e) { + debugPrint('MonthlyPage: 加载日记 ${journal.id} 元素失败: $e'); // 单个日记加载元素失败不影响整体统计 } } diff --git a/app/lib/features/calendar/views/weekly_page.dart b/app/lib/features/calendar/views/weekly_page.dart index f3d5037..f86920c 100644 --- a/app/lib/features/calendar/views/weekly_page.dart +++ b/app/lib/features/calendar/views/weekly_page.dart @@ -56,7 +56,8 @@ class _WeeklyPageState extends State { _isLoading = false; }); } - } catch (_) { + } catch (e) { + debugPrint('WeeklyPage._loadWeekData 失败: $e'); if (mounted) setState(() => _isLoading = false); } } diff --git a/app/lib/features/class_/bloc/class_bloc.dart b/app/lib/features/class_/bloc/class_bloc.dart index 868c249..93e89ee 100644 --- a/app/lib/features/class_/bloc/class_bloc.dart +++ b/app/lib/features/class_/bloc/class_bloc.dart @@ -1,5 +1,6 @@ // 班级 BLoC — 通过 ClassRepository 管理班级数据 +import 'package:flutter/foundation.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:nuanji_app/data/models/journal_entry.dart'; import 'package:nuanji_app/data/models/school_class.dart'; @@ -117,9 +118,14 @@ final class ClassLoading extends ClassState { final class ClassListLoaded extends ClassState { final List classes; final bool isLoading; - const ClassListLoaded({this.classes = const [], this.isLoading = false}); - ClassListLoaded copyWith({List? classes, bool? isLoading}) => - ClassListLoaded(classes: classes ?? this.classes, isLoading: isLoading ?? this.isLoading); + final String? error; + const ClassListLoaded({this.classes = const [], this.isLoading = false, this.error}); + ClassListLoaded copyWith({List? classes, bool? isLoading, String? error, bool clearError = false}) => + ClassListLoaded( + classes: classes ?? this.classes, + isLoading: isLoading ?? this.isLoading, + error: clearError ? null : (error ?? this.error), + ); } final class ClassDetailLoaded extends ClassState { @@ -209,7 +215,8 @@ class ClassBloc extends Bloc { final classes = await _classRepo.getMyClasses(); emit(ClassListLoaded(classes: classes)); } catch (e) { - emit(ClassListLoaded(classes: const [])); + debugPrint('ClassBloc._onLoadMyClasses 失败: $e'); + emit(const ClassListLoaded()); } } @@ -224,7 +231,8 @@ class ClassBloc extends Bloc { add(ClassLoadMembers(event.classId)); add(ClassLoadTopics(event.classId)); } catch (e) { - emit(ClassError('加载班级失败: $e')); + debugPrint('ClassBloc._onClassSelected 失败: $e'); + emit(const ClassError('加载班级失败,请重试')); } } @@ -247,7 +255,8 @@ class ClassBloc extends Bloc { )) .toList(); emit(current.copyWith(members: members, isLoadingMembers: false)); - } catch (_) { + } catch (e) { + debugPrint('ClassBloc._onLoadMembers 失败: $e'); emit(current.copyWith(isLoadingMembers: false)); } } @@ -266,7 +275,8 @@ class ClassBloc extends Bloc { final classJournals = journals.where((j) => j.sharedToClass).toList(); emit(current.copyWith(diaryWall: classJournals, isLoadingWall: false)); - } catch (_) { + } catch (e) { + debugPrint('ClassBloc._onLoadDiaryWall 失败: $e'); emit(current.copyWith(isLoadingWall: false)); } } @@ -292,8 +302,9 @@ class ClassBloc extends Bloc { )) .toList(); emit(current.copyWith(topics: topics)); - } catch (_) { - // 静默失败,保留空列表 + } catch (e) { + debugPrint('ClassBloc._onLoadTopics 失败: $e'); + // 保留空列表 } } @@ -316,7 +327,8 @@ class ClassBloc extends Bloc { )) .toList(); emit(current.copyWith(comments: comments, selectedJournalId: event.journalId)); - } catch (_) { + } catch (e) { + debugPrint('ClassBloc._onLoadComments 失败: $e'); emit(current.copyWith(selectedJournalId: event.journalId)); } } @@ -335,7 +347,11 @@ class ClassBloc extends Bloc { emit(current.copyWith(classes: [...current.classes, newClass])); } } catch (e) { - // 创建失败不改变状态 + debugPrint('ClassBloc._onCreateClass 失败: $e'); + // 创建失败不改变状态,但通知 UI + if (state is ClassListLoaded) { + emit((state as ClassListLoaded).copyWith(error: '创建班级失败,请重试')); + } } } @@ -364,8 +380,12 @@ class ClassBloc extends Bloc { ); emit(current.copyWith(topics: [newTopic, ...current.topics])); } - } catch (_) { - // 静默失败 + } catch (e) { + debugPrint('ClassBloc._onTopicAssign 失败: $e'); + // 通知 UI 布置失败 + if (state is ClassDetailLoaded) { + emit((state as ClassDetailLoaded).copyWith(error: '话题布置失败,请重试')); + } } } @@ -378,7 +398,8 @@ class ClassBloc extends Bloc { // 加入成功后刷新列表 add(const ClassLoadMyClasses()); } catch (e) { - emit(ClassError('加入班级失败: $e')); + debugPrint('ClassBloc._onJoinClass 失败: $e'); + emit(const ClassError('加入班级失败,请检查班级码')); } } @@ -397,6 +418,7 @@ class ClassBloc extends Bloc { // 创建成功后重新加载评语列表 add(ClassLoadComments(event.journalId)); } catch (e) { + debugPrint('ClassBloc._onCommentCreate 失败: $e'); emit(currentState.copyWith(error: '评语发布失败')); } } diff --git a/app/lib/features/editor/bloc/editor_bloc.dart b/app/lib/features/editor/bloc/editor_bloc.dart index 6ef34ee..80877c1 100644 --- a/app/lib/features/editor/bloc/editor_bloc.dart +++ b/app/lib/features/editor/bloc/editor_bloc.dart @@ -4,13 +4,16 @@ // - 手写层:笔画列表 + 画笔设置 + 撤销/重做栈 // - 元素层:贴纸/照片/文字元素列表 + 选中元素 + 拖拽状态 // - 工具栏:当前活动工具 + 颜色面板 + 笔刷大小 +// - 标签/心情:日记标签管理 + 心情选择 + 标题编辑 // - 自动保存:笔画/元素变更 debounce → 触发保存回调 import 'dart:async'; +import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import '../widgets/stroke_model.dart'; +import '../../../data/models/journal_entry.dart'; import '../../../data/models/journal_element.dart'; // ============================================================ @@ -110,20 +113,69 @@ class ElementsLoaded extends EditorEvent { ElementsLoaded(this.elements); } +// --- 标签/心情/标题事件 --- + +/// 添加标签 +class TagAdded extends EditorEvent { + final String tag; + TagAdded(this.tag); +} + +/// 移除标签 +class TagRemoved extends EditorEvent { + final String tag; + TagRemoved(this.tag); +} + +/// 加载已有标签 +class TagsLoaded extends EditorEvent { + final List tags; + TagsLoaded(this.tags); +} + +/// 心情变更 +class MoodChanged extends EditorEvent { + final Mood mood; + MoodChanged(this.mood); +} + +/// 标题变更 +class TitleChanged extends EditorEvent { + final String title; + TitleChanged(this.title); +} + +/// 文字格式变更 +class TextFormatChanged extends EditorEvent { + final String elementId; + final bool? bold; + final bool? italic; + final bool? underline; + final String? color; + final TextAlign? alignment; + TextFormatChanged({ + required this.elementId, + this.bold, + this.italic, + this.underline, + this.color, + this.alignment, + }); +} + // ============================================================ // 状态 // ============================================================ -/// 编辑器工具枚举 +/// 编辑器工具枚举 — 对应底部 6 个工具按钮 + 内部 select 模式 enum EditorTool { - pen, // 钢笔 - pencil, // 铅笔 - marker, // 马克笔 - eraser, // 橡皮擦 - select, // 选择/移动元素 - text, // 文字输入 sticker, // 贴纸 - image, // 照片 + template, // 模板 + brush, // 画笔(含钢笔/铅笔/马克笔/橡皮子类型) + photo, // 照片 + text, // 文字 + more, // 更多 + select, // 选择/移动元素(内部模式,非 UI 按钮) } /// 编辑器状态 @@ -134,6 +186,7 @@ class EditorState { final BrushType brushType; final String brushColor; final double brushWidth; + final double brushOpacity; final int maxUndoSteps; // 元素层 @@ -143,6 +196,11 @@ class EditorState { // 工具栏 final EditorTool activeTool; + // 标签/心情/标题 + final List tags; + final Mood selectedMood; + final String title; + // 自动保存 final bool isDirty; final DateTime? lastSavedAt; @@ -153,10 +211,14 @@ class EditorState { this.brushType = BrushType.pen, this.brushColor = '#2D2420', this.brushWidth = 3.0, + this.brushOpacity = 1.0, this.maxUndoSteps = 50, this.elements = const [], this.selectedElementId, - this.activeTool = EditorTool.pen, + this.activeTool = EditorTool.brush, + this.tags = const [], + this.selectedMood = Mood.calm, + this.title = '', this.isDirty = false, this.lastSavedAt, }); @@ -167,10 +229,14 @@ class EditorState { BrushType? brushType, String? brushColor, double? brushWidth, + double? brushOpacity, List? elements, String? selectedElementId, bool clearSelection = false, EditorTool? activeTool, + List? tags, + Mood? selectedMood, + String? title, bool? isDirty, DateTime? lastSavedAt, }) => @@ -180,27 +246,28 @@ class EditorState { brushType: brushType ?? this.brushType, brushColor: brushColor ?? this.brushColor, brushWidth: brushWidth ?? this.brushWidth, + brushOpacity: brushOpacity ?? this.brushOpacity, maxUndoSteps: maxUndoSteps, elements: elements ?? this.elements, - selectedElementId: clearSelection ? null : (selectedElementId ?? this.selectedElementId), + selectedElementId: + clearSelection ? null : (selectedElementId ?? this.selectedElementId), activeTool: activeTool ?? this.activeTool, + tags: tags ?? this.tags, + selectedMood: selectedMood ?? this.selectedMood, + title: title ?? this.title, isDirty: isDirty ?? this.isDirty, lastSavedAt: lastSavedAt ?? this.lastSavedAt, ); - /// 是否处于手写模式(画笔/橡皮工具) - bool get isDrawingMode => - activeTool == EditorTool.pen || - activeTool == EditorTool.pencil || - activeTool == EditorTool.marker || - activeTool == EditorTool.eraser; + /// 是否处于手写模式 + bool get isDrawingMode => activeTool == EditorTool.brush; /// 是否处于元素操作模式 bool get isElementMode => activeTool == EditorTool.select || activeTool == EditorTool.text || activeTool == EditorTool.sticker || - activeTool == EditorTool.image; + activeTool == EditorTool.photo; } // ============================================================ @@ -238,6 +305,14 @@ class EditorBloc extends Bloc { // 工具栏事件 on(_onToolChanged); + + // 标签/心情/标题事件 + on(_onTagAdded); + on(_onTagRemoved); + on(_onTagsLoaded); + on(_onMoodChanged); + on(_onTitleChanged); + on(_onTextFormatChanged); } @override @@ -321,7 +396,8 @@ class EditorBloc extends Bloc { // ============================================================ void _onElementAdded(ElementAdded event, Emitter emit) { - final updated = List.from(state.elements)..add(event.element); + final updated = + List.from(state.elements)..add(event.element); emit(state.copyWith( elements: updated, selectedElementId: event.element.id, @@ -394,6 +470,58 @@ class EditorBloc extends Bloc { )); } + // ============================================================ + // 标签/心情/标题事件处理 + // ============================================================ + + void _onTagAdded(TagAdded event, Emitter emit) { + if (state.tags.contains(event.tag)) return; + if (state.tags.length >= 10) return; // 设计 Token: maxTags=10 + final updated = List.from(state.tags)..add(event.tag); + emit(state.copyWith(tags: updated, isDirty: true)); + _scheduleAutoSave(); + } + + void _onTagRemoved(TagRemoved event, Emitter emit) { + final updated = List.from(state.tags)..remove(event.tag); + emit(state.copyWith(tags: updated, isDirty: true)); + _scheduleAutoSave(); + } + + void _onTagsLoaded(TagsLoaded event, Emitter emit) { + emit(state.copyWith(tags: event.tags)); + } + + void _onMoodChanged(MoodChanged event, Emitter emit) { + emit(state.copyWith(selectedMood: event.mood, isDirty: true)); + _scheduleAutoSave(); + } + + void _onTitleChanged(TitleChanged event, Emitter emit) { + emit(state.copyWith(title: event.title, isDirty: true)); + _scheduleAutoSave(); + } + + void _onTextFormatChanged( + TextFormatChanged event, + Emitter emit, + ) { + final updated = state.elements.map((e) { + if (e.id != event.elementId) return e; + final content = Map.from(e.content); + if (event.bold != null) content['bold'] = event.bold; + if (event.italic != null) content['italic'] = event.italic; + if (event.underline != null) content['underline'] = event.underline; + if (event.color != null) content['color'] = event.color; + if (event.alignment != null) { + content['alignment'] = event.alignment!.index; + } + return e.copyWith(content: content); + }).toList(); + emit(state.copyWith(elements: updated, isDirty: true)); + _scheduleAutoSave(); + } + // ============================================================ // 自动保存 // ============================================================ diff --git a/app/lib/features/editor/views/editor_page.dart b/app/lib/features/editor/views/editor_page.dart index 4808768..95b3ec8 100644 --- a/app/lib/features/editor/views/editor_page.dart +++ b/app/lib/features/editor/views/editor_page.dart @@ -14,10 +14,13 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:go_router/go_router.dart'; import '../../../core/constants/design_tokens.dart'; +import '../../../core/theme/app_colors.dart'; import '../../../data/models/journal_element.dart'; -import '../../../data/models/journal_entry.dart'; +import '../../../data/models/journal_entry.dart' show JournalEntry, Mood; import '../../../data/repositories/journal_repository.dart'; import '../../../data/repositories/class_repository.dart'; +import '../../../data/services/sync_engine.dart'; +import '../../auth/bloc/auth_bloc.dart'; import '../bloc/editor_bloc.dart'; import '../widgets/handwriting_canvas.dart'; import '../widgets/stroke_model.dart'; @@ -27,6 +30,9 @@ import '../widgets/text_input_overlay.dart'; import '../widgets/image_picker_handler.dart'; import '../widgets/sticker_picker_sheet.dart'; import '../widgets/share_bottom_sheet.dart'; +import '../widgets/tag_panel.dart'; +import '../widgets/brush_panel.dart'; +import '../widgets/dot_grid_painter.dart'; /// 手账编辑器页面 class EditorPage extends StatelessWidget { @@ -39,6 +45,8 @@ class EditorPage extends StatelessWidget { Widget build(BuildContext context) { // 从 Provider 树获取 JournalRepository(IsarJournalRepository) final repo = context.read(); + // 从 Provider 树获取 SyncEngine(同步到后端) + final syncEngine = context.read(); // 可变闭包变量:跟踪已保存的日记 ID // 新建日记首次保存后赋值,后续自动更新使用此 ID @@ -48,7 +56,18 @@ class EditorPage extends StatelessWidget { create: (_) => EditorBloc( onSave: (state) async { try { - await _persistState(repo, state, (id) => savedJournalId = id, savedJournalId); + // 从 AuthBloc 获取真实用户 ID + String authorId = 'local'; + final authState = context.read().state; + if (authState is Authenticated) { + authorId = authState.user.id; + } + + await _persistState( + repo, state, (id) => savedJournalId = id, savedJournalId, + syncEngine: syncEngine, + authorId: authorId, + ); } catch (e) { debugPrint('自动保存失败: $e'); } @@ -66,24 +85,27 @@ class EditorPage extends StatelessWidget { ); } - /// 持久化编辑器状态到 Isar + /// 持久化编辑器状态到 Isar,并同步到后端 /// /// 策略: /// - 首次保存(savedJournalId == null)→ createJournal + addElement /// - 后续保存 → updateJournal + upsert 元素 /// - 笔画序列化为 handwriting_ref 元素 + /// - 保存成功后入队 SyncEngine 等待网络同步 Future _persistState( JournalRepository repo, EditorState state, void Function(String) setId, - String? savedJournalId, - ) async { + String? savedJournalId, { + required SyncEngine syncEngine, + String authorId = 'local', + }) async { final now = DateTime.now(); if (savedJournalId == null) { // --- 新建日记 --- final entry = JournalEntry.create( - authorId: 'local', // TODO: 从 AuthBloc 获取真实用户 ID + authorId: authorId, title: '${now.month}月${now.day}日的日记', date: now, ); @@ -99,11 +121,31 @@ class EditorPage extends StatelessWidget { for (final element in state.elements) { await repo.addElement(element.copyWith(journalId: entry.id)); } + + // 入队 SyncEngine 等待同步到后端 + syncEngine.enqueue(PendingOperation( + id: entry.id, + type: SyncOperationType.create, + endpoint: '/diary/journals', + data: entry.toJson(), + version: entry.version, + createdAt: now, + )); } else { // --- 更新已有日记 --- final existing = await repo.getJournal(savedJournalId); if (existing != null) { await repo.updateJournal(existing); + + // 入队 SyncEngine 等待同步到后端 + syncEngine.enqueue(PendingOperation( + id: existing.id, + type: SyncOperationType.update, + endpoint: '/diary/journals/${existing.id}', + data: existing.toJson(), + version: existing.version, + createdAt: now, + )); } // 更新笔画 @@ -154,21 +196,28 @@ class EditorPage extends StatelessWidget { } /// 显示分享面板并在用户选择后导航 - static void _showShareSheetAndNavigate( + static Future _showShareSheetAndNavigate( BuildContext context, JournalRepository repo, String? savedJournalId, - ) { + ) async { // 尝试获取用户的班级信息 String? userClassId; String userClassName = '我的班级'; try { - context.read(); - // Phase 1 简化:不等待异步调用,使用默认值 - userClassId = null; // TODO: 从 AuthBloc/ClassBloc 获取真实班级 ID - } catch (_) { - // ClassRepository 不可用(未注入) + // 从 AuthBloc 获取用户关联的班级 + final authState = context.read().state; + if (authState is Authenticated) { + final classRepo = context.read(); + final classes = await classRepo.getMyClasses(); + if (classes.isNotEmpty) { + userClassId = classes.first.id; + userClassName = classes.first.name; + } + } + } catch (e) { + debugPrint('获取班级信息失败: $e'); } showModalBottomSheet( @@ -226,94 +275,251 @@ class _EditorView extends StatelessWidget { return Scaffold( backgroundColor: colorScheme.surface, - body: SafeArea( - child: Column( - children: [ - // 顶栏 - _buildTopBar(context), + body: Column( + children: [ + // 顶栏(自带状态栏安全区) + BlocBuilder( + builder: (context, state) { + return _buildTopBar(context, state); + }, + ), - // 编辑区域(三层 Stack) - Expanded( - child: BlocBuilder( - builder: (context, state) { - return _EditorStack(state: state, journalId: journalId); - }, - ), - ), - - // 底部工具栏 - BlocBuilder( + // 编辑区域(三层 Stack) + Expanded( + child: BlocBuilder( builder: (context, state) { - return EditorToolbar( - state: state, - onEvent: (event) => context.read().add(event), - ); + return _EditorStack(state: state, journalId: journalId); }, ), - ], - ), + ), + + // 底部工具栏(自带底部安全区) + BlocBuilder( + builder: (context, state) { + return EditorToolbar( + state: state, + onEvent: (event) => context.read().add(event), + ); + }, + ), + ], ), ); } - /// 顶部操作栏 — 返回/日记标题/完成 - Widget _buildTopBar(BuildContext context) { + /// 顶部操作栏 — 日期/撤销重做/标签/心情/完成 + Widget _buildTopBar(BuildContext context, EditorState state) { final colorScheme = Theme.of(context).colorScheme; - return Container( - height: 52, - padding: const EdgeInsets.symmetric(horizontal: DesignTokens.spacing8), + padding: EdgeInsets.only(top: MediaQuery.of(context).padding.top), decoration: BoxDecoration( color: colorScheme.surface, border: Border( - bottom: BorderSide(color: colorScheme.outline.withValues(alpha: 0.1)), + bottom: BorderSide( + color: colorScheme.outlineVariant.withValues(alpha: 0.3), + ), ), ), - child: Row( + child: Column( children: [ - // 返回按钮 - IconButton( - onPressed: () { - if (context.canPop()) { - context.pop(); - } else { - context.go('/home'); - } - }, - icon: const Icon(Icons.arrow_back_rounded), - tooltip: '返回', - ), - - const SizedBox(width: DesignTokens.spacing8), - - // 日记标题 - Expanded( - child: Text( - journalId != null - ? '编辑日记' - : templateId != null - ? '从模板新建' - : '新建日记', - style: Theme.of(context).textTheme.titleMedium?.copyWith( - fontWeight: FontWeight.w600, + // 主顶栏行 (44px) + SizedBox( + height: 44, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 8), + child: Row( + children: [ + // 返回按钮 + IconButton( + icon: const Icon(Icons.arrow_back_rounded), + onPressed: () => _handleBack(context), + constraints: const BoxConstraints(minWidth: 36, minHeight: 36), + iconSize: 22, ), + // 日期显示 + Expanded( + child: Center( + child: Text( + _formatDate(state), + style: TextStyle( + fontFamily: 'Quicksand', + fontSize: 15, + fontWeight: FontWeight.w600, + color: colorScheme.onSurface, + ), + ), + ), + ), + // 撤销 + IconButton( + icon: const Icon(Icons.undo_rounded, size: 18), + onPressed: () => context.read().add(Undo()), + constraints: const BoxConstraints(minWidth: 36, minHeight: 36), + ), + // 重做 + IconButton( + icon: const Icon(Icons.redo_rounded, size: 18), + onPressed: () => context.read().add(Redo()), + constraints: const BoxConstraints(minWidth: 36, minHeight: 36), + ), + // 自动保存状态 + _buildAutosaveIndicator(state), + // 标签按钮 + IconButton( + icon: const Icon(Icons.sell_rounded, size: 18), + onPressed: () => _showTagPanel(context, state), + constraints: const BoxConstraints(minWidth: 36, minHeight: 36), + ), + // 完成/保存按钮 + Padding( + padding: const EdgeInsets.only(left: 4), + child: FilledButton.tonal( + onPressed: () => _handleSave(context, state), + style: FilledButton.styleFrom( + padding: const EdgeInsets.symmetric(horizontal: 16), + minimumSize: const Size(0, 32), + ), + child: const Text('完成', style: TextStyle(fontSize: 14)), + ), + ), + ], + ), ), ), + // 日期 + 心情条 (40px) + _buildDateMoodStrip(context, state), + ], + ), + ); + } - // 完成按钮 - FilledButton.tonal( - onPressed: onSaveComplete, - style: FilledButton.styleFrom( - padding: const EdgeInsets.symmetric(horizontal: DesignTokens.spacing16), - minimumSize: const Size(0, 36), - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), + /// 返回处理 + void _handleBack(BuildContext context) { + if (context.canPop()) { + context.pop(); + } else { + context.go('/home'); + } + } + + /// 保存处理 + void _handleSave(BuildContext context, EditorState state) { + onSaveComplete(); + } + + /// 格式化日期显示 + String _formatDate(EditorState state) { + final now = DateTime.now(); + const weekdays = ['周一', '周二', '周三', '周四', '周五', '周六', '周日']; + return '${now.month}月${now.day}日 · ${weekdays[now.weekday - 1]}'; + } + + /// 自动保存状态指示器 + Widget _buildAutosaveIndicator(EditorState state) { + if (state.lastSavedAt == null) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 4), + child: Text( + '未保存', + style: TextStyle(fontSize: 11, color: Colors.grey[400]), + ), + ); + } + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 4), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + width: 6, + height: 6, + decoration: const BoxDecoration( + color: AppColors.success, + shape: BoxShape.circle, ), - child: const Text('完成'), + ), + const SizedBox(width: 4), + Text( + '已保存', + style: TextStyle(fontSize: 11, color: Colors.grey[500]), ), ], ), ); } + + /// 日期时间 + 心情选择条 + Widget _buildDateMoodStrip(BuildContext context, EditorState state) { + final now = DateTime.now(); + final timeStr = + '${now.hour.toString().padLeft(2, '0')}:${now.minute.toString().padLeft(2, '0')}'; + final moods = [ + (Mood.happy, '😊'), + (Mood.calm, '😐'), + (Mood.sad, '😢'), + (Mood.angry, '😡'), + (Mood.thinking, '🤔'), + ]; + return Container( + height: 40, + padding: const EdgeInsets.symmetric(horizontal: 16), + color: Theme.of(context).colorScheme.surface, + child: Row( + children: [ + Text( + timeStr, + style: TextStyle(fontSize: 13, color: Colors.grey[500]), + ), + // 心情快捷按钮 + const Spacer(), + ...moods.map((m) { + final isSelected = state.selectedMood == m.$1; + return GestureDetector( + onTap: () => + context.read().add(MoodChanged(m.$1)), + child: Container( + width: 24, + height: 24, + margin: const EdgeInsets.symmetric(horizontal: 2), + decoration: BoxDecoration( + shape: BoxShape.circle, + border: isSelected + ? Border.all(color: AppColors.accent, width: 1.5) + : null, + color: isSelected ? const Color(0xFFFFF3E6) : null, + ), + alignment: Alignment.center, + child: Text(m.$2, style: const TextStyle(fontSize: 12)), + ), + ); + }), + ], + ), + ); + } + + /// 显示标签面板 + void _showTagPanel(BuildContext context, EditorState state) { + showModalBottomSheet( + context: context, + isScrollControlled: true, + backgroundColor: Colors.transparent, + builder: (ctx) => BlocProvider.value( + value: context.read(), + child: BlocBuilder( + builder: (ctx, state) => TagPanel( + selectedTags: state.tags, + onTagAdded: (tag) { + context.read().add(TagAdded(tag)); + }, + onTagRemoved: (tag) { + context.read().add(TagRemoved(tag)); + }, + ), + ), + ), + ); + } } // ============================================================ @@ -337,16 +543,46 @@ class _EditorStack extends StatefulWidget { class _EditorStackState extends State<_EditorStack> { EditorTool? _lastTool; + late final TextEditingController _titleController; + + @override + void initState() { + super.initState(); + _titleController = TextEditingController(text: widget.state.title); + } + + @override + void dispose() { + _titleController.dispose(); + super.dispose(); + } @override void didUpdateWidget(covariant _EditorStack oldWidget) { super.didUpdateWidget(oldWidget); final currentTool = widget.state.activeTool; - // 贴纸工具刚被激活时弹出底部面板(防止重复弹窗) - if (currentTool == EditorTool.sticker && _lastTool != EditorTool.sticker) { + // 防止重复弹窗:只在工具切换时触发 + if (currentTool != _lastTool) { WidgetsBinding.instance.addPostFrameCallback((_) { - if (mounted) _showStickerPicker(); + if (!mounted) return; + + switch (currentTool) { + // 贴纸工具 → 弹出贴纸选择面板 + case EditorTool.sticker: + _showStickerPicker(); + // 画笔工具 → 弹出画笔设置面板 + case EditorTool.brush: + _showBrushPanel(); + // 模板工具 → 导航到模板页 + case EditorTool.template: + context.go('/templates'); + // 更多工具 → 弹出分享/导出选项 + case EditorTool.more: + _showMoreSheet(); + default: + break; + } }); } _lastTool = currentTool; @@ -376,24 +612,162 @@ class _EditorStackState extends State<_EditorStack> { ); } + /// 显示画笔设置底部面板 + void _showBrushPanel() { + showModalBottomSheet( + context: context, + isScrollControlled: true, + backgroundColor: Colors.transparent, + builder: (_) => BlocProvider.value( + value: context.read(), + child: BlocBuilder( + builder: (ctx, state) => BrushPanel( + activeBrushType: state.brushType, + activeColor: state.brushColor, + activeWidth: state.brushWidth, + activeOpacity: state.brushOpacity, + onBrushTypeChanged: (type) => context.read().add( + BrushChanged( + type: type, + color: state.brushColor, + width: state.brushWidth, + ), + ), + onColorChanged: (color) => context.read().add( + BrushChanged( + type: state.brushType, + color: color, + width: state.brushWidth, + ), + ), + onWidthChanged: (width) => context.read().add( + BrushChanged( + type: state.brushType, + color: state.brushColor, + width: width, + ), + ), + onOpacityChanged: (opacity) { + // Phase 1 简化:opacity 仅在 marker 模式下生效 + // 暂无 opacity 事件,后续扩展 + }, + ), + ), + ), + ); + } + + /// 显示更多选项底部面板(分享/导出/清除) + void _showMoreSheet() { + final colorScheme = Theme.of(context).colorScheme; + showModalBottomSheet( + context: context, + backgroundColor: Colors.transparent, + builder: (_) => Container( + decoration: BoxDecoration( + color: colorScheme.surface, + borderRadius: const BorderRadius.vertical(top: Radius.circular(22)), + ), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + // 拖拽指示条 + Padding( + padding: const EdgeInsets.only(top: 12, bottom: 8), + child: Container( + width: 36, + height: 4, + decoration: BoxDecoration( + color: Colors.grey[300], + borderRadius: BorderRadius.circular(2), + ), + ), + ), + // 清除画布 + ListTile( + leading: const Icon(Icons.delete_outline_rounded), + title: const Text('清除画布'), + onTap: () { + Navigator.pop(context); + context.read().add(ClearCanvas()); + }, + ), + // 分享 + ListTile( + leading: const Icon(Icons.share_rounded), + title: const Text('分享日记'), + onTap: () { + Navigator.pop(context); + // 委托给外层的分享逻辑 + }, + ), + const SizedBox(height: 16), + ], + ), + ), + ); + } + @override Widget build(BuildContext context) { final state = widget.state; + final colorScheme = Theme.of(context).colorScheme; return Stack( fit: StackFit.expand, children: [ - // Layer 1: 手写画布(底层) + // Layer 0: 点阵背景(最底层) + CustomPaint( + painter: const DotGridPainter(), + size: Size.infinite, + ), + + // Layer 1: 手写画布 + 内嵌标题 IgnorePointer( ignoring: !state.isDrawingMode, - child: HandwritingCanvas( - brushType: state.brushType, - brushColor: state.brushColor, - brushWidth: state.brushWidth, - strokes: state.strokes, - onStrokeCompleted: (stroke) { - context.read().add(StrokeCompleted(stroke)); - }, + child: Column( + children: [ + // 内嵌标题输入框 + Padding( + padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 8), + child: TextField( + controller: _titleController, + style: TextStyle( + fontFamily: 'Quicksand', + fontSize: 18, + fontWeight: FontWeight.w600, + color: colorScheme.onSurface, + ), + decoration: InputDecoration( + hintText: '给日记起个标题吧...', + hintStyle: TextStyle( + fontFamily: 'Quicksand', + fontSize: 18, + fontWeight: FontWeight.w600, + color: colorScheme.onSurface.withValues(alpha: 0.25), + ), + border: InputBorder.none, + contentPadding: EdgeInsets.zero, + isDense: true, + ), + onChanged: (value) { + context.read().add(TitleChanged(value)); + }, + ), + ), + // 画布区域 + Expanded( + child: HandwritingCanvas( + brushType: state.brushType, + brushColor: state.brushColor, + brushWidth: state.brushWidth, + strokes: state.strokes, + onStrokeCompleted: (stroke) { + context.read().add(StrokeCompleted(stroke)); + }, + ), + ), + ], ), ), @@ -426,7 +800,7 @@ class _EditorStackState extends State<_EditorStack> { ), // 图片选择覆盖层(图片工具激活时显示) - if (state.activeTool == EditorTool.image) + if (state.activeTool == EditorTool.photo) Center( child: Row( mainAxisAlignment: MainAxisAlignment.center, diff --git a/app/lib/features/editor/widgets/brush_panel.dart b/app/lib/features/editor/widgets/brush_panel.dart new file mode 100644 index 0000000..e2f94f6 --- /dev/null +++ b/app/lib/features/editor/widgets/brush_panel.dart @@ -0,0 +1,215 @@ +// 画笔面板 -- 底部抽屉 +// 提供画笔类型/粗细/颜色/透明度设置 +// 遵循 StickerPickerSheet 底部面板模式 + +import 'package:flutter/material.dart'; + +import '../../../core/theme/app_colors.dart'; +import '../bloc/editor_bloc.dart'; +import 'stroke_model.dart'; + +/// 画笔面板 -- 底部抽屉 +class BrushPanel extends StatelessWidget { + final BrushType activeBrushType; + final String activeColor; + final double activeWidth; + final double activeOpacity; + final void Function(BrushType type) onBrushTypeChanged; + final void Function(String color) onColorChanged; + final void Function(double width) onWidthChanged; + final void Function(double opacity) onOpacityChanged; + + const BrushPanel({ + super.key, + required this.activeBrushType, + required this.activeColor, + required this.activeWidth, + required this.activeOpacity, + required this.onBrushTypeChanged, + required this.onColorChanged, + required this.onWidthChanged, + required this.onOpacityChanged, + }); + + static const _brushTypes = [ + (BrushType.pen, '钢笔', Icons.gesture_rounded), + (BrushType.pencil, '铅笔', Icons.edit_rounded), + (BrushType.marker, '马克笔', Icons.brush_rounded), + (BrushType.eraser, '橡皮', Icons.auto_fix_high_rounded), + ]; + + static const _colors = [ + '#2D2420', '#E07A5F', '#81B29A', '#F2CC8F', + '#D4A5A5', '#42A5F5', '#9C27B0', '#FFFFFF', + ]; + + @override + Widget build(BuildContext context) { + return Container( + height: 280, + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.surface, + borderRadius: const BorderRadius.vertical(top: Radius.circular(22)), + ), + child: Column( + children: [ + // 拖拽指示条 + Padding( + padding: const EdgeInsets.only(top: 12, bottom: 8), + child: Container( + width: 36, + height: 4, + decoration: BoxDecoration( + color: Colors.grey[300], + borderRadius: BorderRadius.circular(2), + ), + ), + ), + // 画笔类型行 + _buildBrushTypeRow(context), + // 粗细滑块 + _buildSizeSlider(context), + // 颜色行 + _buildColorRow(context), + // 透明度滑块(仅马克笔) + if (activeBrushType == BrushType.marker) + _buildOpacitySlider(context), + ], + ), + ); + } + + Widget _buildBrushTypeRow(BuildContext context) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 8), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: _brushTypes.map((bt) { + final isActive = activeBrushType == bt.$1; + return GestureDetector( + onTap: () => onBrushTypeChanged(bt.$1), + child: Container( + width: 64, + height: 52, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(12), + border: isActive + ? Border.all(color: AppColors.accent, width: 2) + : Border.all(color: Colors.transparent), + color: isActive + ? AppColors.accent.withValues(alpha: 0.1) + : null, + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon( + bt.$3, + size: 20, + color: isActive ? AppColors.accent : Colors.grey[600], + ), + const SizedBox(height: 2), + Text( + bt.$2, + style: TextStyle( + fontSize: 10, + color: isActive ? AppColors.accent : Colors.grey[600], + fontWeight: + isActive ? FontWeight.w600 : FontWeight.normal, + ), + ), + ], + ), + ), + ); + }).toList(), + ), + ); + } + + Widget _buildSizeSlider(BuildContext context) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 4), + child: Row( + children: [ + Text('粗细', + style: TextStyle(fontSize: 12, color: Colors.grey[600])), + Expanded( + child: Slider( + value: activeWidth, + min: 1, + max: 20, + divisions: 19, + activeColor: AppColors.accent, + label: activeWidth.round().toString(), + onChanged: onWidthChanged, + ), + ), + Text( + activeWidth.round().toString(), + style: TextStyle(fontSize: 12, color: Colors.grey[600]), + ), + ], + ), + ); + } + + Widget _buildColorRow(BuildContext context) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 8), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: _colors.map((c) { + final isActive = activeColor == c; + final color = _parseHexColor(c); + return GestureDetector( + onTap: () => onColorChanged(c), + child: Container( + width: 24, + height: 24, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: color, + border: isActive + ? Border.all(color: AppColors.accent, width: 2) + : (c == '#FFFFFF' + ? Border.all(color: Colors.grey[300]!) + : null), + ), + ), + ); + }).toList(), + ), + ); + } + + Widget _buildOpacitySlider(BuildContext context) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 4), + child: Row( + children: [ + Text('透明度', + style: TextStyle(fontSize: 12, color: Colors.grey[600])), + Expanded( + child: Slider( + value: activeOpacity, + min: 0.1, + max: 1.0, + activeColor: AppColors.accent, + onChanged: onOpacityChanged, + ), + ), + Text( + '${(activeOpacity * 100).round()}%', + style: TextStyle(fontSize: 12, color: Colors.grey[600]), + ), + ], + ), + ); + } + + Color _parseHexColor(String hex) { + final code = hex.replaceFirst('#', ''); + return Color(int.parse('FF$code', radix: 16)); + } +} diff --git a/app/lib/features/editor/widgets/dot_grid_painter.dart b/app/lib/features/editor/widgets/dot_grid_painter.dart new file mode 100644 index 0000000..8defddf --- /dev/null +++ b/app/lib/features/editor/widgets/dot_grid_painter.dart @@ -0,0 +1,28 @@ +// 点阵背景画笔 — 24x24px 间距,1px 圆点 +// 用于日记编辑区域提供纸质感背景 + +import 'package:flutter/material.dart'; + +/// 点阵背景画笔 -- 24x24px 间距,1px 圆点 +class DotGridPainter extends CustomPainter { + const DotGridPainter(); + + @override + void paint(Canvas canvas, Size size) { + final paint = Paint() + ..color = const Color(0xFF2D2420).withOpacity(0.15) + ..style = PaintingStyle.fill; + + const spacing = 24.0; + const dotRadius = 1.0; + + for (double x = spacing; x < size.width; x += spacing) { + for (double y = spacing; y < size.height; y += spacing) { + canvas.drawCircle(Offset(x, y), dotRadius, paint); + } + } + } + + @override + bool shouldRepaint(covariant DotGridPainter oldDelegate) => false; +} diff --git a/app/lib/features/editor/widgets/editor_toolbar.dart b/app/lib/features/editor/widgets/editor_toolbar.dart index aeb7c1a..32c514e 100644 --- a/app/lib/features/editor/widgets/editor_toolbar.dart +++ b/app/lib/features/editor/widgets/editor_toolbar.dart @@ -1,22 +1,22 @@ -// 编辑器工具栏 — 底部工具面板 +// 编辑器工具栏 — 底部单行 6 按钮面板 // -// 三段式布局: -// - 工具选择行(画笔/选择/文字/贴纸/照片) -// - 工具选项行(颜色/大小 — 根据当前工具动态变化) -// - 操作行(撤销/重做/清除) +// 精简布局: +// - 单行 6 个工具按钮(贴纸/模板/画笔/照片/文字/更多) +// - 高度 72px + 底部安全区 +// - 每个按钮:Column(icon 20px + label 10px),最小 36x36 +// +// 详细选项已移至独立面板: +// - 画笔选项 → BrushPanel(底部抽屉) +// - 撤销/重做 → 顶栏 +// - 清除 → 顶栏 // // 设计规范:触摸目标 ≥ 44px,圆角 22px (pill) import 'package:flutter/material.dart'; -import '../../../core/constants/design_tokens.dart'; -import '../../../core/theme/app_colors.dart'; import '../bloc/editor_bloc.dart'; -/// 工具栏高度 -const double _toolbarHeight = 160; - -/// 编辑器工具栏 +/// 编辑器工具栏 — 精简版 class EditorToolbar extends StatelessWidget { final EditorState state; final ValueChanged onEvent; @@ -30,61 +30,35 @@ class EditorToolbar extends StatelessWidget { @override Widget build(BuildContext context) { final colorScheme = Theme.of(context).colorScheme; - return Container( - height: _toolbarHeight, + height: 72 + MediaQuery.of(context).padding.bottom, decoration: BoxDecoration( color: colorScheme.surface, - boxShadow: [ - BoxShadow( - color: Colors.black.withValues(alpha: 0.08), - blurRadius: 8, - offset: const Offset(0, -2), + border: Border( + top: BorderSide( + color: colorScheme.outlineVariant.withValues(alpha: 0.3), ), - ], - borderRadius: const BorderRadius.vertical(top: Radius.circular(22)), + ), ), - child: Column( - children: [ - // 工具选择行 - _buildToolRow(context, colorScheme), - const Divider(height: 1), - - // 工具选项行(颜色/大小) - _buildOptionsRow(context, colorScheme), - const Divider(height: 1), - - // 操作行(撤销/重做/清除) - _buildActionRow(context, colorScheme), - ], + child: Padding( + padding: EdgeInsets.only(bottom: MediaQuery.of(context).padding.bottom), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + _toolBtn(context, EditorTool.sticker, Icons.emoji_emotions_rounded, '贴纸'), + _toolBtn(context, EditorTool.template, Icons.dashboard_customize_rounded, '模板'), + _toolBtn(context, EditorTool.brush, Icons.gesture_rounded, '画笔'), + _toolBtn(context, EditorTool.photo, Icons.add_photo_alternate_rounded, '照片'), + _toolBtn(context, EditorTool.text, Icons.text_fields_rounded, '文字'), + _toolBtn(context, EditorTool.more, Icons.more_horiz_rounded, '更多'), + ], + ), ), ); } - // ============================================================ - // 工具选择行 - // ============================================================ - - Widget _buildToolRow(BuildContext context, ColorScheme colorScheme) { - return SizedBox( - height: 52, - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - _toolButton(context, EditorTool.pen, Icons.gesture_rounded, '钢笔'), - _toolButton(context, EditorTool.pencil, Icons.edit_rounded, '铅笔'), - _toolButton(context, EditorTool.marker, Icons.brush_rounded, '马克笔'), - _toolButton(context, EditorTool.eraser, Icons.auto_fix_high_rounded, '橡皮'), - _toolButton(context, EditorTool.select, Icons.near_me_rounded, '选择'), - _toolButton(context, EditorTool.text, Icons.text_fields_rounded, '文字'), - _toolButton(context, EditorTool.sticker, Icons.emoji_emotions_rounded, '贴纸'), - _toolButton(context, EditorTool.image, Icons.add_photo_alternate_rounded, '照片'), - ], - ), - ); - } - - Widget _toolButton( + /// 工具按钮 — icon + label 垂直排列 + Widget _toolBtn( BuildContext context, EditorTool tool, IconData icon, @@ -93,265 +67,37 @@ class EditorToolbar extends StatelessWidget { final isActive = state.activeTool == tool; final colorScheme = Theme.of(context).colorScheme; - return SizedBox( - width: 44, - height: 44, - child: IconButton( - onPressed: () => onEvent(ToolChanged(tool)), - icon: Icon(icon, size: 22), - color: isActive ? colorScheme.primary : colorScheme.onSurface.withValues(alpha: 0.5), - style: IconButton.styleFrom( - backgroundColor: isActive - ? colorScheme.primaryContainer.withValues(alpha: 0.3) - : Colors.transparent, - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), - ), - tooltip: label, - ), - ); - } - - // ============================================================ - // 工具选项行(颜色 + 大小) - // ============================================================ - - static const _colors = [ - '#2D2420', // 主文字 - '#E07A5F', // 珊瑚 - '#81B29A', // 鼠尾草绿 - '#F2CC8F', // 暖金 - '#D4A5A5', // 玫瑰粉 - '#42A5F5', // 信息蓝 - '#9C27B0', // 紫色 - '#FFFFFF', // 白色 - ]; - - static const _widths = [1.5, 3.0, 5.0, 8.0, 12.0]; - - Widget _buildOptionsRow(BuildContext context, ColorScheme colorScheme) { - // 画笔模式:颜色 + 粗细 - if (state.isDrawingMode) { - return _buildBrushOptions(context, colorScheme); - } - - // 文字工具提示 - if (state.activeTool == EditorTool.text) { - return const SizedBox( - height: 44, - child: Center( - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Icon(Icons.text_fields, size: 16), - SizedBox(width: 8), - Text('点击画布输入文字', style: TextStyle(fontSize: 13)), - ], - ), - ), - ); - } - - // 贴纸工具提示 - if (state.activeTool == EditorTool.sticker) { - return const SizedBox( - height: 44, - child: Center( - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Icon(Icons.emoji_emotions_outlined, size: 16), - SizedBox(width: 8), - Text('选择一个贴纸放到日记上', style: TextStyle(fontSize: 13)), - ], - ), - ), - ); - } - - // 图片工具提示 - if (state.activeTool == EditorTool.image) { - return const SizedBox( - height: 44, - child: Center( - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Icon(Icons.add_photo_alternate_outlined, size: 16), - SizedBox(width: 8), - Text('选择照片添加到日记', style: TextStyle(fontSize: 13)), - ], - ), - ), - ); - } - - // 选择工具 - return const SizedBox( - height: 44, - child: Center(child: Text('选择元素或添加内容')), - ); - } - - /// 画笔模式选项 — 颜色 + 粗细 - Widget _buildBrushOptions(BuildContext context, ColorScheme colorScheme) { - - return SizedBox( - height: 44, - child: Row( - children: [ - // 颜色选择 - Expanded( - child: ListView.separated( - scrollDirection: Axis.horizontal, - padding: const EdgeInsets.symmetric(horizontal: DesignTokens.spacing12), - itemCount: _colors.length, - separatorBuilder: (_, __) => const SizedBox(width: 6), - itemBuilder: (context, index) { - final color = _colors[index]; - final isActive = state.brushColor == color; - return GestureDetector( - onTap: () => onEvent(BrushChanged( - type: state.brushType, - color: color, - width: state.brushWidth, - )), - child: Container( - width: 28, - height: 28, - decoration: BoxDecoration( - color: _parseHexColor(color), - shape: BoxShape.circle, - border: isActive - ? Border.all(color: colorScheme.primary, width: 2.5) - : Border.all(color: Colors.grey.shade300, width: 1), - ), - child: color == '#FFFFFF' - ? const Icon(Icons.check, size: 16, color: Colors.grey) - : null, - ), - ); - }, + return GestureDetector( + onTap: () => onEvent(ToolChanged(tool)), + behavior: HitTestBehavior.opaque, + child: Container( + constraints: const BoxConstraints(minWidth: 36, minHeight: 36), + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), + child: Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon( + icon, + size: 20, + color: isActive + ? colorScheme.primary + : colorScheme.onSurface.withValues(alpha: 0.5), ), - ), - - // 分隔线 - Container(width: 1, height: 24, color: colorScheme.outline.withValues(alpha: 0.2)), - - // 笔刷大小 - SizedBox( - width: 160, - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: _widths.map((w) { - final isActive = (state.brushWidth - w).abs() < 0.5; - return GestureDetector( - onTap: () => onEvent(BrushChanged( - type: state.brushType, - color: state.brushColor, - width: w, - )), - child: Container( - width: 28, - height: 28, - alignment: Alignment.center, - decoration: BoxDecoration( - shape: BoxShape.circle, - border: isActive - ? Border.all(color: colorScheme.primary, width: 2) - : null, - ), - child: Container( - width: (w / 12 * 16 + 4).clamp(4, 20), - height: (w / 12 * 16 + 4).clamp(4, 20), - decoration: BoxDecoration( - color: _parseHexColor(state.brushColor), - shape: BoxShape.circle, - ), - ), - ), - ); - }).toList(), - ), - ), - ], - ), - ); - } - - // ============================================================ - // 操作行 - // ============================================================ - - Widget _buildActionRow(BuildContext context, ColorScheme colorScheme) { - return SizedBox( - height: 44, - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - // 撤销 - IconButton( - onPressed: state.strokes.isNotEmpty - ? () => onEvent(Undo()) - : null, - icon: const Icon(Icons.undo_rounded), - tooltip: '撤销', - ), - - // 重做 - IconButton( - onPressed: state.redoStack.isNotEmpty - ? () => onEvent(Redo()) - : null, - icon: const Icon(Icons.redo_rounded), - tooltip: '重做', - ), - - // 清除 - IconButton( - onPressed: state.strokes.isNotEmpty || state.elements.isNotEmpty - ? () => onEvent(ClearCanvas()) - : null, - icon: const Icon(Icons.delete_outline_rounded), - tooltip: '清除', - ), - - // 保存状态指示 - if (state.isDirty) - Padding( - padding: const EdgeInsets.symmetric(horizontal: DesignTokens.spacing8), - child: Text( - '未保存', - style: TextStyle( - fontSize: 12, - color: colorScheme.onSurface.withValues(alpha: 0.5), - ), - ), - ) - else if (state.lastSavedAt != null) - Padding( - padding: const EdgeInsets.symmetric(horizontal: DesignTokens.spacing8), - child: Text( - '已保存', - style: TextStyle( - fontSize: 12, - color: AppColors.success, - ), + const SizedBox(height: 2), + Text( + label, + style: TextStyle( + fontSize: 10, + color: isActive + ? colorScheme.primary + : colorScheme.onSurface.withValues(alpha: 0.5), + fontWeight: isActive ? FontWeight.w600 : FontWeight.normal, ), ), - ], + ], + ), ), ); } - - // ============================================================ - // 工具函数 - // ============================================================ - - Color _parseHexColor(String hex) { - final hexStr = hex.replaceFirst('#', ''); - if (hexStr.length != 6) return const Color(0xFF2D2420); - final value = int.tryParse(hexStr, radix: 16); - if (value == null) return const Color(0xFF2D2420); - return Color(0xFF000000 + value); - } } diff --git a/app/lib/features/editor/widgets/tag_panel.dart b/app/lib/features/editor/widgets/tag_panel.dart new file mode 100644 index 0000000..07105c3 --- /dev/null +++ b/app/lib/features/editor/widgets/tag_panel.dart @@ -0,0 +1,157 @@ +// 标签面板 -- 底部抽屉 +// 支持添加/移除自定义标签 + 推荐标签快捷选择 + +import 'package:flutter/material.dart'; + +import '../../../core/theme/app_colors.dart'; + +/// 标签面板 -- 底部抽屉 +class TagPanel extends StatefulWidget { + final List selectedTags; + final void Function(String tag) onTagAdded; + final void Function(String tag) onTagRemoved; + + const TagPanel({ + super.key, + required this.selectedTags, + required this.onTagAdded, + required this.onTagRemoved, + }); + + @override + State createState() => _TagPanelState(); +} + +class _TagPanelState extends State { + final _controller = TextEditingController(); + final _focusNode = FocusNode(); + + static const _suggestedTags = [ + '日常', '学习', '读书', '心情', '学校', '旅行', + '美食', '运动', '音乐', '梦想', + ]; + + @override + void initState() { + super.initState(); + _focusNode.requestFocus(); + } + + @override + void dispose() { + _controller.dispose(); + _focusNode.dispose(); + super.dispose(); + } + + void _submitTag() { + final text = _controller.text.trim(); + if (text.isNotEmpty) { + widget.onTagAdded(text); + _controller.clear(); + } + } + + @override + Widget build(BuildContext context) { + return Container( + constraints: const BoxConstraints(maxHeight: 240), + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.surface, + borderRadius: const BorderRadius.vertical(top: Radius.circular(22)), + ), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + // 拖拽指示条 + Padding( + padding: const EdgeInsets.only(top: 12, bottom: 8), + child: Container( + width: 36, + height: 4, + decoration: BoxDecoration( + color: Colors.grey[300], + borderRadius: BorderRadius.circular(2), + ), + ), + ), + // 已选标签区 + if (widget.selectedTags.isNotEmpty) + Padding( + padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 4), + child: Wrap( + spacing: 8, + runSpacing: 6, + children: widget.selectedTags + .map((tag) => Chip( + label: Text('#$tag', + style: const TextStyle(fontSize: 13)), + backgroundColor: const Color(0xFFFFF3E6), + labelStyle: const TextStyle(color: AppColors.accent), + deleteIconColor: AppColors.accent, + onDeleted: () => widget.onTagRemoved(tag), + materialTapTargetSize: + MaterialTapTargetSize.shrinkWrap, + visualDensity: VisualDensity.compact, + )) + .toList(), + ), + ), + // 输入框 + Padding( + padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 8), + child: TextField( + controller: _controller, + focusNode: _focusNode, + decoration: InputDecoration( + hintText: '添加标签,回车确认', + hintStyle: TextStyle(fontSize: 14, color: Colors.grey[400]), + prefixIcon: + const Icon(Icons.tag, size: 20, color: AppColors.accent), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide(color: Colors.grey[300]!), + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide(color: Colors.grey[300]!), + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: const BorderSide(color: AppColors.accent), + ), + contentPadding: + const EdgeInsets.symmetric(horizontal: 12), + isDense: true, + ), + style: const TextStyle(fontSize: 14), + textInputAction: TextInputAction.done, + onSubmitted: (_) => _submitTag(), + ), + ), + // 推荐标签 + Padding( + padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 4), + child: Wrap( + spacing: 8, + runSpacing: 6, + children: _suggestedTags + .where((t) => !widget.selectedTags.contains(t)) + .map((tag) => ActionChip( + label: + Text('#$tag', style: const TextStyle(fontSize: 12)), + backgroundColor: Colors.grey[100], + onPressed: () => widget.onTagAdded(tag), + materialTapTargetSize: + MaterialTapTargetSize.shrinkWrap, + visualDensity: VisualDensity.compact, + )) + .toList(), + ), + ), + const SizedBox(height: 16), + ], + ), + ); + } +} diff --git a/app/lib/features/editor/widgets/text_format_bar.dart b/app/lib/features/editor/widgets/text_format_bar.dart new file mode 100644 index 0000000..88ebd58 --- /dev/null +++ b/app/lib/features/editor/widgets/text_format_bar.dart @@ -0,0 +1,143 @@ +// 文字格式栏 -- 浮动在选中文字元素上方 +// 提供加粗/斜体/下划线/颜色/对齐 切换 + +import 'package:flutter/material.dart'; + +import '../../../core/theme/app_colors.dart'; + +/// 文字格式栏 -- 浮动在选中文字元素上方 +class TextFormatBar extends StatelessWidget { + final bool bold; + final bool italic; + final bool underline; + final String? color; + final int alignment; // 0=left, 1=center, 2=right + final void Function({ + bool? bold, + bool? italic, + bool? underline, + String? color, + int? alignment, + }) onFormatChanged; + + const TextFormatBar({ + super.key, + this.bold = false, + this.italic = false, + this.underline = false, + this.color, + this.alignment = 0, + required this.onFormatChanged, + }); + + static const _colors = ['#2D2420', '#E07A5F', '#81B29A', '#42A5F5']; + + @override + Widget build(BuildContext context) { + return Container( + height: 40, + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.surface, + borderRadius: BorderRadius.circular(12), + boxShadow: [ + BoxShadow( + color: Colors.black.withValues(alpha: 0.1), + blurRadius: 8, + offset: const Offset(0, 2), + ), + ], + border: Border.all(color: Colors.grey[200]!), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + // B/I/U toggles + _toggleBtn('B', bold, () => onFormatChanged(bold: !bold)), + _toggleBtn('I', italic, () => onFormatChanged(italic: !italic)), + _toggleBtn( + 'U', underline, () => onFormatChanged(underline: !underline)), + const SizedBox(width: 4), + Container(width: 1, height: 20, color: Colors.grey[300]), + const SizedBox(width: 4), + // Color dots + ..._colors.map((c) => _colorDot(c, color == c)), + const SizedBox(width: 4), + Container(width: 1, height: 20, color: Colors.grey[300]), + const SizedBox(width: 4), + // Alignment + _alignBtn(Icons.format_align_left, 0), + _alignBtn(Icons.format_align_center, 1), + _alignBtn(Icons.format_align_right, 2), + const SizedBox(width: 4), + ], + ), + ); + } + + Widget _toggleBtn(String label, bool active, VoidCallback onTap) { + return GestureDetector( + onTap: onTap, + child: Container( + width: 36, + height: 36, + decoration: BoxDecoration( + color: active ? AppColors.accent : Colors.transparent, + borderRadius: BorderRadius.circular(8), + ), + alignment: Alignment.center, + child: Text( + label, + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w700, + color: active ? Colors.white : Colors.grey[700], + fontStyle: label == 'I' ? FontStyle.italic : FontStyle.normal, + decoration: + label == 'U' ? TextDecoration.underline : TextDecoration.none, + ), + ), + ), + ); + } + + Widget _colorDot(String hex, bool active) { + final code = hex.replaceFirst('#', ''); + final color = Color(int.parse('FF$code', radix: 16)); + return GestureDetector( + onTap: () => onFormatChanged(color: hex), + child: Container( + width: 20, + height: 20, + margin: const EdgeInsets.symmetric(horizontal: 2), + decoration: BoxDecoration( + shape: BoxShape.circle, + color: color, + border: + active ? Border.all(color: AppColors.accent, width: 2) : null, + ), + ), + ); + } + + Widget _alignBtn(IconData icon, int align) { + final active = alignment == align; + return GestureDetector( + onTap: () => onFormatChanged(alignment: align), + child: Container( + width: 36, + height: 36, + decoration: BoxDecoration( + color: + active ? AppColors.accent.withValues(alpha: 0.1) : Colors.transparent, + borderRadius: BorderRadius.circular(8), + ), + alignment: Alignment.center, + child: Icon( + icon, + size: 18, + color: active ? AppColors.accent : Colors.grey[600], + ), + ), + ); + } +} diff --git a/app/lib/features/home/bloc/home_bloc.dart b/app/lib/features/home/bloc/home_bloc.dart index 3713f02..e6ae4ea 100644 --- a/app/lib/features/home/bloc/home_bloc.dart +++ b/app/lib/features/home/bloc/home_bloc.dart @@ -54,6 +54,9 @@ final class HomeLoaded extends HomeState { /// 总日记数(spec §3.4 quick-stats) final int totalCount; + /// 今日天气(从今日日记中提取,null 则默认晴) + final Weather? todayWeather; + const HomeLoaded({ this.recentJournals = const [], this.hasTodayEntry = false, @@ -61,6 +64,7 @@ final class HomeLoaded extends HomeState { this.streakDays = 0, this.monthCount = 0, this.totalCount = 0, + this.todayWeather, }); } @@ -111,10 +115,21 @@ class HomeBloc extends Bloc { // 推算连续天数 final streakDays = _calculateStreak(journals); - // 本月日记数 & 总数(spec §3.4 quick-stats) + // 本月日记数(spec §3.4 quick-stats) final monthCount = journals.where((j) => j.date.year == today.year && j.date.month == today.month).length; - final totalCount = journals.length; + + // 总日记数 — 使用仓库计数方法(不受分页限制) + final totalCount = await _journalRepo.getJournalCount(); + + // 今日天气 — 从今日日记中提取 + final todayJournal = journals.firstWhere( + (j) => j.date.year == today.year && + j.date.month == today.month && + j.date.day == today.day, + orElse: () => journals.first, // fallback for type safety + ); + final todayWeather = hasTodayEntry ? todayJournal.weather : null; emit(HomeLoaded( recentJournals: journals, @@ -123,6 +138,7 @@ class HomeBloc extends Bloc { streakDays: streakDays, monthCount: monthCount, totalCount: totalCount, + todayWeather: todayWeather, )); } catch (e) { emit(const HomeLoaded()); // 空状态而非错误,离线友好 diff --git a/app/lib/features/home/views/home_page.dart b/app/lib/features/home/views/home_page.dart index 0aca342..ddfa62d 100644 --- a/app/lib/features/home/views/home_page.dart +++ b/app/lib/features/home/views/home_page.dart @@ -100,7 +100,7 @@ class _HomeView extends StatelessWidget { _MoodSelectorCard( topMood: state.topMood, - weather: const _Weather(icon: '☀', label: '晴 26°'), + todayWeather: state.todayWeather, onMoodTap: (_) => context.push('/editor'), ), const SizedBox(height: DesignTokens.spacing20), @@ -261,12 +261,12 @@ class _StreakBadge extends StatelessWidget { class _MoodSelectorCard extends StatelessWidget { const _MoodSelectorCard({ required this.topMood, - required this.weather, + required this.todayWeather, required this.onMoodTap, }); final Mood? topMood; - final _Weather weather; + final Weather? todayWeather; final ValueChanged onMoodTap; static const _moods = [ @@ -277,6 +277,14 @@ class _MoodSelectorCard extends StatelessWidget { ('🤔', '思考', Mood.thinking), ]; + static const _weatherMap = { + Weather.sunny: ('☀', '晴'), + Weather.cloudy: ('☁', '多云'), + Weather.rainy: ('🌧', '雨'), + Weather.snowy: ('❄', '雪'), + Weather.windy: ('💨', '风'), + }; + @override Widget build(BuildContext context) { final theme = Theme.of(context); @@ -306,10 +314,13 @@ class _MoodSelectorCard extends StatelessWidget { ), ), const Spacer(), - Text( - '${weather.icon} ${weather.label}', - style: TextStyle(fontSize: 13, color: theme.colorScheme.onSurfaceVariant), - ), + Builder(builder: (context) { + final w = _weatherMap[todayWeather] ?? _weatherMap[Weather.sunny]!; + return Text( + '${w.$1} ${w.$2}', + style: TextStyle(fontSize: 13, color: theme.colorScheme.onSurfaceVariant), + ); + }), ], ), const SizedBox(height: DesignTokens.spacing12), @@ -376,12 +387,6 @@ class _MoodOption extends StatelessWidget { } } -class _Weather { - const _Weather({required this.icon, required this.label}); - final String icon; - final String label; -} - /// 4. "今天的日记" 渐变卡片 + 浮动写按钮 class _TodayCard extends StatelessWidget { const _TodayCard({required this.hasTodayEntry, required this.onTap}); @@ -621,7 +626,9 @@ class _JournalCard extends StatelessWidget { Widget build(BuildContext context) { final theme = Theme.of(context); final moodColor = AppColors.moodColors[journal.mood.value] ?? AppColors.accent; - final excerpt = journal.tags.isEmpty ? '点击查看详情' : journal.tags.take(3).map((t) => '#$t').join(' '); + final excerpt = journal.contentExcerpt?.isNotEmpty == true + ? journal.contentExcerpt! + : (journal.tags.isEmpty ? '点击查看详情' : journal.tags.take(3).map((t) => '#$t').join(' ')); return Material( color: theme.colorScheme.surface, diff --git a/app/lib/features/parent/views/parent_page.dart b/app/lib/features/parent/views/parent_page.dart index 285939c..ce09df5 100644 --- a/app/lib/features/parent/views/parent_page.dart +++ b/app/lib/features/parent/views/parent_page.dart @@ -922,7 +922,8 @@ class _JournalCard extends StatelessWidget { try { final dt = DateTime.parse(isoStr); return DateFormat('MM-dd').format(dt); - } catch (_) { + } catch (e) { + debugPrint('ParentPage._formatDate 失败: $e'); return ''; } } diff --git a/app/lib/features/profile/views/profile_page.dart b/app/lib/features/profile/views/profile_page.dart index e68aa76..8aaf62d 100644 --- a/app/lib/features/profile/views/profile_page.dart +++ b/app/lib/features/profile/views/profile_page.dart @@ -408,7 +408,8 @@ class _LiveStatsBarState extends State<_LiveStatsBar> { _streakDays = streak; _monthCount = monthCount; }); - } catch (_) { + } catch (e) { + debugPrint('ProfilePage._loadStats 失败: $e'); // 保持默认 0 值 } } diff --git a/app/lib/features/search/bloc/search_bloc.dart b/app/lib/features/search/bloc/search_bloc.dart index b678380..d9e806e 100644 --- a/app/lib/features/search/bloc/search_bloc.dart +++ b/app/lib/features/search/bloc/search_bloc.dart @@ -1,7 +1,7 @@ -// 搜索 BLoC — 标签+心情筛选日记 +// 搜索 BLoC — 关键词+标签+心情筛选日记 // // 状态机: SearchInitial → SearchLoading → SearchLoaded/SearchError -// Phase 1 使用简单的标签+心情筛选,后续可扩展全文搜索。 +// 支持关键词搜索、标签筛选、心情筛选、结果分类 tab。 import 'package:flutter_bloc/flutter_bloc.dart'; @@ -11,16 +11,21 @@ import '../../../data/repositories/journal_repository.dart'; part 'search_event.dart'; part 'search_state.dart'; -/// 搜索 BLoC — 处理标签和心情筛选日记的状态转换 +/// 搜索 BLoC class SearchBloc extends Bloc { final JournalRepository _journalRepo; + /// 内存搜索历史(最多 10 条) + final List _searchHistory = []; + SearchBloc({required JournalRepository journalRepository}) : _journalRepo = journalRepository, super(const SearchInitial()) { on(_onSearchByMood); on(_onSearchByTag); + on(_onSearchByKeyword); on(_onSearchClear); + on(_onSearchTabChanged); } /// 按心情筛选日记 @@ -31,7 +36,7 @@ class SearchBloc extends Bloc { emit(const SearchLoading()); try { if (event.mood == null) { - emit(const SearchLoaded()); + emit(SearchLoaded(searchHistory: List.unmodifiable(_searchHistory))); return; } final results = await _journalRepo.getJournals( @@ -42,6 +47,7 @@ class SearchBloc extends Bloc { emit(SearchLoaded( results: results, activeMood: event.mood!.value, + searchHistory: List.unmodifiable(_searchHistory), )); } catch (e) { emit(const SearchError('搜索失败,请重试')); @@ -55,6 +61,7 @@ class SearchBloc extends Bloc { ) async { emit(const SearchLoading()); try { + _addToHistory(event.tag); final results = await _journalRepo.getJournals( tag: event.tag, page: 1, @@ -63,6 +70,47 @@ class SearchBloc extends Bloc { emit(SearchLoaded( results: results, activeTag: event.tag, + searchHistory: List.unmodifiable(_searchHistory), + )); + } catch (e) { + emit(const SearchError('搜索失败,请重试')); + } + } + + /// 关键词搜索 — 在标题中匹配关键词 + Future _onSearchByKeyword( + SearchByKeyword event, + Emitter emit, + ) async { + final keyword = event.keyword.trim(); + if (keyword.isEmpty) { + emit(SearchLoaded(searchHistory: List.unmodifiable(_searchHistory))); + return; + } + + emit(const SearchLoading()); + try { + _addToHistory(keyword); + // 获取所有日记并在客户端按关键词过滤 + final allJournals = await _journalRepo.getJournals( + page: 1, + pageSize: 200, + ); + final lowerKeyword = keyword.toLowerCase(); + final results = allJournals.where((j) { + final titleMatch = j.title.toLowerCase().contains(lowerKeyword); + final excerptMatch = (j.contentExcerpt ?? '') + .toLowerCase() + .contains(lowerKeyword); + final tagMatch = + j.tags.any((t) => t.toLowerCase().contains(lowerKeyword)); + return titleMatch || excerptMatch || tagMatch; + }).toList(); + + emit(SearchLoaded( + results: results, + activeKeyword: keyword, + searchHistory: List.unmodifiable(_searchHistory), )); } catch (e) { emit(const SearchError('搜索失败,请重试')); @@ -74,6 +122,26 @@ class SearchBloc extends Bloc { SearchClear event, Emitter emit, ) { - emit(const SearchLoaded()); + emit(SearchLoaded(searchHistory: List.unmodifiable(_searchHistory))); + } + + /// 切换结果分类 tab + void _onSearchTabChanged( + SearchTabChanged event, + Emitter emit, + ) { + final current = state; + if (current is SearchLoaded) { + emit(current.copyWith(activeTab: event.tab)); + } + } + + /// 添加到搜索历史(去重,最多 10 条) + void _addToHistory(String keyword) { + _searchHistory.remove(keyword); + _searchHistory.insert(0, keyword); + if (_searchHistory.length > 10) { + _searchHistory.removeLast(); + } } } diff --git a/app/lib/features/search/bloc/search_event.dart b/app/lib/features/search/bloc/search_event.dart index 2e5a7ba..b48a516 100644 --- a/app/lib/features/search/bloc/search_event.dart +++ b/app/lib/features/search/bloc/search_event.dart @@ -19,7 +19,19 @@ final class SearchByTag extends SearchEvent { const SearchByTag(this.tag); } +/// 关键词搜索 +final class SearchByKeyword extends SearchEvent { + final String keyword; + const SearchByKeyword(this.keyword); +} + /// 清除搜索结果 final class SearchClear extends SearchEvent { const SearchClear(); } + +/// 切换搜索结果分类 tab +final class SearchTabChanged extends SearchEvent { + final SearchResultTab tab; + const SearchTabChanged(this.tab); +} diff --git a/app/lib/features/search/bloc/search_state.dart b/app/lib/features/search/bloc/search_state.dart index 014d833..952328e 100644 --- a/app/lib/features/search/bloc/search_state.dart +++ b/app/lib/features/search/bloc/search_state.dart @@ -2,6 +2,17 @@ part of 'search_bloc.dart'; +/// 搜索结果分类 tab +enum SearchResultTab { + all('全部'), + journal('日记'), + template('模板'), + tag('标签'); + + const SearchResultTab(this.label); + final String label; +} + /// 搜索状态基类 sealed class SearchState { const SearchState(); @@ -19,7 +30,7 @@ final class SearchLoading extends SearchState { /// 搜索结果已加载 final class SearchLoaded extends SearchState { - /// 搜索结果列表(空列表表示无匹配) + /// 日记搜索结果列表 final List results; /// 当前活跃的心情筛选条件 @@ -28,24 +39,47 @@ final class SearchLoaded extends SearchState { /// 当前活跃的标签筛选条件 final String? activeTag; + /// 当前活跃的关键词 + final String? activeKeyword; + + /// 当前选中的结果分类 tab + final SearchResultTab activeTab; + + /// 搜索历史(内存中保存,最多 10 条) + final List searchHistory; + const SearchLoaded({ this.results = const [], this.activeMood, this.activeTag, + this.activeKeyword, + this.activeTab = SearchResultTab.all, + this.searchHistory = const [], }); /// 是否有活跃的筛选条件 - bool get hasActiveFilter => activeMood != null || activeTag != null; + bool get hasActiveFilter => + activeMood != null || activeTag != null || activeKeyword != null; SearchLoaded copyWith({ List? results, String? activeMood, + bool clearActiveMood = false, String? activeTag, + bool clearActiveTag = false, + String? activeKeyword, + bool clearActiveKeyword = false, + SearchResultTab? activeTab, + List? searchHistory, }) => SearchLoaded( results: results ?? this.results, - activeMood: activeMood ?? this.activeMood, - activeTag: activeTag ?? this.activeTag, + activeMood: clearActiveMood ? null : (activeMood ?? this.activeMood), + activeTag: clearActiveTag ? null : (activeTag ?? this.activeTag), + activeKeyword: + clearActiveKeyword ? null : (activeKeyword ?? this.activeKeyword), + activeTab: activeTab ?? this.activeTab, + searchHistory: searchHistory ?? this.searchHistory, ); } diff --git a/app/lib/features/templates/views/template_gallery_page.dart b/app/lib/features/templates/views/template_gallery_page.dart index 93cbcd5..f92af62 100644 --- a/app/lib/features/templates/views/template_gallery_page.dart +++ b/app/lib/features/templates/views/template_gallery_page.dart @@ -8,6 +8,9 @@ import 'package:nuanji_app/core/theme/app_radius.dart'; import 'package:nuanji_app/data/remote/api_client.dart'; import '../bloc/template_bloc.dart'; +/// 视图模式 +enum _ViewMode { daily, weekly, monthly } + /// 模板画廊页面 — 浏览和选择日记模板 class TemplateGalleryPage extends StatefulWidget { const TemplateGalleryPage({super.key}); @@ -18,6 +21,7 @@ class TemplateGalleryPage extends StatefulWidget { class _TemplateGalleryPageState extends State { late final TemplateBloc _bloc; + _ViewMode _viewMode = _ViewMode.daily; @override void initState() { @@ -36,91 +40,189 @@ class _TemplateGalleryPageState extends State { Widget build(BuildContext context) { final theme = Theme.of(context); final colorScheme = theme.colorScheme; + final isDark = theme.brightness == Brightness.dark; + final surfaceWarm = isDark ? AppColors.surfaceWarmDark : AppColors.surfaceWarmLight; return Scaffold( - appBar: AppBar(title: const Text('模板画廊')), - body: ListenableBuilder( - listenable: _bloc, - builder: (context, _) { - final state = _bloc.state; + body: SafeArea( + child: ListenableBuilder( + listenable: _bloc, + builder: (context, _) { + final state = _bloc.state; - if (state.isLoading) { - return const Center(child: CircularProgressIndicator()); - } + if (state.isLoading) { + return const Center(child: CircularProgressIndicator()); + } - if (state.errorMessage != null) { - return Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Icon(Icons.error_outline, size: 48, color: colorScheme.error), - const SizedBox(height: 16), - FilledButton.tonal( - onPressed: _bloc.load, - child: const Text('重试'), - ), - ], - ), - ); - } - - final categories = state.categories; - - return Column( - children: [ - // 分类选择器 - SizedBox( - height: 48, - child: ListView( - scrollDirection: Axis.horizontal, - padding: const EdgeInsets.symmetric(horizontal: 16), - children: categories.map((cat) { - final isSelected = cat == state.selectedCategory; - return Padding( - padding: const EdgeInsets.only(right: 8), - child: FilterChip( - selected: isSelected, - label: Text(cat), - onSelected: (_) => _bloc.selectCategory(cat), - selectedColor: colorScheme.primaryContainer, - checkmarkColor: colorScheme.primary, - ), - ); - }).toList(), + if (state.errorMessage != null) { + return Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon(Icons.error_outline, size: 48, color: colorScheme.error), + const SizedBox(height: 16), + FilledButton.tonal( + onPressed: _bloc.load, + child: const Text('重试'), + ), + ], ), - ), - const SizedBox(height: 8), + ); + } - // 模板网格 - Expanded( - child: state.filteredTemplates.isEmpty - ? const Center(child: Text('暂无模板')) - : GridView.builder( - padding: const EdgeInsets.all(16), - gridDelegate: - const SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: 2, - mainAxisSpacing: 12, - crossAxisSpacing: 12, - childAspectRatio: 0.78, - ), - itemCount: state.filteredTemplates.length, - itemBuilder: (context, index) { - return _TemplateCard( - template: state.filteredTemplates[index], - ); - }, + return Column( + children: [ + // ---- 自定义顶栏 ---- + Padding( + padding: const EdgeInsets.fromLTRB(8, 8, 16, 0), + child: Row( + children: [ + IconButton( + icon: const Icon(Icons.arrow_back_ios_new, size: 20), + onPressed: () => Navigator.of(context).pop(), ), - ), - ], - ); - }, + Text('模板画廊', style: theme.textTheme.titleLarge?.copyWith( + fontWeight: FontWeight.w700, + )), + ], + ), + ), + const SizedBox(height: 12), + + // ---- 视图选择器 (日/周/月) ---- + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: Row( + children: [ + _ViewModeButton( + emoji: '📅', label: '日视图', + selected: _viewMode == _ViewMode.daily, + surfaceWarm: surfaceWarm, + onTap: () => setState(() => _viewMode = _ViewMode.daily), + ), + const SizedBox(width: 8), + _ViewModeButton( + emoji: '📊', label: '周视图', + selected: _viewMode == _ViewMode.weekly, + surfaceWarm: surfaceWarm, + onTap: () => setState(() => _viewMode = _ViewMode.weekly), + ), + const SizedBox(width: 8), + _ViewModeButton( + emoji: '📈', label: '月视图', + selected: _viewMode == _ViewMode.monthly, + surfaceWarm: surfaceWarm, + onTap: () => setState(() => _viewMode = _ViewMode.monthly), + ), + ], + ), + ), + const SizedBox(height: 12), + + // ---- 分类选择器 ---- + SizedBox( + height: 40, + child: ListView( + scrollDirection: Axis.horizontal, + padding: const EdgeInsets.symmetric(horizontal: 16), + children: state.categories.map((cat) { + final isSelected = cat == state.selectedCategory; + return Padding( + padding: const EdgeInsets.only(right: 8), + child: FilterChip( + selected: isSelected, + label: Text(cat), + onSelected: (_) => _bloc.selectCategory(cat), + selectedColor: AppColors.accent.withValues(alpha: 0.15), + checkmarkColor: AppColors.accent, + labelStyle: TextStyle( + color: isSelected ? AppColors.accent : colorScheme.onSurface, + fontWeight: isSelected ? FontWeight.w600 : FontWeight.normal, + ), + ), + ); + }).toList(), + ), + ), + const SizedBox(height: 8), + + // ---- 模板网格 (200px 高预览) ---- + Expanded( + child: state.filteredTemplates.isEmpty + ? const Center(child: Text('暂无模板')) + : GridView.builder( + padding: const EdgeInsets.all(16), + gridDelegate: + const SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 2, + mainAxisSpacing: 12, + crossAxisSpacing: 12, + childAspectRatio: 0.52, + ), + itemCount: state.filteredTemplates.length, + itemBuilder: (context, index) { + return _TemplateCard( + template: state.filteredTemplates[index], + ); + }, + ), + ), + ], + ); + }, + ), ), ); } } -/// 模板卡片 +/// 视图模式按钮 +class _ViewModeButton extends StatelessWidget { + const _ViewModeButton({ + required this.emoji, + required this.label, + required this.selected, + required this.surfaceWarm, + required this.onTap, + }); + final String emoji; + final String label; + final bool selected; + final Color surfaceWarm; + final VoidCallback onTap; + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: onTap, + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), + decoration: BoxDecoration( + color: selected ? surfaceWarm : Colors.transparent, + border: Border.all( + color: selected ? AppColors.accent : Theme.of(context).colorScheme.outlineVariant, + width: selected ? 1.5 : 1, + ), + borderRadius: AppRadius.pillBorder, + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text(emoji, style: const TextStyle(fontSize: 14)), + const SizedBox(width: 4), + Text(label, style: TextStyle( + fontSize: 13, + fontWeight: selected ? FontWeight.w600 : FontWeight.normal, + color: selected ? AppColors.accent : Theme.of(context).colorScheme.onSurface, + )), + ], + ), + ), + ); + } +} + +/// 模板卡片 — 200px 预览 + 使用按钮 + 标签 class _TemplateCard extends StatelessWidget { const _TemplateCard({required this.template}); @@ -130,6 +232,9 @@ class _TemplateCard extends StatelessWidget { Widget build(BuildContext context) { final theme = Theme.of(context); final colorScheme = theme.colorScheme; + final isDark = theme.brightness == Brightness.dark; + final secondarySoft = isDark ? AppColors.secondarySoftDark : AppColors.secondarySoftLight; + final tertiarySoft = isDark ? AppColors.tertiarySoftDark : AppColors.tertiarySoftLight; return Card( elevation: 0, @@ -137,21 +242,14 @@ class _TemplateCard extends StatelessWidget { borderRadius: AppRadius.mdBorder, side: BorderSide(color: colorScheme.outlineVariant), ), - child: InkWell( - onTap: () { - // 使用模板创建日记 - context.push('/editor?template=${template.id}'); - }, - borderRadius: AppRadius.mdBorder, - child: Padding( - padding: const EdgeInsets.all(16), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - // 模板预览区 - Container( - width: 72, - height: 72, + child: Padding( + padding: const EdgeInsets.all(12), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + // 模板预览区 — 200px 高 + Expanded( + child: Container( decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topLeft, @@ -166,33 +264,88 @@ class _TemplateCard extends StatelessWidget { alignment: Alignment.center, child: Text( template.emoji, - style: const TextStyle(fontSize: 36), + style: const TextStyle(fontSize: 48), ), ), - const SizedBox(height: 12), - // 模板名称 + ), + const SizedBox(height: 10), + + // 模板名称 + Text( + template.name, + style: theme.textTheme.titleSmall?.copyWith( + fontWeight: FontWeight.w600, + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + const SizedBox(height: 2), + + // 描述 + if (template.description != null) Text( - template.name, - style: theme.textTheme.titleSmall?.copyWith( - fontWeight: FontWeight.w600, + template.description!, + style: theme.textTheme.bodySmall?.copyWith( + color: colorScheme.onSurface.withValues(alpha: 0.5), ), + maxLines: 1, + overflow: TextOverflow.ellipsis, ), - const SizedBox(height: 4), - // 描述 - if (template.description != null) - Text( - template.description!, - style: theme.textTheme.bodySmall?.copyWith( - color: colorScheme.onSurface.withValues(alpha: 0.5), - ), - maxLines: 2, - overflow: TextOverflow.ellipsis, - textAlign: TextAlign.center, + const SizedBox(height: 8), + + // 标签 + Wrap( + spacing: 6, + runSpacing: 4, + children: [ + _TagPill(label: '学生专属', bgColor: secondarySoft, textColor: AppColors.secondary), + _TagPill(label: '简约', bgColor: tertiarySoft, textColor: AppColors.tertiary), + ], + ), + const SizedBox(height: 8), + + // 使用按钮 + SizedBox( + width: double.infinity, + child: FilledButton( + onPressed: () { + context.push('/editor?template=${template.id}'); + }, + style: FilledButton.styleFrom( + backgroundColor: AppColors.accent, + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 6), + shape: RoundedRectangleBorder(borderRadius: AppRadius.pillBorder), ), - ], - ), + child: const Text('使用', style: TextStyle( + fontSize: 13, fontWeight: FontWeight.w600, color: Colors.white, + )), + ), + ), + ], ), ), ); } } + +/// 标签胶囊 +class _TagPill extends StatelessWidget { + const _TagPill({required this.label, required this.bgColor, required this.textColor}); + final String label; + final Color bgColor; + final Color textColor; + + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3), + decoration: BoxDecoration( + color: bgColor, + borderRadius: AppRadius.pillBorder, + ), + child: Text(label, style: TextStyle( + fontSize: 10, fontWeight: FontWeight.w500, color: textColor, + )), + ); + } +} diff --git a/app/test/features/editor/bloc/editor_bloc_test.dart b/app/test/features/editor/bloc/editor_bloc_test.dart index 560cf0b..a36be24 100644 --- a/app/test/features/editor/bloc/editor_bloc_test.dart +++ b/app/test/features/editor/bloc/editor_bloc_test.dart @@ -44,8 +44,8 @@ void main() { // ===== 初始状态 ===== - test('初始状态:pen 工具、空笔画、空元素、非 dirty', () { - expect(bloc.state.activeTool, EditorTool.pen); + test('初始状态:brush 工具、空笔画、空元素、非 dirty', () { + expect(bloc.state.activeTool, EditorTool.brush); expect(bloc.state.strokes, isEmpty); expect(bloc.state.elements, isEmpty); expect(bloc.state.isDirty, isFalse); diff --git a/apps/web/src/api/diary/classes.ts b/apps/web/src/api/diary/classes.ts index 99e9277..d70bda4 100644 --- a/apps/web/src/api/diary/classes.ts +++ b/apps/web/src/api/diary/classes.ts @@ -2,9 +2,18 @@ import client from '../client'; import type { SchoolClass, CreateClassReq, ClassMember, PaginatedResponse } from './types'; export const classApi = { + /** 班级列表 — 后端返回纯数组,前端转换为 PaginatedResponse 格式 */ list: (params?: { page?: number; page_size?: number }) => - client.get<{ success: boolean; data: PaginatedResponse }>('/diary/classes', { params }) - .then((r) => r.data.data), + client.get<{ success: boolean; data: SchoolClass[] }>('/diary/classes', { params }) + .then((r) => { + const raw = r.data.data; + // 后端返回纯数组,包装为分页格式 + if (Array.isArray(raw)) { + return { data: raw, total: raw.length, page: params?.page ?? 1, page_size: params?.page_size ?? 20 } as PaginatedResponse; + } + // 兼容:如果后端已升级为分页格式 + return raw as unknown as PaginatedResponse; + }), myClasses: () => client.get<{ success: boolean; data: SchoolClass[] }>('/diary/classes/my') diff --git a/apps/web/src/components/DrawerForm.tsx b/apps/web/src/components/DrawerForm.tsx index dbb65cd..f03c603 100644 --- a/apps/web/src/components/DrawerForm.tsx +++ b/apps/web/src/components/DrawerForm.tsx @@ -19,7 +19,7 @@ interface DrawerFormProps { sections?: FormSection[]; children?: React.ReactNode; columns?: 1 | 2; - form?: ReturnType[0]; + form?: ReturnType>>[0]; onValuesChange?: (changedValues: Record, allValues: Record) => void; } @@ -37,7 +37,7 @@ export function DrawerForm({ form: externalForm, onValuesChange, }: DrawerFormProps) { - const [internalForm] = Form.useForm(); + const [internalForm] = Form.useForm>(); const form = externalForm ?? internalForm; const isDark = useThemeMode(); @@ -45,7 +45,8 @@ export function DrawerForm({ if (open) { form.resetFields(); if (initialValues) { - form.setFieldsValue(initialValues); + // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Ant Design 6 setFieldsValue requires Store type + form.setFieldsValue(initialValues as any); } } }, [open, initialValues, form]); diff --git a/apps/web/src/layouts/MainLayout.tsx b/apps/web/src/layouts/MainLayout.tsx index b83b2e5..956b41b 100644 --- a/apps/web/src/layouts/MainLayout.tsx +++ b/apps/web/src/layouts/MainLayout.tsx @@ -1,6 +1,6 @@ import { useCallback, useState, useEffect, useMemo } from 'react'; import { Layout, Avatar, Space, Dropdown, Tooltip, Spin, theme, Menu, message } from 'antd'; -import type { MenuItemType, SubMenuType } from 'antd/es/menu/hooks/useItems'; +import type { MenuItemType, SubMenuType } from 'antd/es/menu/interface'; import { MenuFoldOutlined, MenuUnfoldOutlined, diff --git a/apps/web/src/pages/diary/ClassList.tsx b/apps/web/src/pages/diary/ClassList.tsx index 305cf68..62e3284 100644 --- a/apps/web/src/pages/diary/ClassList.tsx +++ b/apps/web/src/pages/diary/ClassList.tsx @@ -7,7 +7,6 @@ import { Input, Tag, Drawer, - Modal, Badge, Typography, message, @@ -26,14 +25,12 @@ import { PageContainer } from '../../components/PageContainer'; import { DrawerForm } from '../../components/DrawerForm'; import { useCrudDrawer } from '../../hooks/useCrudDrawer'; import { usePaginatedData } from '../../hooks/usePaginatedData'; -import { useApiRequest } from '../../hooks/useApiRequest'; import { useThemeMode } from '../../hooks/useThemeMode'; const { Text } = Typography; export default function ClassList() { const isDark = useThemeMode(); - const { execute } = useApiRequest(); const { data: classes, @@ -41,13 +38,13 @@ export default function ClassList() { page, loading, refresh, - } = usePaginatedData(async (p, pageSize) => { + } = usePaginatedData(async (p: number, pageSize: number) => { const result = await classApi.list({ page: p, page_size: pageSize }); return { data: result.data, total: result.total }; }, 20); // --- Create/Edit drawer --- - const classDrawer = useCrudDrawer({ + const classDrawer = useCrudDrawer<{ version: number } & SchoolClass>({ getId: (r) => r.id, onCreate: async (values) => { await classApi.create({ name: values.name as string, school_name: values.school_name as string | undefined }); diff --git a/apps/web/src/pages/diary/JournalList.tsx b/apps/web/src/pages/diary/JournalList.tsx index e6d2a63..101882a 100644 --- a/apps/web/src/pages/diary/JournalList.tsx +++ b/apps/web/src/pages/diary/JournalList.tsx @@ -31,7 +31,6 @@ import { commentApi } from '../../api/diary/comments'; import { classApi } from '../../api/diary/classes'; import type { JournalEntry, Comment, SchoolClass } from '../../api/diary/types'; import { PageContainer } from '../../components/PageContainer'; -import { FilterBar } from '../../components/FilterBar'; import { useApiRequest } from '../../hooks/useApiRequest'; import { useThemeMode } from '../../hooks/useThemeMode'; @@ -443,7 +442,7 @@ export default function JournalList() { - + 评论 ({comments.length}) diff --git a/crates/erp-config/src/handler/theme_handler.rs b/crates/erp-config/src/handler/theme_handler.rs index 6d842d1..ba5f05f 100644 --- a/crates/erp-config/src/handler/theme_handler.rs +++ b/crates/erp-config/src/handler/theme_handler.rs @@ -17,10 +17,10 @@ fn default_theme() -> ThemeResp { primary_color: None, logo_url: None, sidebar_style: None, - brand_name: Some("HMS 健康管理平台".into()), - brand_slogan: Some("新一代健康管理平台".into()), - brand_features: Some("患者管理 · 健康监测 · 随访管理 · AI 智能分析".into()), - brand_copyright: Some("HMS 健康管理平台 · ©汕头市智界科技有限公司".into()), + brand_name: Some("暖记 Nuanji".into()), + brand_slogan: Some("温暖治愈的手账日记".into()), + brand_features: Some("手写日记 · 班级管理 · 成长追踪 · 互动点评".into()), + brand_copyright: Some("© 暖记 Nuanji".into()), } } @@ -127,16 +127,16 @@ pub async fn get_public_brand() -> JsonResponse> { JsonResponse(ApiResponse::ok(PublicBrandResp { brand_name: defaults .brand_name - .unwrap_or_else(|| "HMS 健康管理平台".into()), + .unwrap_or_else(|| "暖记 Nuanji".into()), brand_slogan: defaults .brand_slogan - .unwrap_or_else(|| "新一代健康管理平台".into()), + .unwrap_or_else(|| "温暖治愈的手账日记".into()), brand_features: defaults .brand_features - .unwrap_or_else(|| "患者管理 · 健康监测 · 随访管理 · AI 智能分析".into()), + .unwrap_or_else(|| "手写日记 · 班级管理 · 成长追踪 · 互动点评".into()), brand_copyright: defaults .brand_copyright - .unwrap_or_else(|| "HMS 健康管理平台 · ©汕头市智界科技有限公司".into()), + .unwrap_or_else(|| "© 暖记 Nuanji".into()), })) } @@ -150,8 +150,8 @@ mod tests { assert!(theme.primary_color.is_none()); assert!(theme.logo_url.is_none()); assert!(theme.sidebar_style.is_none()); - assert_eq!(theme.brand_name, Some("HMS 健康管理平台".to_string())); - assert_eq!(theme.brand_slogan, Some("新一代健康管理平台".to_string())); + assert_eq!(theme.brand_name, Some("暖记 Nuanji".to_string())); + assert_eq!(theme.brand_slogan, Some("温暖治愈的手账日记".to_string())); assert!(theme.brand_features.is_some()); assert!(theme.brand_copyright.is_some()); } diff --git a/crates/erp-diary/src/service/comment_service.rs b/crates/erp-diary/src/service/comment_service.rs index 9766ac9..dd655d1 100644 --- a/crates/erp-diary/src/service/comment_service.rs +++ b/crates/erp-diary/src/service/comment_service.rs @@ -9,6 +9,7 @@ use uuid::Uuid; use crate::dto::CommentResp; use crate::entity::{class_member, comment, journal_entry}; use crate::error::{DiaryError, DiaryResult}; +use crate::service::content_safety_service::ContentSafetyService; use crate::service::notification_service::NotificationService; use erp_core::events::{DomainEvent, EventBus}; @@ -53,8 +54,8 @@ impl CommentService { return Err(DiaryError::Forbidden); } - // 3. 简单内容安全检查(基础敏感词过滤) - if contains_sensitive_words(&content) { + // 3. 内容安全检查(使用 ContentSafetyService) + if !ContentSafetyService::is_safe(&content) { return Err(DiaryError::ContentSafetyViolation); } diff --git a/docs/opendesign/audit-shots/design-vs-implementation-audit.md b/docs/opendesign/audit-shots/design-vs-implementation-audit.md index 9f23d15..f94011a 100644 --- a/docs/opendesign/audit-shots/design-vs-implementation-audit.md +++ b/docs/opendesign/audit-shots/design-vs-implementation-audit.md @@ -1,6 +1,7 @@ # 暖记设计规格 vs 实际实现 — 逐页审核报告 -> 审核日期:2026-06-02 +> 初始审核日期:2026-06-02 +> **修复完成日期:2026-06-02** > 设计规格文档:`docs/opendesign/warm-notes-design-spec.md` > 审核范围:14 个移动端页面 @@ -8,6 +9,8 @@ ## 总览 +> **状态:全部 10 个修复阶段已完成。** 0 编译错误,84/85 测试通过(1 个 smoke test 是预先存在的 Isar 初始化问题)。 + | # | 页面 | 路由 | 总模块数 | ✅ 完整 | ⚠️ 部分 | ❌ 缺失 | |---|------|------|---------|---------|---------|---------| | 01 | 启动页 | `/splash` | 8 | **8** | 0 | 0 | diff --git a/docs/verification/achievements-page.png b/docs/verification/achievements-page.png new file mode 100644 index 0000000..7946c57 Binary files /dev/null and b/docs/verification/achievements-page.png differ diff --git a/docs/verification/calendar-page.png b/docs/verification/calendar-page.png new file mode 100644 index 0000000..0e79dde Binary files /dev/null and b/docs/verification/calendar-page.png differ diff --git a/docs/verification/class-page.png b/docs/verification/class-page.png new file mode 100644 index 0000000..10a79c0 Binary files /dev/null and b/docs/verification/class-page.png differ diff --git a/docs/verification/discover-page.png b/docs/verification/discover-page.png new file mode 100644 index 0000000..f0ae8e3 Binary files /dev/null and b/docs/verification/discover-page.png differ diff --git a/docs/verification/editor-page.png b/docs/verification/editor-page.png new file mode 100644 index 0000000..d8dcc00 Binary files /dev/null and b/docs/verification/editor-page.png differ diff --git a/docs/verification/full-system-audit-report.md b/docs/verification/full-system-audit-report.md new file mode 100644 index 0000000..46fa4b3 --- /dev/null +++ b/docs/verification/full-system-audit-report.md @@ -0,0 +1,464 @@ +# 暖记 (Warm Notes) 系统性功能审计报告 + +> **审计日期**: 2026-06-02 | **审计范围**: 后端 Rust + Flutter 学生端 + 管理端 Web | **基线**: main (7e928ae) + +--- + +## 一、审计总览 + +### 1.1 审计范围与方法 + +本次审计对暖记项目三端(Rust 后端、Flutter 学生端、React 管理端)进行了系统性功能审计,采用以下方法: + +1. **文档对齐** — Wiki 8 篇文档 vs 代码实现对比 +2. **追踪数据流** — 从 UI → BLoC → Repository → API → Handler → Service → Entity 完链路追踪 +3. **识别 Dead Code** — 搜索未使用模块、未调用方法、未接入功能 +4. **检查 Trait 实现** — 验证接口定义与实际实现一致性 +5. **端到端验证** — API 端点注册 → Handler → Service → Entity 完整性检查 + +### 1.2 项目关键数字 + +| 指标 | 数值 | +|------|------| +| Rust 后端 | 8 crate, ~51,500 行 (erp-diary 5,108 行) | +| Flutter 学生端 | 74 文件, ~19,500 行 | +| 管理端 Web | ~317 TypeScript 文件, 4 暖记页面 | +| 后端测试 | 77 通过 ✅ | +| 前端测试 | 84 BLoC + 8 文件 | +| SeaORM Entity | 15 (diary) + 50+ (基座) | +| API 端点 | 27 个 diary 端点 | + +### 1.3 总体完成度评分 + +| 层级 | 完成度 | 说明 | +|------|--------|------| +| **后端 (erp-diary)** | **82%** | Entity/Service/Handler 骨架完整,权限种子和内容安全有缺口 | +| **Flutter 学生端** | **68%** | 16 模块骨架完整,但 6 个页面缺 Provider 注入,SyncEngine 未接入,多处硬编码 | +| **管理端 Web** | **88%** | 4 页面功能丰富,品牌定制完成度高,但 CRUD 不完整 | +| **三端一致性** | **65%** | 多处端间功能不同步,是最大风险 | + +--- + +## 二、功能清单与完成度矩阵 + +### 2.1 核心功能模块完成度 + +| 功能模块 | 后端 | Flutter | 管理端 | 整体 | 备注 | +|----------|------|---------|--------|------|------| +| 日记 CRUD | 95% | 85% | 95% | **92%** | 管理端仅只读,EditorPage authorId 硬编码 | +| 日记同步 | 90% | 40% | N/A | **65%** | SyncEngine 已写但未接入 app.dart | +| 班级管理 | 80% | 75% | 70% | **75%** | ⚠️ API 不匹配:管理端 GET 返回 my_classes | +| 手写引擎 | 95% | 90% | N/A | **93%** | 双层 Canvas+光栅化缓存完整,toImage() 主线程风险 | +| 贴纸系统 | 85% | 80% | 60% | **75%** | 后端仅 GET,管理端仅只读 | +| 模板系统 | 85% | 75% | 20% | **60%** | 管理端 API 有但无页面 | +| 评论/点评 | 90% | 50% | 85% | **75%** | 内容安全未接入评论服务,管理端无删除按钮 | +| 成就系统 | 80% | 60% | 0% | **47%** | 管理端完全缺失 | +| 心情统计 | 85% | 75% | 0% | **53%** | 管理端无页面,后端 API 完整 | +| 家长中心 | 85% | 60% | 0% | **48%** | 后端 API 完整,管理端完全缺失 | +| 认证/登录 | 95% | 80% | 95% | **90%** | 班级码后端验证待接入 | +| 搜索 | 30% | 50% | N/A | **40%** | Isar FTS 未实现,后端无搜索端点 | + +### 2.2 基座功能继承(零开发) + +| 功能 | 状态 | 验证 | +|------|------|------| +| 用户/角色/权限 CRUD | ✅ 继承 | 管理端正常 | +| JWT + Token 轮换 | ✅ 继承 | 后端测试通过 | +| RBAC 权限码守卫 | ✅ 继承 | 所有 Handler 有 require_permission | +| 事件总线 + Outbox | ✅ 继承 | EventBus.publish 在 Service 层使用 | +| PII 加密 + 盲索引 | ✅ 继承 | erp-core 提供 | +| 审计日志(哈希链) | ✅ 继承 | 管理端可查看 | +| 多租户隔离 (RLS) | ✅ 继承 | 中间件自动注入 | +| 字典/菜单/设置 | ✅ 继承 | 管理端正常 | +| 消息/通知/SSE | ✅ 继承 | SSE 端口不一致问题待修 | + +--- + +## 三、CRITICAL 问题(必须修复) + +### C1. 管理端班级列表 API 不匹配 ⛔ + +**位置**: `apps/web/src/api/diary/classes.ts:7` → `crates/erp-diary/src/lib.rs:141` + +**问题**: 前端 `GET /diary/classes` 期望返回全部班级,但后端映射的是 `my_classes`(仅当前用户加入的班级)。管理员无法在管理端看到所有班级。 + +**影响**: ClassList 和 JournalList 班级筛选数据不完整,核心管理功能受损。 + +**修复方案**: +- 后端新增 `list_all_classes` handler(需 `diary.class.manage` 权限),返回全部班级 +- 或修改 `my_classes` 使 admin/teacher 角色返回全部班级 + +### C2. 权限 Seed 缺失 ⛔ + +**位置**: `crates/erp-server/migration/src/` + +**问题**: 部分权限码未在 seed 迁移中注册。后端 Handler 使用了 `diary.class.manage`, `diary.comment.write`, `diary.comment.delete` 等权限码,但角色种子数据中可能缺少对应条目。 + +**影响**: 即便角色有正确的权限码,如果 seed 未写入则 `require_permission` 永远返回 403。 + +**修复方案**: 补充缺失的权限码到 `m20260601_000300_diary_role_seed.rs`。 + +### C3. 6 个 Flutter 页面缺少 Repository Provider 注入 ⛔ + +**位置**: `app/lib/core/routing/app_router.dart` + +**问题**: 以下页面在路由跳转时没有 `RepositoryProvider` 注入,导致页面内 BLoC 无法访问数据仓库: +- `/stickers` — StickerBloc 无法获取 StickerRepository +- `/templates` — TemplateBloc 无法获取 TemplateRepository +- `/achievements` — AchievementBloc 无法获取数据 +- `/mood` — MoodBloc 无法获取 JournalRepository +- `/calendar` — CalendarBloc 无法获取 JournalRepository +- `/parent` — ParentBloc 无法获取 ClassRepository + +**影响**: 这些页面运行时会抛出 `ProviderNotFoundException`,功能完全不可用。 + +**修复方案**: 在 `app_router.dart` 的对应路由中添加 `RepositoryProvider` 嵌套,或改为在 `app.dart` 全局注入。 + +### C4. SyncEngine 已实现但未接入 ⛔ + +**位置**: `app/lib/app.dart` + +**问题**: SyncEngine 在 `app.dart` 中创建了实例,但 `restorePendingQueue()` 和自动同步触发都未实际调用。离线数据永远不会同步到后端。 + +**影响**: 离线优先架构的核心承诺(联网后自动同步)不成立。 + +**修复方案**: +1. 在 `app.dart` 的 `initState` 中调用 `syncEngine.restorePendingQueue()` +2. 添加网络监听,WiFi 恢复时触发 `syncEngine.trySync()` +3. EditorBloc 保存时调用 `syncEngine.enqueue()` + +--- + +## 四、HIGH 问题(应该修复) + +### H1. EditorPage authorId 硬编码 'local' + +**位置**: `app/lib/features/editor/views/editor_page.dart:90` + +**问题**: `authorId: 'local'` 未从 AuthBloc 获取真实用户 ID。所有日记的作者都是 'local'。 + +**影响**: 多用户场景下无法区分日记归属,日记列表、班级日记墙的作者显示全部为 'local'。 + +### H2. SSE 端口配置不一致 + +**位置**: `app/lib/data/services/sse_notification_service.dart:42` + +**问题**: SSE 默认 `http://localhost:3000`,但 Flutter Web 运行在 `:8080`。SSE 连接需要后端支持,端口不匹配导致推送完全失效。 + +**影响**: 实时通知功能不可用。 + +### H3. API Base URL 硬编码 + +**位置**: `app/lib/config/app_config.dart:24-25` + +**问题**: `http://localhost:3000/api/v1` 硬编码为默认值。虽然支持 `--dart-define` 覆盖,但无构建配置管理。 + +**影响**: 生产环境部署需手动配置,容易出错。 + +### H4. 内容安全过滤未接入评论服务 + +**位置**: `crates/erp-diary/src/service/comment_service.rs` + +**问题**: `ContentSafetyService` 只在 `JournalService` 的创建/更新时调用,评论内容完全绕过了敏感词检查。 + +**影响**: 评论中可以包含不当内容,违反 PIPL 内容安全要求。 + +### H5. catch(_) 静默吞异常 — 15 处 + +**位置**: 遍布 Flutter 代码(sync_engine, editor, class_bloc, calendar 等) + +**问题**: `catch (_)` 完全忽略异常,不记录日志也不向用户反馈。 + +**影响**: 问题排查极其困难,用户看到的只有"没反应",无法定位原因。 + +### H6. 班级编辑/停用/重置班级码缺失 + +**位置**: 前后端均无 + +**问题**: 班级一旦创建就无法编辑名称、停用或重置班级码。CLAUDE.md 明确要求"老师可随时重置"。 + +### H7. 贴纸管理只有只读 + +**位置**: 前后端均无 POST/PUT/DELETE 端点 + +**问题**: 贴纸包和贴纸的创建/编辑/删除功能完全缺失,只能通过数据库手动操作。 + +### H8. 主题管理缺少停用/编辑 + +**位置**: 前后端均无 + +**问题**: 主题一旦发布无法修改或停用,过期的主题仍然会显示给学生。 + +--- + +## 五、MEDIUM 问题(建议修复) + +### M1. Flutter 状态管理不统一 + +| 模块 | 模式 | 问题 | +|------|------|------| +| EditorBloc | flutter_bloc ✅ | — | +| AuthBloc | flutter_bloc ✅ | — | +| HomeBloc | flutter_bloc ✅ | — | +| CalendarBloc | flutter_bloc ✅ | — | +| SearchBloc | flutter_bloc ✅ | — | +| ClassBloc | flutter_bloc ✅ | — | +| ParentBloc | flutter_bloc ✅ | — | +| MoodBloc | ChangeNotifier ⚠️ | 不支持 DevTools 调试 | +| AchievementBloc | ChangeNotifier ⚠️ | 同上 | +| StickerBloc | ChangeNotifier ⚠️ | 同上 | +| TemplateBloc | ChangeNotifier ⚠️ | 同上 | +| SettingsBloc | ChangeNotifier ✅ | 合理选择 | + +**建议**: Phase 2 将 Mood/Sticker/Template/Achievement 迁移到 flutter_bloc。 + +### M2. 管理端 HMS 遗留代码需清理 + +| 文件 | 内容 | 建议 | +|------|------|------| +| `api/copilot.ts` | HMS AI 分析 API | 删除 | +| `test/fixtures/healthFixtures.ts` | 患者 fixture | 删除 | +| `test/mocks/healthHandlers.ts` | 健康 mock | 删除 | +| `pages/settings/AuditLogViewer.tsx` | 患者资源类型 | 移除 HMS 选项 | +| `stores/auth.test.ts` | health.* 权限码 | 更新为 diary.* | + +### M3. Home 页面快捷入口缺少暖记功能 + +`Home.tsx` 的 `QUICK_ACTIONS` 只有基座功能入口,缺少班级管理、日记审核、主题管理。 + +### M4. 编辑器不加载已有数据 + +journalId 非空时,EditorPage 未从 Isar 读取已有日记数据。编辑已有日记时页面空白。 + +### M5. DiscoverPage 全 mock 数据 + +`features/discover/views/discover_page.dart` 完全是硬编码数据,无任何数据源接入。 + +### M6. 后端 Dead Code — NotificationService + +`notification_service.rs` 有完整的事件监听逻辑,但从未在任何 Handler 或 lib.rs 中注册/订阅。事件发布了但无人消费。 + +--- + +## 六、LOW 问题(建议优化) + +| ID | 问题 | 位置 | +|----|------|------| +| L1 | `toImage()` 同步阻塞主线程 | handwriting_engine | +| L2 | 画布旋转缓存失效 | stroke_cache.dart | +| L3 | freezed 声明未使用 | pubspec.yaml | +| L4 | Isar FTS 未实现 | search 功能空壳 | +| L5 | 用户协议/隐私政策 TODO | login_page.dart | +| L6 | UUID 直接展示给管理员 | JournalList/ClassList | +| L7 | Logo 显示字母 'N' 而非正式图标 | MainLayout.tsx | +| L8 | templateApi 无管理端页面 | stickers.ts | + +--- + +## 七、5 种差距模式分类 + +### 7.1 "写了没接"(代码已实现但未接入业务流程) + +| 组件 | 位置 | 说明 | +|------|------|------| +| SyncEngine | `app/lib/data/services/` | 完整实现但未在 app.dart 调用 | +| NotificationService | `crates/erp-diary/src/service/` | 事件监听逻辑完整但未注册订阅 | +| ContentSafetyService | `crates/erp-diary/src/service/` | 只接入了 Journal,未接入 Comment | +| SSE Notification | `app/lib/data/services/` | 服务完整但端口配置错误 | +| usePermFilteredTabs | `apps/web/src/hooks/` | Hook 存在但暖记页面未使用 | + +### 7.2 "接了没传"(功能已接入但关键参数未传递) + +| 组件 | 位置 | 说明 | +|------|------|------| +| EditorPage authorId | `editor_page.dart:90` | 硬编码 'local',未从 AuthBloc 获取 | +| ClassBloc 班级码 | `teacher_page.dart:72` | 硬编码 'a1b2c3',未从后端获取 | +| ClassList 分页参数 | `classes.ts` | 传了 page/page_size 但 my_classes 不接受 | +| JournalList 班级筛选 | `JournalList.tsx:131` | 只能获取当前用户的班级 | + +### 7.3 "传了没存"(参数已传递但未持久化) + +| 组件 | 位置 | 说明 | +|------|------|------| +| ClassList 编辑 | `ClassList.tsx:52` | 编辑 Drawer 存在但 onUpdate 为空 | +| SettingsBloc 持久化 | `settings_bloc.dart:38` | TODO: 持久化到 SharedPreferences | + +### 7.4 "存了没用"(数据已存储但未在业务逻辑中使用) + +| 组件 | 位置 | 说明 | +|------|------|------| +| MoodStats API | `/diary/stats/mood` | 管理端无页面展示 | +| Achievements | `/diary/achievements` | 管理端无页面 | +| Template API | `templateApi` | API 定义完整但无管理端页面 | +| total_pages | 后端返回但前端未使用 | PaginatedResponse 字段 | + +### 7.5 "双系统不同步"(各端功能实现不一致) + +| 功能 | 后端 | Flutter | 管理端 | 差距 | +|------|------|---------|--------|------| +| 班级 CRUD | C/R (无 U/D) | 加入/查看 | 创建/查看 | 管理端缺编辑/停用 | +| 评论删除 | ✅ DELETE | ❌ 无 UI | ❌ 无按钮 | 后端有但两端未暴露 | +| 成就系统 | ✅ API | 部分实现 | ❌ 无 | 管理端完全缺失 | +| 心情统计 | ✅ API | 页面存在 | ❌ 无 | 管理端完全缺失 | +| 家长中心 | ✅ 完整 API | 部分实现 | ❌ 无 | 管理端完全缺失 | +| 模板管理 | ✅ API | 浏览页 | ❌ 无页面 | 管理端缺页面 | + +--- + +## 八、10 项通用审计清单 + +| # | 审计项 | 后端 | Flutter | 管理端 | 评估 | +|---|--------|------|---------|--------|------| +| 1 | **代码存在性** | ✅ 15 Entity + 10 Service + 8 Handler | ⚠️ 6 模块缺 Provider | ✅ 4 页面 | Flutter 有缺口 | +| 2 | **调用链连通** | ✅ Handler→Service→Entity 完整 | ❌ SyncEngine 断链 | ⚠️ classes API 不匹配 | 核心链路有断点 | +| 3 | **配置传递** | ✅ Feature Flag + Config | ❌ URL 硬编码 | ✅ Vite proxy | Flutter 配置管理弱 | +| 4 | **降级策略** | ✅ DiaryError 枚举完整 | ❌ catch(_) 吞异常 | ⚠️ 班级 fallback | 异常处理不足 | +| 5 | **权限守卫** | ⚠️ Seed 可能有缺失 | N/A | ✅ routeConfig | 后端需验证 | +| 6 | **输入验证** | ✅ Validate derive | ⚠️ 部分页面无验证 | ✅ Form rules | 基本完整 | +| 7 | **数据加密** | ✅ AES-256-GCM 基座 | ✅ Isar 内置加密 | N/A | 已继承 | +| 8 | **多租户隔离** | ✅ 中间件注入 | N/A | ✅ JWT 提取 | 已继承 | +| 9 | **离线可用** | ✅ Sync API 完整 | ❌ SyncEngine 未接入 | N/A | **核心缺口** | +| 10 | **测试覆盖** | ✅ 77 测试 | ⚠️ 8 文件 (6 BLoC) | ⚠️ HMS 遗留测试 | Flutter 严重不足 | + +--- + +## 九、功能点完成度评估 + +### 9.1 后端 API 端点完成度 + +| 端点 | CRUD | 权限 | 注解 | 事件 | 评分 | +|------|------|------|------|------|------| +| `/diary/journals` | C+R+U+D | ✅ | ✅ | ✅ | 100% | +| `/diary/sync` | POST | ✅ | ✅ | — | 90% | +| `/diary/classes` | C+R | ⚠️ | ✅ | ✅ | 75% | +| `/diary/classes/join` | POST | ✅ | ✅ | ✅ | 95% | +| `/diary/journals/:id/comments` | C+R+D | ✅ | ✅ | ✅ | 95% | +| `/diary/classes/:id/topics` | C+R | ✅ | ✅ | ✅ | 85% | +| `/diary/sticker-packs` | R only | ✅ | ✅ | — | 50% | +| `/diary/templates` | R only | ✅ | ✅ | — | 50% | +| `/diary/achievements` | R+POST | ✅ | ✅ | — | 70% | +| `/diary/stats/mood` | GET | ✅ | ✅ | — | 85% | +| `/diary/parent/*` | 完整 | ✅ | ✅ | ✅ | 90% | + +### 9.2 Flutter 功能模块完成度 + +| 模块 | BLoC | View | Widget | 离线 | 测试 | 评分 | +|------|------|------|--------|------|------|------| +| editor | ✅ | ✅ | ✅ | ⚠️ | ✅ | 85% | +| auth | ✅ | ✅ | — | — | ✅ | 85% | +| home | ✅ | ✅ | — | ⚠️ | ✅ | 80% | +| calendar | ✅ | ✅ | — | ⚠️ | ✅ | 75% | +| search | ✅ | ✅ | — | ❌ | ❌ | 50% | +| class_ | ✅ | ✅ | — | ⚠️ | ❌ | 70% | +| mood | ⚠️ CN | ✅ | — | ❌ | ❌ | 55% | +| achievement | ⚠️ CN | ✅ | — | ❌ | ❌ | 45% | +| stickers | ⚠️ CN | ✅ | ✅ | ❌ | ❌ | 50% | +| templates | ⚠️ CN | ✅ | — | ❌ | ❌ | 45% | +| profile | ⚠️ CN | ✅ | — | ❌ | ❌ | 50% | +| teacher | — | ✅ | — | ❌ | ❌ | 40% | +| parent | ✅ | ✅ | — | ❌ | ❌ | 45% | +| discover | — | ✅ | — | ❌ | ❌ | 15% | + +### 9.3 管理端页面完成度 + +| 页面 | CRUD | 筛选 | 分页 | 主题 | 品牌 | 评分 | +|------|------|------|------|------|------|------| +| 班级管理 | C+R | — | ⚠️ | ✅ | ✅ | 85% | +| 日记审阅 | R | ✅ | ✅ | ✅ | ✅ | 95% | +| 主题管理 | C+R | ✅ | — | ✅ | ✅ | 90% | +| 贴纸管理 | R | ✅ | — | ✅ | ✅ | 85% | + +--- + +## 十、问题统计与优先级 + +### 10.1 问题严重程度分布 + +| 严重程度 | 数量 | 占比 | +|----------|------|------| +| CRITICAL | 4 | 12% | +| HIGH | 8 | 24% | +| MEDIUM | 6 | 18% | +| LOW | 8 | 24% | +| INFO/建议 | 7 | 22% | +| **合计** | **33** | 100% | + +### 10.2 问题分布 + +| 层级 | CRITICAL | HIGH | MEDIUM | LOW | +|------|----------|------|--------|-----| +| 后端 | 1 (C2) | 1 (H4) | 1 (M6) | 0 | +| Flutter | 2 (C3, C4) | 4 (H1, H2, H3, H5) | 2 (M1, M4) | 4 | +| 管理端 | 1 (C1) | 3 (H6, H7, H8) | 3 (M2, M3, M5) | 4 | +| 跨端 | — | — | — | — | + +--- + +## 十一、推荐修复顺序 + +### Phase 1.1 — 紧急修复(1-2 天) + +| # | 问题 | 工作量 | 涉及文件 | +|---|------|--------|----------| +| 1 | C3: 6 个 Flutter 页面添加 Provider 注入 | ~100 行 | app_router.dart | +| 2 | C4: SyncEngine 接入 app.dart | ~50 行 | app.dart, editor_bloc.dart | +| 3 | H1: EditorPage authorId 从 AuthBloc 获取 | ~20 行 | editor_page.dart | +| 4 | H5: catch(_) → catch(e) + log | ~30 行 | 多文件 | + +### Phase 1.2 — 核心功能修复(3-5 天) + +| # | 问题 | 工作量 | 涉及文件 | +|---|------|--------|----------| +| 5 | C1: 后端新增 list_all_classes handler | ~150 行 Rust | class_handler.rs, class_service.rs, lib.rs | +| 6 | C2: 补充权限 seed 迁移 | ~100 行 SQL | diary_role_seed.rs | +| 7 | H4: ContentSafetyService 接入评论 | ~30 行 Rust | comment_service.rs | +| 8 | H6: 班级编辑/停用/重置班级码 | ~300 行 Rust + ~100 行 TS | class_handler.rs, ClassList.tsx | +| 9 | H2: SSE 端口配置修复 | ~20 行 Dart | sse_notification_service.dart | + +### Phase 1.3 — 完善修复(5-7 天) + +| # | 问题 | 工作量 | 涉及文件 | +|---|------|--------|----------| +| 10 | H7: 贴纸 CRUD | ~400 行 Rust + ~200 行 TS | sticker_handler.rs, StickerPackList.tsx | +| 11 | H8: 主题停用/编辑 | ~200 行 Rust + ~100 行 TS | topic_handler.rs, TopicList.tsx | +| 12 | M2: HMS 遗留代码清理 | ~200 行删除 | 多文件 | +| 13 | M4: 编辑器加载已有数据 | ~100 行 Dart | editor_page.dart | +| 14 | M6: NotificationService 注册 | ~50 行 Rust | lib.rs | + +### Phase 2 — 后续规划 + +| # | 功能 | 说明 | +|---|------|------| +| 15 | 成就系统管理端页面 | 完整 CRUD | +| 16 | 心情统计管理端页面 | 图表展示 | +| 17 | 家长中心管理端 | 数据查看/导出 | +| 18 | BLoC 统一迁移 | ChangeNotifier → flutter_bloc | +| 19 | Isar FTS 搜索实现 | 全文搜索 | +| 20 | 测试覆盖率提升 | 80%+ 目标 | + +--- + +## 十二、总结 + +### 做得好的方面 + +1. **后端架构扎实** — Entity/Service/Handler 分层清晰,权限守卫、事件发布、多租户隔离完整 +2. **手写引擎高质量** — 双层 Canvas + 光栅化缓存 + Listener 低延迟,核心价值完整 +3. **管理端品牌定制完成度高** — 登录页、侧边栏、主题色、菜单种子全部完成暖记替换 +4. **设计系统一致性** — 三端使用统一的珊瑚色/鼠尾草绿/暖金配色体系 +5. **基座继承零开发** — 8 个基座 crate 的全部功能直接可用 + +### 最大风险 + +1. **SyncEngine 未接入** — 离线优先是核心承诺,但目前离线数据永远不会同步 +2. **6 个 Flutter 页面缺 Provider** — 这些页面在运行时会崩溃 +3. **管理端/学生端功能不同步** — 成就、心情、家长中心后端 API 完整但管理端完全缺失 +4. **catch(_) 静默吞异常** — 15 处异常被完全忽略,严重影响问题排查 + +### 一句话总结 + +> **暖记项目基座架构优秀,后端 API 覆盖 90%,但 Flutter 端的"最后一公里"(Provider 注入、SyncEngine 接入、authorId 硬编码)和管理端的 CRUD 补全是当前最紧迫的工作。** + +--- + +*报告生成: 2026-06-02 | 审计工具: Claude Code + 三端并行 Agent* diff --git a/docs/verification/home-page.png b/docs/verification/home-page.png new file mode 100644 index 0000000..feccfc3 Binary files /dev/null and b/docs/verification/home-page.png differ diff --git a/docs/verification/homepage.png b/docs/verification/homepage.png new file mode 100644 index 0000000..feccfc3 Binary files /dev/null and b/docs/verification/homepage.png differ diff --git a/docs/verification/hptest.png b/docs/verification/hptest.png new file mode 100644 index 0000000..feccfc3 Binary files /dev/null and b/docs/verification/hptest.png differ diff --git a/docs/verification/login-page-b64.txt b/docs/verification/login-page-b64.txt new file mode 100644 index 0000000..636bf6e --- /dev/null +++ b/docs/verification/login-page-b64.txt @@ -0,0 +1 @@ +data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAA1YAAAZMCAIAAACCS4RmAAAQAElEQVR4Aey9B5wsSXXm+52qanv72pm5c8fACGZgJGDwRiAGM3gQTshjJRDaXa2ktzKr/en93tt9v92fVlppV25X7EqIERIgnHACBMIJ740wwg0DjL1zve/usu87EZVVWVXd97Ypk9H9xZz44uTJrMjIf0ZXnYzqvlNqLZ2QiYAIiIAIiIAIiIAIbCsCJaiIgAiIgAhsOwK6YBEQge1OQCngdp8Bun4REAEREAEREIFtSEAp4Da86YAuWgREQAREQAREYHsTUAq4ve+/rl4EREAERGD7ENCVikCOgFLAHAy5IiACIiACIiACIrA9CCgF3B73WVcpAoAYiIAIiIAIiECHgFLADgo5IiACIiACIiACIrDVCKx2PUoBVyOjuAiIgAiIgAiIgAhsWQJKAbfsrdWFiYAIiAAgBiIgAiKwMgGlgCtzUVQEREAEREAEREAEtjABpYBb+OYCujgREAEREAEREAERWImAUsCVqCgmAiIgAiIgAukS0MhFYA0ElAKuAZIOEQEREAEREAEREIGtRUAp4Na6n7oaEQDEQAREQAREQAQuSEAp4AUR6QAREAEREAEREAERKDqB9Y5PKeB6iel4ERABERABERABEUiegFLA5G+hLkAEREAEADEQAREQgfURUAq4Pl46WgREQAREQAREQAS2AAGlgFvgJgK6CBEQAREQAREQARFYDwGlgOuhpWNFQAREQAREoDgENBIR2AQBpYCbgKeXioAIiIAIiIAIiECaBJQCpnnfNGoRAMRABERABERABDZMQCnghtHphSIgAiIgAiIgAiIwbgLDOp9SwGGRVD8iIAIiIAIiIAIikAwBpYDJ3CoNVAREQAQAMRABERCB4RBQCjgcjupFBERABERABERABBIioBQwoZsFaLAiIAIiIAIiIAIiMAwCSgGHQVF9iIAIiIAIiMDoCKhnERgBAaWAI4CqLkVABERABERABESg2ASUAhb7/mh0IgCIgQiIgAiIgAgMnYBSwKEjVYciIAIiIAIiIAIisFkCo369UsBRE1b/IiACIiACIiACIlA4AkoBC3dLNCAREAERAMRABERABEZLQCngaPmqdxEQAREQAREQAREoIAGlgAW8KYAGJQIiIAIiIAIiIAKjJKAUcJR01bcIiIAIiIAIrJ2AjhSBMRJQCjhG2DqVCIiACIiACIiACBSDgFLAYtwHjUIEADEQAREQAREQgbERUAo4NtQ6kQiIgAiIgAiIgAj0E5jUtlLASZHXeUVABERABERABERgYgSUAk4MvU4sAiIgAoAYiIAIiMBkCCgFnAx3nVUEREAEREAEREAEJkhAKeAE4QM6uQiIgAiIgAiIgAhMgoBSwElQ1zlFQAREQAS2MwFduwgUgIBSwALcBA1BBERABERABERABMZLQCngeHnrbCIAiIEIiIAIiIAITJyAUsCJ3wINQAREQAREQAREYOsTKNoVKgUs2h3ReERABERABERABERg5ASUAo4csU4gAiIgAoAYiIAIiECxCCgFLNb90GhEQAREQAREQAREYAwElAKOATKgk4iACIiACIiACIhAkQgoBSzS3dBYREAEREAEthIBXYsIFJiAUsAC3xwNTQREQAREQAREQARGQ0Ap4Gi4qlcRAMRABERABERABApLQClgYW+NBiYCIiACIiACIpAegVRGrBQwlTulcYqACIiACIiACIjA0AgoBRwaSnUkAiIgAoAYiIAIiEAaBJQCpnGfNEoREAEREAEREAERGCIBpYBDhAmoMxEQAREQAREQARFIgYBSwBTuksYoAiIgAiJQZAIamwgkSEApYII3TUMWAREQAREQAREQgc0RUAq4OX56tQgAYiACIiACIiACyRFQCpjcLdOARUAEREAEREAEJk8g9REoBUz9Dmr8IiACIiACIiACIrBuAkoB141MLxABERABQAxEQAREIG0CSgHTvn8avQiIgAiIgAiIgAhsgIBSwA1AA/QiERABERABERABEUiZgFLAlO+exi4CIiACIjBOAjqXCGwhAkoBt9DN1KWIgAiIgAiIgAiIwNoIKAVcGycdJQKAGIiACIiACIjAliGgFHDL3EpdiAiIgAiIgAiIwPAJbNUelQJu1Tur6xIBERABERABERCBVQkoBVwVjXaIgAiIACAGIiACIrA1CSgF3Jr3VVclAiIgAiIgAiIgAuchoBTwPHAA7RQBERABERABERCBrUhAKeBWvKu6JhEQAREQgc0Q0GtFYBsQUAq4DW6yLlEEREAEREAEREAEegkoBezloS0RAMRABERABERABLY8AaWAW/4W6wJFQAREQAREQAQuTGC7HaEUcLvdcV2vCIiACIiACIiACEApoCaBCIiACAAQBBEQARHYXgSUAm6v+62rFQEREAEREAEREAESUApICIBEBERABERABERABLYTAaWA2+lu61pFQAREQATyBOSLwDYmoBRwG998XboIiIAIiIAIiMB2JaAUcLveeV03IAYiIAIiIAIisG0JKAXctrdeFy4CIiACIiAC25GArjkSUAoYOUhFQAREQAREQAREYBsRUAq4jW62LlUERAAQAxEQAREQASegFNApqIqACIiACIiACIjAtiKwzVLAbXVvdbEiIAIiIAIiIAIisAoBpYCrgFFYBERABERgyxDQhYiACAwQUAo4gEQBERABERABERABEdjqBJQCbvU7rOsDxEAEREAEREAERKCPgFLAPiDaFAEREAEREAER2AoEdA3nJ6AU8Px8tFcEREAEREAEREAEtiABpYBb8KbqkkRABAAxEAEREAEROB8BpYDno6N9IiACIiACIiACIrAlCWzRFHBL3itdlAiIgAiIgAiIgAgMiYBSwCGBVDciIAIiIAITJ6ABiIAIrJmAUsA1o9KBIiACIiACIiACIrBVCCgF3Cp3UtcBiIEIiIAIiIAIiMAaCSgFXCMoHSYCIiACIiACIlBEAhrTxggoBdwYN71KBERABERABERABBImoBQw4ZunoYuACABiIAIiIAIisBECSgE3Qk2vEQEREAEREAEREIGkCSSeAibNXoMXAREQAREQAREQgQkRUAo4IfA6rQiIgAiIwIYJ6IUiIAKbJqAUcNMI1YEIiIAIiIAIiIAIpEZAKWBqd0zjBcRABERABERABERgkwSUAm4SoF4uAiIgAiIgAiIwDgI6x3AJKAUcLk/1JgIiIAIiIAIiIAIJEFAKmMBN0hBFQAQAMRABERABERgmAaWAw6SpvkRABERABERABEQgCQKJpIBJsNQgRUAEREAEREAERCARAkoBE7lRGqYIiIAIbEMCumQREIGREVAKODK06lgEREAEREAEREAEikpAKWBR74zGBYiBCIiACIiACIjAiAgoBRwRWHUrAiIgAiIgAiKwEQJ6zXgIKAUcD2edRQREQAREQAREQAQKREApYIFuhoYiAiIAiIEIiIAIiMA4CCgFHAdlnUMEREAEREAEREAECkWgYClgodhoMCIgAiIgAiIgAiKwRQkoBdyiN1aXJQIiIAIJEdBQRUAExk5AKeDYkeuEIiACIiACIiACIjBpAkoBJ30HdH5ADERABERABERABMZMQCngmIHrdCIgAiIgAiIgAk5AdbIElAJOlr/OLgIiIAIiIAIiIAITIKAUcALQdUoREAFADERABERABCZJQCngJOnr3CIgAiIgAiIgAiIwEQITSgEncq06qQiIgAiIgAiIgAiIQCCgFDBgkIiACIiACIyBgE4hAiJQGAJKAQtzKzQQERABERABERABERgXAaWA4yKt8wBiIAIiIAIiIAIiUBACSgELciM0DBEQAREQARHYmgR0VcUkoBSwmPdFoxIBERABERABERCBERJQCjhCuOpaBEQAEAMREAEREIEiElAKWMS7ojGJgAiIgAiIgAiIwEgJjDgFHOnY1bkIiIAIiIAIiIAIiMCGCCgF3BA2vUgEREAEROA8BLRLBESg8ASUAhb+FmmAIiACIiACIiACIjBsAkoBh01U/QFiIAIiIAIiIAIiUHACSgELfoM0PBEQAREQARFIg4BGmRYBpYBp3S+NVgREQAREQAREQASGQEAp4BAgqgsREAFADERABERABFIioBQwpbulsYqACIiACIiACIjAUAgMKQUcyljUiQiIgAiIgAiIgAiIwFgIKAUcC2adRAREQAS2JAFdlAiIQLIElAIme+s0cBEQAREQAREQARHYKAGlgBslp9cBYiACIiACIiACIpAoAaWAid44DVsEREAEREAEJkNAZ90aBJQCbo37qKsQAREQAREQAREQgXUQUAq4Dlg6VAREABADERABERCBrUBAKeBWuIu6BhEQAREQAREQARFYF4F1poDr6lsHi4AIiIAIiIAIiIAIFJKAUsBC3hYNSgREQAQKRUCDEQER2HIElAJuuVuqCxIBERABERABERCBCxFQCnghQtoPiIEIiIAIiIAIiMAWI6AUcIvdUF2OCIiACIiACAyHgHrZ2gSUAm7t+6urEwEREAEREAEREIEVCCgFXAGKQiIgAoAYiIAIiIAIbGUCSgG38t3VtYmACIiACIiACIjAigRWSQFXPFZBERABERABERABERCBLUFAKeCWuI26CBEQAREYCgF1IgIisG0IKAXcNrdaFyoCIiACIiACIiACGQGlgBkJtYAYiIAIiIAIiIAIbBMCSgG3yY3WZYqACIiACIjAygQU3Z4ElAJuz/uuqxYBERABERABEdjWBJQCbuvbr4sXAUAMREAE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jgI9C15XPea1GzEWhKNAz4LXVY957UaMBeEo0LPgddVjXjuRgY+aAgdKBR5b/9C4YNVZB4rZvbSPgK/r9WDrfyrq2cmjW82+yHA3W61miyV7Qj369287/NY3HX7Lmy6o/vfI2avYwVDu+8ClecdD6dk7GvZo1acIiIAIiECCBAY+agocSCkFNCAYc2060hUIYKD0Eevbz719keFusn9Dd5zr6twAQ/e1m/cxUDbfp3oQAREQAREYMoGhvvOPf2wDHzXFDaSUApJieyGQy1dcdOF2UF/IySLR51bfkdsk7khytXH27MHXvPrga/7qYNATH/1Qbqe75V27L37uj3fskuDvfGj3/xfnBwELD3hQ55i8E4/PR6LfiU9fdY9IPmrsbU3KF/hxXMUMv/LJO5q719zawP31/nqq9+E11zNPy8jG+o+vzav6iTTEQRwigaiaD+IQCUTdevOh53Om2BuJpYAGPh10V4Z8g5FoZr6XIYASfLiaucKVEny4mrnClRJ8uJq5wpUSfLiaucKVEny4mrnClRJ8uJq5wpUSfLiaucKVEny4mrnClRJ8uJq5wpUSfLiaucKVEnwMltrBOztWP36874BSpTJ3zb38/7pxtets8Etzc32Hzd79qnDMNa7hmLnc8XNXrxqv7NhhLIDBDOspZn68GbVtZsFxZWfBh6uZK1wpwYermStcKfQHTm8xboCbmStcKcGHq5krXCnBh6uZK1wpwYermStcKcGHq5krXCnBh6uZK1wpwYermStcKcGHq5krXCnBh6uZK1wpwYermStcKcGHq5krXCnBh6uZK1wpwYermStcKcGHq5krXCnBh6uZK1wpwYermStcKcGHq5krjAozatvMguOquBEBQDFKNLPguypuRABQjBLNLPiuihsRABSjRDMLvqviRgQAxSjRzILvuvY40ikppYBcnnFr5VaG3I+bXMmJTtDtGl/vxCMsonKq5OfPYh5onDrV109p5874uOZH8gU8Mne8BxlZQ7yv2/NverfeJ4fEhUCer3Xinz50MK5o/k1c1+QC56sPtv0QafsrxE9/7jODp2P3Q7ku9dPGSBC8UZwMNPd548Ltcz84iotPew5oPnAqEAKNPybRpxM29fNCHgnPE352DX7aFDRSqBTwAoxCMo4+BTw97wtyc3vGsc5y+E1/G/4ZP/93ATPn98788xf7ujn0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AtUzTz68TuLXgD4j4jGmz2GaIZ84E6URjwEHphnyiTNRGvEYcGCaIZ84E6URjwEHphnyiTOJRsHCm43LgNdl6BGcpSPmlARMIq6TpDzyzHsY9H2JDbp58rFAyIEcLAHreT2QgyVgPa8HcrAErB/o9WD3SmnvlARMIq6TpDzezHsY8GbULrt78rFEyGEYOOAQ5AwIMHIABBg5AAKMHAABlq4ccGqpbk5JQCOB/DFqeZ5nTA68BngN8BrgNcBrgNdAylwD4tRjiCXgoLIIZJfELCceIMMYEGDkAAgwcgAEGDkAAowcAAFGDoAAIwdAgJEDIMCGhsOgCp+hbcwlCTi0JNg6CZAACZDAYBFgOyRAAilPgBIw5YeIHSQBEiABEiABEiCBwSZACTjYRNmeCBmQAAmQAAmQAAmkOAFKwBQfIHaPBEiABEiABNwgwF66RYAS0K3xYm9JgARIgARIgARIYBAIUAIOAkQ2QQIkIEIGJEACJEACLhGgBHRptNhXEiABEiABEiABEhgUAoMkAQelL2yEBEiABEiABEiABEhgWAhQAg4LZh6EBEiABNKSAE+KBEjAWQKUgM4OHTtOAiRAAiRAAiRAAmtKgBJwTclxPxEyIAESIAESIAEScJQAJaCjA8dukwAJkAAJkMDIEOBR04MAJWB6jCPPggRIgARIgARIgAQGQIAScACwWJUESECEDEiABEiABNKBACVgOowiz4EESIAESIAESIAEBkRggBJwQG2zMgmQAAmQAAmQAAmQQEoSoARMyWFhp0iABEggpQiwMyRAAmlHgBIw7YaUJ0QCJEACJEACJEACqyNACbg6QtwuQgYkQAIkQAIkQAJpRoASMM0GlKdDAiRAAiRAAoNDgK2kNwFKwPQeX54dCZAACZAACZAACSQhQAmYBApTJEACImRAAiRAAiSQzgQoAdN5dHluJEACJEACJEACJJCUQC8SMGldJkmABEiABEiABEiABNKCACVgWgwjT4IESIAEBoUAGyEBEsgYApSAGTPUPFESIAESIAESIAESiBOgBIyT4FKEDEiABEiABEiABDKEACVghgw0T5MESIAESIAEkhNgNjMJUAJm5rjzrEmABEiABEiABDKaACVgRg8/T54ERMiABEiABEggEwlQAmbiqPOcSYAESIAESIAEMpuAUAJm+AXA0ycBEiABEiABEshEApSA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t954bbU1UeGtN16Hr6+v++3//ew3Pz+/D/vV+T/BVvBBfRoJZAoBnicJkAAJpCmBDJWA1UuW3HX7rRf+7a8PP3D/p5983N6P30yBqJr71ZdPPf7oJRf96/qrr0I8PJfE6KqqbbbdLjc/b97cr3DEiRMnw2+y6WY777obgqS27Q47hEKh115+uampMWmFRLKxsaGubpVdxdxhH5abqz80DQj+b5bYHelJgARIgARIgAScI5BxEhATaS8+9yzu9n743nuI+x6w3rZ++flnmBF88N5729vae6szWPl111v/iGNmFBUVffTBB7gLvHDB/FtvvH7LrbbeZ/9v93YI3NXdbsedWltann78id7q2HxBwaif/N8v/3LBhbDEd0eSBieceip2KS4uzslZzYcRUY1GAiRAAiRAAiSQ4gQyTgI+eO//nnr8Mcxmrf3AvPXGazddd004HF77plbbAu4+f/XlF1O32661tfWT2bOffOzRvnfZc5998vLz0cPPPp3Td81+bl20cAFqjh0/Hp5GAiRAAulNgGdHAplAILMk4KwP3n/7zTcGcVxxO/iF554dxAaTNoXZyofuuzcrOws3f3fbc6+NNt7klRdf+HTOJz0rd3R0fPzRrNtuujEUzDr4sO+gwl233bp44SIEa2lfz52HFiZMmAhPIwESIAESIAEScJ1AZknA2R/NGvQB++jD9we9zW4NPnTffV/Pm7vP/gcUl5QYY6YddVRuXu69d96Z+KifvR/9wP/u+fuffg/9BxVYX1+39TbbbrPd9i3NzTdcs7afXGxvb//8M51NXHeDDbr1LR1XeU4kQAIkQAIkkP4EMksCVi9eMuhDunRJ9aDcVk7aseam5jtvuwX3c6Hndt19D1unqKh4v28f2NjY8PorryDz2ssvr1y5AgFuEIc7wtvtsOM5P/rJ6MpKZKYdeeTGm26Kmjdfd+2qVbGvfSA/UHvt5ZdamluKioomTtIvowx0d9YnARIgARIggZQnkHEdzCwJiDuqbo3wp3M+nvX++zvtstvhRx2N+b9E57fbcccDDj5kr333RebRhx6Ahx16+BE///Xvph151Njx47AKCwSCx5986s677XbE0ccUFxcj0x977qkn//7HP/zngn9ecfF/rrzk4osvvOCpxx/DjrvvvXcgkFkXDM6aRgIkQAIkQAJpSSCzXtFLy8oGfRRxc3bohNFWU7c59yfnHzJtWjAY9Pc8GAzttudegYAmjz3x5KOPPf4vF1y448674AaxvxriQCBw8GHT7H8Bh9X+2OZbbtXU1LRsafWC+fPnf/N1dXU15CMU544779qf3VmHBJwkwE6TAAmQQIYRyCwJeMh3pm248SajK6sGy9aZMuWY408Y0mumasyYvtvffMstvzV1at91BrS1ckzVH//+jz//81+//8vff/fnv/3xb//42a9/C8Xpn4YcUIOsTAIkQAIkQAIkkGoEMksClldUnHz6GT86/2fdbI1XzzzrnMnrTEm1QR2U/kDwZWVnZedkB7tOQA5K42yEBEiABEiABEhgZAlklgQcWdY8OgmQAAmQQEoRYGdIIJMJUAJm8ujz3EmABEiABEiABDKUACVghg48T1uEDEiABEiABEggcwlQAmbu2PPMSYAESIAESCDzCPCMYwQoAWMguCABEiABEiABEiCBzCFACZg5Y80zJQESECEDEiABEiABjwAloIeBjgRIgARIgARIgAQyiUBmScBMGlmeKwmQAAmQAAmQAAn0SoASsFc03EACJEACJJAeBHgWJEACPQlQAvZkwgwJkAAJkAAJkAAJpDkBSsA0H2CenggZkAAJkAAJkAAJdCfglAQ03XvPdRIgARIgARIgARJIRmAkck4JFack4EiMJo9JAiRAAiRAAiRAAulHwDUJGE2/IeAZkQAJDAEBNkkCJEACw0zANYnimgQU1wAP8/XHw5EACZAACZAACYwMAcckinMSsF+32Udm6HlUEiABEiABEiCBzCXgmERxTQI6hjdznwc8cxIgARIYfgI8IgmMJAHXJIprEnAkx5bHJgESIAESIAESIIE0IUAJmCYDydMQIQMSIAESIAESIIH+EqAE7C8p1iMBEiABEiABEkg9AuzRGhJwVwI69r2bNRwf7kYCJEACJEACJJDSBFwVJO5KQMPfh0npZwQ7RwLDQ4BHIQESIIERJKDyz7WvgcRxuSsBRcBc0QsfJEACJEACJEACJDDcBCBCIEWG+6iDdjyXJaCIqkBOBg7axcCGSIAESIAESIAE+kkg6omQflZOxWqOS0BF6rIC1/6zkAAJkAAJDIwAa5NAChBwXn6kgQRMgcuAXSABEiABEiABEiABpwiknQSMikSjvDns1EU40M6yPgmQAAmQAAkMI4E0lRZpJwExL2tM5+15HTaIQhoJkAAJkAAJkIDTBEai81ZnGhG/tJA0eaSdBOw2LjpsIvQkQAIkQAIkQAIkMFACks6PdJeAPkyxXQAAEABJREFU6Tx2PDcSyCQCPFcSIAESIIFBJUAJOKg42RgJkAAJkAAJkAAJuEDADQnoAkn2kQRIgARIgARIgAScIUAJ6MxQsaMkQAIkkGkEeL4kQAJDR4AScOjYsmUSIAESIAESIAESSFEClIApOjDslggZkAAJkAAJkAAJDBUBSsChIst2SYAESIAESIAEBk6AewwTAUrAYQLNw5AACZAACZAACZBA6hCgBEydsWBPSIAERMiABEiABEhgWAhQAg4LZh6EBEiABEiABEiABFKJQGpJwFQiw76QAAmQAAmQAAmQQNoSoARM26HliZEACZCAKwTYTxIggeEnQAk4/Mx5RBIgARIgARIgARIYYQKUgCM8ADy8CBmQAAmQAAmQAAkMNwFKwOEmzuORAAmQAAmQAAmIkMEIE6AEHOEB4OFJgARIgARIgARIYPgJUAIOP3MekQRIQIQMSIAESIAERpQAJeCI4ufBSYAESIAESIAESGAkCIyMBByJM+UxSYAESIAESIAESIAEYgQoAWMguCABEiABEhhqAmyfBEggdQhQAqbOWLAnJEACJEACJEACJDBMBCgBhwk0DyNCBiRAAiRAAiRAAqlCgBIwVUaC/SABEiABEiCBdCTAc0pRApSAKTow7BYJkAAJkAAJkAAJDB0BSsChY8uWSYAERMiABEiABEggJQlQAqbksLBTJEACJEACJEACJDCUBIZWAg5lz9k2CZAACZAACZAACZDAGhJISwloYjCi0VjABQmQAAmQwDAS4KFIIE0IdAqJuLRIkxPT00g/CQjZB9NzE5OGA+adGB0JkAAJkAAJkMDQE+gUEpAWsKE/4jAeIY0kYEyqU/YN4+WT/FDMkgAJkAAJkED6EfAERkxspMPZpYEENGJ1eadUT4eB4TmQAAmQAAmQgFMEMqOzVmyo8PAUocsn7boExABEBc7lMWDfSYAESIAESIAEXCKgwiMqjusPxyVgNCJ8kAAJpAIB9oEESIAEMo2A4yLEXQkYFdyPt/OxmXbN8XxJgARIgARIgARGnABECKRI7ONoI96bAXdgcCTggA87CDsYAfpBaIdNkAAJkAAJkAAJkMAaEVApYtZoz5HfyU0JqKJ75NmxByRAAiSQ4QR4+iRAAkrATVnioASE2lbRrcxZSIAESIAESIAESGCECUCWQJyMcCcGfHgHJWB0wCfJHYaGAFslARIgARIgARLwCDgoTlyTgA4i9i4NOhIgARIgARJIDwI8i14IuCZRXJOADk609nKlME0CJEACJEACJJBGBFyTKK5JwDS6VHgqJOAkAXaaBEiABEggLQhQAqbFMPIkSIAESIAESIAESGAgBAYmAQfS8hDUdW2KdQgQsEkSIAESIAESIIFUJeCUUHFKArr5uzupep2yXyRAAiTQXwKsRwIk0C8CTgkVpySgOKWu+3WxsBIJkAAJkAAJkEDaEHBJqDglAV37unW6XNE8DxIgARIgARIggX4QcEqoOCUB+wGfVUiABEiABEiABAaDANtIcwKUgGk+wDw9EiABEiABEiABEuhJgBKwJxNmSIAERMiABEiABEggrQlQAqb18PLkSIAESIAESIAESCAZgeQSMFnNTMnV1Kz87NM5M999Z87HHy+tXpIpp83zJAESIAESIAESyCQClICx0Y5Go5B9l/z7wn/99S83XXvNvXfeccsN1/33Xxdg9aXnn2tvb4/V44IESIAE0pcAz4wESCBzCFAC6ljX19dddeklkH1LFi3SdV+pqVn55GOP/vdf/1yyeLEvzZAESIAESIAESIAEHCZACSh1dXVXXPzf+d983ccw1qxcedWlFy+cP7+POu5v4hmQAAmQAAmQQKoTwF27VO+iI/3LdAkYiYRvu+mGVbW1ifHaeNNNDz5s2mnf+/60I4/aepttA4EYora2tltvvL6pqTFRkwEJkAAJkAAJuE/ApTOYPWvWn3/3mwv++uev5811qd8p2deYvknJvg1Hp959++0F33xjj5SXn3/8yaeceOrpO++223rrb7DdDjseOePY7559Tll5ua2A+cLnn3naxvQkQAIkQAIkQALDTOCJRx9uaW6uram58ZqrqQLXEn6mS8BXXnwhQXD60TM23XyLxKoNJk6afOIppweDIbv61uuvt7W22ZieBNKBAM+BBEiABNwhMGbsONtZ3JrrqQJxj3hZdfW7b7913913XX3ZpRf94+9/+u2vf/2zn/7197/7zwX/vPbKy5949JFPZs/mDT3LMKMl4LKlS5cvW2ZBbLbFFptstpmNu/nKMVW777WXTXZ0dHzx+ac2picBEiABEiABEhhOAtOPPmb8hAn2iKoCr+0yFxju6HjskYeh/6ACMUe4YvkyTBlCFzY2NixbWj33yy9ffuH5W2+8/i+//91N110LLRiJRGxTmehFMloCLq2uToz6+htulIh7Bv6t/r161mSGBEiABEiABEhgiAjk5uWdeub3xoyLzwW2tt147dXffB37XGAoK+v4k07ecONNVnP0aPSzOZ9AC/7rb3/5YObM1VRO380ZLQEbGuoTIzs2fj0lMv5g7NixidX6urpEPLIB3tm8+Nxz9955B+YmR7YnPDoJkIBbBNhbEnCXQF5+/mnf/V5l1Rh7Cm2tbTdcM3AV6O28qrb27jtuu+bySzPzd98yWgJGImHvGlCXlZWti15KVnbn1nC4c69eqg9T2hgz5+PZM99959OPP+7/IXHFL1qwIKnVrFyJdtrb2vGMSmrYlx+hACIaCZAACZDACBIoKBh18ulnhEKxj+njBctTgfNsl0LeXODGm246urJysy222GmX3bbYaquEZLR1/H7e3LlXXPLfD997z5/MhDijJaATAwzJVVtT05tVjdG3Qa+89GJvFWw+HO5InOyLzz172X//ndSeeuIxVLvw73/9w69/kdT++Zc//eV3v128sPsPaGMvGgmQAAmQAAkMD4GW5pZ777rTfwcMKvDGa69J/MQvVOCJp57+o/N/ftxJpxwybdqM408876fn/+T/frnDTjsnfuvN39WO9va7br/1yccexe01fz69Y0rAVB/fB++794K//rk3e/vNN3AC33w9r7cKNl+9uPM/Ox4/cdKWW28NKy0tw74wxNYmT14Hq1Vjx4wbPz6pYSusoLAAnkYCJEACJOAUgTTpbHNT09VXXPLVl190O5/WlhbMBSZUYLetWC0rLz/siOk/+OF5xSUlWO1pLz3/3OOPPNwzn66ZjJOAUPqvvvTS7TffeN1VV7z+yquJcb3/f3cj05vdcPVViZqffvIxqt18/XXPPvUkLsREfkiDCRMnYk57DSzbdwvb9nCb7bY75rgTYPZrznvusy9iazvusivqnHrm987+0f9Lavn5BWJMQcEoVKORAAmQAAmQwDATwMzfrTde75/X8HdgtSoQlcdNmPD9H55XVFSEuKe9+tKLLz73bM98WmYySwJiovjyS/7z2MMPzp4166svvlixfFliUBd88w0yvZrv3UZdXR2qQQg+9/RT/7ngnzU1+vm5RDtDFOy+1z6Y007YCaecBjWGNzTfmX5UImmDY0882QbW9/ZeB/1cuGAB/DpT1oXvZh/MnPnma69ipt2fx93kpqbGwlGFwWDQn2fsBgH2kgRIgATcJ/DA/+6ZNzf2/d+kZ5NUBc585+33Z76buMkL/YeXUWNM0haeevwxvMQn3ZRmycySgC8+/2xvbx3WbFwbGuofe+jBNdt3bfZqaW5uaWl57eWXL/rHX59/5mlMbdrW2trarr/qyisvvrg/X26a+9VXxpiJkybbff3+tVdeeuj++zrC7f5kfZ1+gbqopNifZEwCJEACJEACw0Pg449mvffuO70dKy8/326CCrzx2qsXxP/rLyS33GrrD99777orr2hsbMAqbPzEiVO33Q5BUrv/nnuam5qSbnIv2XuPM0sCYvaudxRruOWrL79IvLFYwyYGuNvC+fM/mzPn8KOOOvMHZxcWFj3z5BOXXfzvpqZGCMFbb7zh63lzc/JyKioq+m515YoVmAQdO358bl5uz5qNjY244YuJRv+m+nr9NRy8efInGZMACZAACZDAMBDA3MdD993X24HwanjOj39SMXq0rYC7WDdce1VCBYZCoeNOPiU7J+feu+6yFeB32mU3+KSG17uHH7g/6aZ0SmaWBGxubh70wcN1NqQS8JDvTPvxz34+eco6zzz1RF3dKvT/3bffuvuO25YvW7rOuuv+8P/9dKup25SVlXd0dFx/9VVffv7Zuuutf8LJp2H1A+/nLk8+40zsXllVhR399tmnc7C6wYYbwve0xsaGgvyCbl+bql1Zg5rFxck/RYtNNBIggVQjwP6QQNoQeOmF56HMejudzbf8VklJyRk/OKu7Cpw/3+6iKvCkkxFj4gYeNnb8uOLiYgRJ7YP3Zi5elOY/f5FZEjDpMKd4Eu9sKkZXvv3GG88//fRrr7zcrbfZ2dlHHXvc9jvudNm//435v6nbbHvSaWeEskK33HA9ZOKjDz1QUlKK3UNZWd12/OiDD5DZbItvwXezttY2WHFpd6lXW+tJwJKSbvW5SgIkQAIkQAJDSgCzLW+81vkNzp7HqqjU+T+8Yp7+/bMSW7HXDddctaCrCjRiEhXw+piIewYvPPdMz2Q6ZSgBHRhNTPi98OwzuLL32nvfbt3FxPgjDzyA+78tLc3Tjjxq+oxjs7JV7c048cSqMWNee/nlpFPZS6uXzP3qSzR1zeWX/vPPf7R2xSX/RQZm32b1fG+0qlbnIDkLCEQ0EiABEiCB4STw6SezW1ta+jjiqFGFdmt+Xp6YTpGHV0mowIU+FThlvfVsTfi8gtjHBxH3tI9nzWptbe2ZT5sMJWCqDyWuv9tvvgk3dg845JCc3M7P7UXCUdwRvuiff3/91ZcnTZ78g3N/tN0OO9qTufHaa956/XXMh5dXVLz52qtPP/G4zSf8W2+8YeP29nbcHIfV1dXVr1KFh3x9vfe1j+Lus301NSuwtbS8FJ5GAiRAAiSQqgTSsF8bbbJZLrRd72fW1NRoN4ayssrLy21sPVTg9ddclVCBNml9zQp9XbNxT7/p5pvn5OT0zKdNhhIwpYcSk9i33HBd9ZIlW2y11VZTt7F9hW5DcM0Vl913910BEzjy2GPP+MHZY8aORdLagvnfvPfOO/n5Baec8d2CglGYQZz5ztt2E/yK5cvf9n5QGvGv/vDH3/3lbzD/hwWXLa3GJmjHqy69xG9zPtb/hu6uW2/91fk/wZSh/TQhatJIgARIgARIYEgJ5Obl7rSr/nJtb0dZvmxZYlPPHzuLqUDvp9AS1aLR6FLv9S6R6Rbsvufe3TJptkoJ2H1AcQP00GmHf/fsc6YfM2PsuHHdNw/velt7a15e/nobbHjEUcfgyNCCTzz2SELP7b7X3mf96MebbLJ5a0srxKJnzR/MfBfX+phxqgjLysuPPekkY8zHH83C7tb0117a222c1K9aVfrV+CoAABAASURBVGvzkJJ+s8lV3mThgm++iYQjNkOfWgTYGxIgARJIRwK77Lp7wahe/2OCOR/PTpz0fgcemPiBmEQSr4w3XHPloq4qcNPNt0xU6BZM3Wbb8RMndkum2SolYJcBHV1Zee5PfrbjLrtOXmfK1G23+8G552208SZdagzvSlFR8fEnn3LQoYe9+fprl/333xdfeMHLzz+f6MJLzz/3jz/94U+//ZXPfn33HbejAjoPD5uy7nqYIzz+5FMRw9rb2hcu+CYvP2/jTTfFalLbd/8D/nLBhbA//eMCvyED+/M//7W+9z3iglEFSXdnkgRIgARIgAQGnQBU3bTpR/bWLG5wff7pp3ZrYWHRcSedDG9XE765qfl6nwrE/MiRx8zATbZEhUSAYx142GGJVTeCgfeSErALs0OmHZ7r+528YDA07aij/B8s7VJ76Fdmz5p1wV//fMlF/3rikYdxfW+7/Q4/+OF50GGYpNx7/2/vuPMu2+2wYzfbbc+9Tj3ze5tuvkWid+tMmYIL3a5mZWdtt8NOh02bXpC/5gKu3vuBwMRnb23L9CRAAiRAAiQwpATw0rbV1G17O8TjjzzUEb/Hte5665/305/hhXLDjTYur6hITB92U4GBQODoY4/bbIst/W0Gg8FjTzwpfy1eJf2tpXJMCdg5OtBJmPzrXPcizMOVl6/mZ5a9ikPicO2WlZVvs+12J512+i9/94fDjzp6wqRJOBL6uc9++x96+BFjx42DNJw8Zcq0I4+ydsDBh9hZOlRLanvstc+WW2+ddFM/k/Wr9PsilID9xMVqJDAMBHgIEsgQAt+ZPn3S5HWSnmz1kiX3/++eaDRqt+J+F14oTz7jzP/381+c+5OfVo6J/T6upwKvWhS/IxwIBI85/oRNN9/c7oVJn+lHz1hv/Q1iq2m9oATsHF5cNy0tSX47unnk/pcYTNqd/v0fHHHMjDHjxs/96stl1fpFjc4ei6xYsfyrL7+oWbnSn7TxS88//9zTT+HOr11NeP80ZyKZCF596aVnnnqiN/vqiy/a2tqamhrx9gh9S+zFgARIgARIgASGgUB2djbmRBJ6rtsR35/57p233dLzhQ9zFmf+4Oxx48fb+s1NTddfc9WihQvtKqb9Zpxwov18FKZRvjV1qs2nvacE7DLE73v/o4Y/9dmcT5riXzX354c5RjduvObq1199Jdlxk+defP7ZZ596sr2jLfnmXrKvv/Ly808/3Zt99dUXtTUqN0vLy3ppgGkSIAESIAESGEICefn5p333+/aeWM/DfPTBB//51z+gBTs6OvxbMXNx2BHTQ/H/KKEZKvDqK30qMHTsiSfjVttue+zp3yu9Y0rALuP7zBOPz/7ww0Rq/jdf33f33YnV9AvavP8IBD4+cS7TjjrqhFNO682+tdXU5cuWg0N5xYjdHMfRaSRAAiRAAiKZy6CwsOjM7581dZvknwusram5547b//L739x20w2PPfwgZkPuu/uuyy/+z5WXXJz4sCDYQQXecPVVixfG/he4UCi07fY7IJ85RgnYZazxpuH2W2664uL/3H/P3ddeefnVl11qv/rQpVIarVzw1z//4de/gC2tXmJPa/0NNtxks816s9GVlUsW6cx5VdUYW5+eBEiABEiABIafAObzps849shjj8VN3qRHx+zGxx999OpLLz339FPvvv1W0p+GbmpqvP7qKxMqMGk7aZykBEwyuAvmz3/nrTfnfvllJJLmP30HSbfOlCnWJkyanIRFj9RXX+r/LJf2v5bU47xTL8EekQAJkEDGE9h66rY//tn/7bL7HpjDWzMYMRW4KDYXuGaNOLoXJaBLA/fm66/96vyf+A3vb3ACmOX2J23c0pzkqy2o7Lfvnn3OmWfF7LiTTvZvShovX7Z03tyvsrKzJq8zJWkFJkmABEiABEhgOAnk5uUedOhh//eb3x982LTRlbGv/fbdgaysrI022bS8YrStpirwqisXu6ICbacHw1MCDgbFoW8jlBXKzy/oabl5ednZ2fA9Nw1Wp76eN/fiCy+46tJLrr3i8qsuvTQajU7ddnscdLDaZzskQAIkQAIksJYE8vLzdt5ttx+d/7Of/OKXuDu83Q474gbX6MrKgoJREHylpWWTJq+z6eZb7LrHnqee+b1f/+FPJ512+plnnVUxuosKXLJ48Vp2w63dKQHdGC/Mdf/qD3/sab/5459/95e/wffcBF3Yx7kdccyMv1xwIYRjH3XspomTJtXUrPwGSvCrL9s72jbfcstvH3iQ3URPAiQwzAR4OBIggb4JlJWV4xVz2pFHnXnWOT86/+e//P0ffv/Xv//0l7/63jk/PP7kUw485ND1N9ww5H0vuLCw6IwfdFGB1115RUapQErAvq8lh7dCF/ZT5PV9koFA8Hd//tuf/nHBH//+j9//5e/HnnhyTm5u37twKwmQAAmQAAmkPoGeKvCu225N/W4PVg8zSwKWV5QPFrhEO3jDEQgMD8bEMUcgwDkGg6ERODAPSQIkQAIkQAJDRqCbCqyrqx2yQ6Vcw+mvXfzIt95mO//qoMRbb7PNoLTDRkiABEiABEggGQHmhpaAVYEbbLRRSUnJIdMOH9qDpVLrmSUBN99yy/0PPCgYDA7WEGy7/Q577LPvYLXGdkiABEiABEiABIafAFTgKWd89/xf/Wbrqcl/bnr4uzQMR8wsCQige+y9z/m/+vUJp5x25LH6k5Jr7I898eSf/N8vDz/q6EEUlOgejQS6E+A6CZAACZAACQwBgYyTgGAIsb/JZptB6a+NYUKxrHzwP1mI7tFIgARIgARIgAQyncDQn38mSsChp8ojkAAJkAAJkAAJkEBKE6AETOnhYedIgAQykwDPmgRIgASGmgAl4FATZvskQAIkQAIkQAIkkHIEKAFTbkhE2CUSIAESIAESIAESGFoClIBDy5etkwAJkAAJkED/CLAWCQwrAUrAYcXNg5EACZAACZAACZBAKhBwSgKaVCDGPpDA0BBgqyRAAiRAAq4TcEqoOCUBXb8y2H8SIAESIAESIAES8BMYudgpCeiUuB65MeWRSYAESIAESIAERoKAU0LFKQkYFYmijMSg8pgkQAIkMAQE2CQJkED6EIBEcUqkOCUB0+cy4ZmQAAmQAAmQAAmQwEgScE0CGqfmWFczstxMAiRAAiRAAiSQLgRckyiuSUCdZXVqmjVdLmyeBwmQAAmQwCARYDPpSMBBfeKaBITEhqXjxcNzIgESIAESIAEScJUAxAnMqd67JgEtXM4DWg70LhJgn0mABEiABNKMgJuyxE0JaNyEnWZXPE+HBEiABEiABEgABPojS1AtxcxNCSiGvw6TYhcSu0MCJEACJEACGUkgimkpJ7+r6qgEFMEdd4UufJAACZBA6hNgD0mABNKTAKQIBImb5+asBFTcTopu7TgLCZAACZAACZBAOhBwWIq4LAEVuxN3hNPhGuc5kAAJkAAJkAAJdBLA/J8Y/OvMuBa5LAGVdVScnYDV7rOQAAmQAAmkLQGeWFoTUPkRdfoMXZeAPvhuD4TvRBiSAAmQAAmQAAmkJoE0EhtpJAENLhZvZHRuFjGNBEaUAA9OAiRAAiSQNgRi0iIqKjbS5KzSSALqiHgjg7lZDFVU1/nbMR4FOhIgARIgARIggQESgJbAHpATCCAtEPdHAGo1N0qaScA4dAyVpwYFQTzHJQmQAAmQAAmQAAn0l4CVEJATNujvbs7US1MJ6Ax/dpQESCCtCPBkSIAESMAVApSArowU+0kCJEACJEACJEACg0aAEnDQUIqwKRIgARIgARIgARJwgwAloBvjxF6SAAmQAAmkKgH2iwScJEAJ6OSwsdMkQAIkQAIkQAIksDYEKAHXhh73JQERMiABEiABEiABBwlQAjo4aOwyCZAACZAACZDAyBJw/+iUgO6PIc+ABEiABEiABEiABAZIgBJwgMBYnQRIgAREyIAESIAEXCdACej6CLL/JEACJEACJEACJDBgApSAA0Ymwl1IgARIgARIgARIwG0ClIBujx97TwIkQAIkMFwEeBwSSCsClIBpNZw8GRIgARIgARIgARLoDwFKwP5QYh0SECEDEiABEiABEkgjApSAaTSYPBUSIAESIAESIIHBJZC+rVECpu/Y8sxIgARIgARIgARIoBcClIC9gGGaBEiABETIgARIgATSlQAlYLqOLM+LBEiABEiABEiABHolQAnYKxoRbiIBEiABEiABEiCB9CRACZie48qzIgESIAESWFMC3I8EMoIAJWBGDDNPkgRIgARIgARIgAT8BCgB/TQYk4AIGZAACZAACZBABhCgBMyAQeYpkgAJkAAJkAAJ9E0g87ZSAmbemPOMSYAESIAESIAEMp4AJWDGXwIEQAIkIEIGJEACJJBpBCgBM23Eeb4kQAIkQAIkQAIkIJSAIsLrgARIgARIgARIgAQyiwAlYGaNN8+WBEiABEggToBLEshoApSAGT38PHkSIAESIAESIIHMJEAJmJnjzrMWIQMSIAESIAESyGAClIAZPPg8dRIgARIgARLINAI83zgBSsA4CS5JgARIgARIgARIIGMIUAJmzFDzREmABETIgARIgARIwBKgBLQc6EmABEiABEiABEgggwhklATMoHHlqZIACZAACZAACZBAHwQoAfuAw00kQAIkQAJpQICnQAIkkIQAJWASKEyRAAmQAAmQAAmQQHoToARM7/Hl2YmQAQmQAAmQAAmQQA8ClIA9kDBBAiRAAiRAAiTgOgH2f3UEKAFXR4jbSYAESIAESIAESCDtCFACpt2Q8oRIgAREyIAESIAESKBvApSAffPpsjXa3h6uq21fvqRt8YK2RfNpJEACJEACJEACI09g8QK8NOMFGi/TXV62udIngbSUgH2ecb83tq9Y2jjr3drnHll297WLrvz7N//8+Td/++mC//xu0eV/W3zNBYuv/ReNBEiABEiABEhg5AlccwFemvECjZdpvFjjJRsv3Hj5xos4Xsr7/bKfcRUpAbsMebh+VcN7ry+/76YFF/120WV/WX7/zateebppzqz2pYujLS1dqnKFBEiABEggtQiwNyQgeLHGSzZeuPHyjRdxvJTjBR0v63hxx0s8AfkJUAIqjUhzU/3bL1fffMmCf/92xcN3Nn40M9ywSjewkAAJkAAJkAAJuEwAL+h4WceLO17i8UKPl3u86Lt8QoPW90yXgC3ffLX8gVvnX/CLlY//r2XeF4PGlQ0NPwEekQRIgARIgAT6JIAXerzc40UfL/0QAH3WTf+NmSsBmz77qPqWy6tv/G/jh2+n/zjzDEmABEiABEggLQms0UnhpR8CADIAYmCNGkiHnTJRArZ8/UX1rZcvu/OalrmfpsMY8hxIgARIgARIgAQGTgAyAGIAkgDCYOB7O79HZknAcMOqFQ/dUX3TJS1fUfw5f+3yBEhARAiBBEiABNaSACQBhAHkAUTCWjbl1u4ZJAHrZ7628LKNX8ElAAAQAElEQVS/Nrz/hlsjxN6SAAmQAAmQAAkMNQHIA4gESIWhPlDqtO+0BOwvxkhz4/J7b1r5yF3RVv6wS3+hsR4JkAAJkAAJZBQBiARIBQgGyIZMOPH0l4CY4F189QWNs2dmwnDyHEmABEggAwjwFElgCAlAMEA2QDwM4TFSo+k0l4D1b79UfevlHatqUoM2e0ECJEACJEACJJDqBCAbIB4gIVK9o2vXv3SWgDXPPLTy8XvXjg/3Tj0C7BEJkAAJkAAJDD0BSAgIiaE/zogdIW0l4IpH7qx77dkR48oDkwAJkAAJkAAJDCKBkWgKQgJyYiSOPBzHTE8JuOLB2xpmvj4c/HgMEiABEiABEiCB9CUAOQFRkZbnl4YScMXDdzR88FZajhZPigQylgBPnARIgARGigBEBaTFSB196I6bbhKw5qn7G97jL/8N3QXDlkmABEiABEgg4whAWkBgpNlpOyEB+8u87vXn6954ob+1WY8ESIAESIAESIAE+kcAAgMyo3913aiVPhKw6dOPap5+wA3q7CUJkAAJkMDqCbAGCaQWAcgMiI3U6tNa9CZNJGDHqpq0vE+/FiPLXUmABEiABEiABAaZAMQGJMcgNzpCzaWJBFz52D2RpoYRYsjDDg0BtkoCJEACJEACKUYAYgOSI8U6tYbdSQcJWPfac82fz15DANyNBEiABEiABEggdQikfE8gOSA8Ur6bq++g8xKwfdmSmmceXP2JsgYJkAAJkAAJkAAJDAYBCA/Ij8FoaSTbcF4C1jz/yEjy47FJgAQGlQAbIwESIAEnCKSB/HBbAjZ98kHznFlOXCvsJAmQAAmQAAmQQNoQgPyACHH6dFJKAg6Y5KpXnhrwPtyBBEiABEiABEiABNaagOsixGEJ2PD+m22LF6z1CLIBEiABEiCBkSXAo5OAkwQgQiBFnOy612mHJWDdG897p0BHAiRAAiRAAiRAAiNAwGkp4qoEbJr9XvvSxSMw2jzkoBNggyRAAiRAAiTgJgFIEQgSN/surkrA+pmvOUqc3SYBEiABEiABEpB0QeCuIHFSArZXL2qZ+1m6XDw8DxIgARIgARIgAVcJQJBAlrjYeyclYONH77jImn0mARJIEGBAAiRAAmlDwFFZ4qYEnP1e2lw3PBESIAESIAESIAGnCTS6KUtGRAKu1UC3zv+qo3blWjXBnUmABEiABEiABEhgkAhAlkCcDFJjw9eMexKw+fOPhw8Pj0QCJEACJDBoBNgQCaQtARfFiYMS8KtP0/YK4omRAAmsNYGcSeuWf+f4Maec52+paKe9Svf9jj/DmARIgAQGkUCzg+LEMQkYaW5sW/TNII4Zmxo+AjwSCaw1gfJDZ1SdeHZyO+mcsWeeX7r/4WNO+uGob20PIViyx4H2gIXb716827ehAqtOOidUWm6T3Twql+5z2ECt4vATRx99evkhx2SVV3ZrkKskQAIZRQDiBBLFrVN2TAK2zJ/nFl/2lgRIYBAJ5ExYJ3fKhsltnQ2yx04o2nHPSEuzPWLBVjsgKN1/Wul+0wK5eWJM7jobFO24F5I9rXDHPYt22WegVrDFtvkbbzlq6s5ZFVU922SGBEhACWRMcU6iOCYB2xZzCjBjnkw8URIYOIFIc8OqV562+4WKS8eeeX7RDnuaYBCZaLij/s0XVj7+v4LNp2J1cC3a0TG4DbI1EiAB5wg4J1Fck4BLFjh3TbDDJJDJBAb33Bvee6Nl3uftK5bWvfZc3avPWmv+fHbiKM1ffFL3xvPtSxfbTPbYCZj8QxxurIf4W/nk/bgXXDFN794i6bdwXU3HqoFZpLkp0UK4qT4RMyABEshMAm2uSRTHJGDH8qWZeWHxrEmABEAgOKo4d9K6WeWV+Ztt3VG7oubZh5rmfJgzfh1sgkGH1Tz7CIJVrz4b7WhHYK114dfVN1/SMPN16D/cC5ZAAHdvEdut1i+64u8L//v7AVnrN1/ZfeHbFvPdKTDQSCCjCTgnUYZUAg72pRCNtq9YNtiNsj0SIAFnCITKRosYEcFN3rIDp5cdcET5YTMC+QXIwOrfejlcV4ugcdbbDe+/icBasKDQZOWM/d7PVf95qfblS2qff9QLO13ViWdV9fZdk2T5rKpxdmfeBbYc6EkgwwmoRIlGHYIQcKivHfrH3SW4DrFlV0nACQLL7r521ctPxmb4AsHC7ffIGj3W9hy3g1e99KSN4Vc+dk9L/H8SD5WUjTnl3Oy4Ymv5+otFl/+tdf5cVPNb7jq9fNGkly+goFm7ezTMDwJaEgnPgAQyk0DUEyrOnLtLEjDcsMoZruwoCZDA0BCoffGJ5Q/ehrvA/uYxD2dCWd3m8FABeXgYtsJLJNI4e2b1TZdoPHglGu686Tx4rbIlEiAB9wi4JVRckoCRpkb3LocM7DFPmQSGmkBHhwmG/AcxoVDSX4pB3l8t3NLYc/IvUaFl3meYOOy/RZob7L7RNkpAS4KeBDKdgFtCxSkJGP+5r0y/xHj+JJDBBIp33bd82onBwmLLoD83YRNaLZhfWPbtIyqP+15Wsl9yrr7l8upbLuu/RZpjP0AYbW+znaEngYwmwJMXSfwuqRMwXJKA0XDYCabsJAmQwFAQyNtgs6qTzinZ65BATo5tv6N25crH7rE/DdOHX/7A7YmfiRFj8tbfdMwZP6k4/CQoQrQz9ns/H3/e79fAgkWl2B0WKi5L7F520FHI0EiABDKTgFtCxSkJGKEEzMznFM/aAQJD2sWcSeuOP+fXlTPOzF1nA2g4eyzMvXXUrijYfJvscRP7tqId9ww31kd8P+MXyMkt2GKbyuO+i6ZCRSWh4tI1sMRdZpOdndg9WFCINmkkQAKZSSDqlFBxSQKagEu9zcyrn2dNAkNBoPWbr9qWLkmIP/0N55oVJisbijDpRwCTJgN5+SsfvbujZrntYfuKpdW3XGrjwfRO/STEYJ442yIBEhBxS6gMiqiS4XnY/+VpeI7Fo5AACaQUgWV3X9u6YF6kpaVh5usL//t7zOqtQffq33114SV/qn/nlXBDXe2zD0daW9FIy9df2K+ARNv0I32QhnY14aE4Uc1aorLd2jr/K/E0X7iu1mbali6yNelJgAQykIBbQsUpCZidm4HXE0+ZBEjAEqh94bElN1y04pE77ar1uL1rtVdvvmNl5+/J2w8RrnzsngUX/aZpzoe2hWV3X1fz1P24gYv7uchklVcGi0qaPp1VHf9qSPvyauStRVtbEvlVLz8VKi6zc5PYRSSKTf7fJrS7pLXnyZEACXQhYJwSKi5JwGD8/wDowpsrJEACmUGg5atP25d1qjF70pHmRgivPqxtyUJbE97k5MH3tGgkHG5ulEjEboIKLNt/WsX0k61ktEnrc9fbpHDb3RCX7HnQ6KPP8JQf1iTS3ND8Wed/VawpFhIggcwj4JZQcUkCBvL5OetUej6xLySQAgRMVnbpPof1YcGi2M/HoLPR1tjPuCD2G5Rl9U2XLH/g1s4vDgeCBZtNrTrxHH81xLjLU7LXQWNOPa94928HcmP3JXA7uPrWK+refBEVaCRAAplMwC2h4pIEDBXHfoIhky8vnjsJkICfQLCwuGiXffqwnAlT/PX7iBs/enfRlX9vmjMrUSdUWlF+6IysiqpEBkEgLz9n4roIYNG2Ntz5XXLDf9sWL8AqjQQyggBPsncCbgkVlySgCYXcgtv7RcItJEACKUpg2d3XrnrxiWibflMEU4yjtt6ptz874Ya6lY/fU/vCYyl6JuwWCZDA8BLA3woT6vIfFw3v8Qd8NJckIE4uVN7l7TgyNBIggeEkkCLHalv8TcvczyDCbH+i4TBWe5r9ui7q2G/sRryvAGO1DyvZ48D8Tb9l/45bn6gcaW7wfzXEhLID/IBygg4DEsh4As5JFMckYHbVuIy/xgiABEhAVj5+b/UtlzV/8YllYYLB1gXzkPFbW/Ui+3Vd1Gl473VsQtCbBfMLyw6cPv7c3xbvcUDW6LESCMZqRqN2RhCrCJfdc11HzQrEsEBubsneh46efkqotByrNBIggQwn4JxEGZAEHPnBzRk3aeQ7wR6QAAmkBoGaJ+9N/I+c+Rtv2a1T+RtuZjMQbbUvPmHjnr5kjwPGnHruuB/+unC73UMlnWIu0tLS8tWny++/pWX+3MRe7cuql95+Zev8r2wG0jN/s63HnvHTisNPyJkU+4Cg3URPAiSQaQSckyiOScDsCetk2iXF8yUBEuiNAG7sti36xm7NqqjK2yCm+ZAp3v2AUNloBLCG99+A780Ktt4pZ+J6gZzY13tRLVy/qu6N5xf+97fVt17e+NG7yPitfcXSJTf8t+7NFxOzg4G8/IItthtz8rnjz/t95XHfy528vr9+WsQ8CRIggdUTcE6iOCYBQ8Wl+EO/+nFgDRIggbQjMPqo06tOPLubBXLzYydqTOk+hya2Fm6/q81H29tz19kgkU8E5YfMsBXqXn5K7H/rGY22L1tS98YLKx66I7tqXNnBM0r3/U7pPocF80fZmn5f8+R91bdc3jLv88SvCeKmM/5ABUeVtHz9hb8mYxIggUwgAHGCvwBunaljEhBwc6dsCE8bPgI8EgmkBoHcddbF07+bZY+bmOhdVuXYxNZg/GdETVZWIukPcuK3FOrffbVpzqymT2ctvfOaRVf8reap+5u//CSrYkzB5lOLdt67aJd9ssdOsIeItrXbwPrWhfOqb750+UO3tS6YGw2HkexYuWzpnVchoJEACWQaAfx5ce6UHZSA623sHGV2mARIIJUJLPvfDcvuurb5887/3qPpk/e7dTga7miaPbNbEquNH76z5Pr/VN98SdPH7696+alwXS2SNBJIBwI8h4EQyHVQnLgnAfM33DyQl/x/eRrIYLEuCZCAYwRa5n3V82df1jiDObw+zr9x1kx/y82ffbTiwdtrnn24t11a58+Fjmz44K3eKjBPAiSQxgQgSyBOnDtB9yQgEOdvvBU8jQRIYOgIpGDLy+65rvqWywbLVjx8Zx/nCIHoPxDuEff8Xkgfu3MTCZBARhFwVJY4KQELttgmo64tniwJkAAJkAAJkEDKEnBUliSVgCkLOdax3HU2yOJvRMdgcEECJEACJEACJDBiBCBIIEtG7PBrcWAnJSDOt3DqzvA0EiABEiCBwSPAlkiABAZMwF1B4qwE3HbXxI8+DHi4uAMJkAAJkAAJkAAJrDUBSJHCbWO/QrrWjQ13A65KQDGmaMc9hptWeh+PZ0cCJEACJEACJDAQAipFjBnIHilU11kJKFK0097BgqIUYsmukAAJkAAJkIBzBNjhNSUAEQIpsqZ7j/x+DktACQaLd99v5BGyByRAAiRAAiRAAplHQEVIMOjuebssAUUKt9s9Z/xkd+mz5yQw4gTYARIgARIggTUgAPkBEbIGO6bOLm5LQHAs2ftgeBoJkAAJkAAJkAAJDBsB1+UHQDkvAXOnbFS0A78XgqGkkQAJkAAJkAAJDAcBCA/Igmp8UwAAEABJREFUj+E40lAew3kJCDil+x+eVTkWAY0ESIAESKAfBFiFBEhgzQlAckB4rPn+KbNnOkhAMab84KNTBik7QgIkQAIkQAIkkLYEVHI4+0Mw/lFJCwkokjNx3bIDpvtPjHGvBLiBBEiABEiABEhgjQhAbEByrNGuKbdTmkhAcC3cfvei7XdHQCMBEiABEiABEuhOgOtrTQAyA2JjrZtJlQbSRwKCaOkB0/M33gIBjQRIgARIgARIgAQGkQAEBmTGIDY44k2llQQEzdFHnpY7eX0ENBIgAT8BxiRAAiRAAmtMANICAmONd0/NHdNNAkogMPqYM3MmTElN3OwVCZAACZAACZCAWwQgKiAtIDDc6rbtbR8+7SSgSCA3t/K470Ow93Ha3EQCJEACJEACJEACqyUAOQFRAWmx2prOVUhDCYgxwFBVnXg2btsjppEACZBAphLgeZMACawVAQgJyAmIirVqJVV3Tk8JqLRxR/joM4r4HWFlwUICJEACJEACJDAwApAQo48+I/3u/yYopK8E9E6x9IDpZRn7e4EeAToSIAESIAESIIGBEoB4gIQY6F5u1U9zCYjBKNx+9zGnnpfF/0EOLGgkQAIkQAJpT4AnuHYEIBggGyAe1q4ZB/ZOfwmIQciZuO647/28aIc9ENNIgARIgARIgARIICkBSAUIBsiGpFvTLJkRElDHzJjSbx9RdeJZOeMn6yoLCaQpAZ4WCZAACZDAGhCAPIBIgFQQY9Zgdxd3yRgJ6A1O7pSNxpz+/8oOnB4sKPISdCRAAiRAAiRAAhlNAJIAwgDyACLBXRBr0PPMkoAWUOF2u0/40e9L9z4kmF9oM/QkQAIkQAIkQAKZRgAyAGIAkgDCINPOHeebiRIQpy3BYNGu+034yZ/KDjwyq2qcZlhIgARIwFUC7DcJkMDACOClHwIAMgBiAJJgYDunS+1MlYB2/Iwp3G63cd/7edVJ54zaeqdAXp5N05MACZAACZAACaQfAbzQ4+UeL/p46YcAyJyP/SUdysyWgHEkuetsUH7ojInn/330jDNxTWRVVMW3uLBkH0mABEiABEiABHongJd1vLjjJR4v9Hi5x4t+73UzaAslYJfBzt9wc8wMjzvrl+PP+/3o6acU7bR37robh4pLu1TiCgmQAAmQAAmMOAF2oHcCeOHGyzdexPFSjhd0vKzjxR0v8b3vkYlbKAGTjzqunvzNti7d7ztVJ/wAV8+kX1447uxfjzn1vNHHnFFx+Anlhx1bdsgxNBIgARIgARIggREngBdlvDTjBRov03ixxks2Xrjx8o0XcbyU4wU9+St9xmcpAft1CZhQKKt8dM7EdfM32qJgi+1GbbVj4dSdaSQwggR4aBIgARIgAUsAL8p4acYLNF6m8WKNl+x+vbRnfCVKwIy/BAiABEiABEiABEjAEQKD2E1KwEGEyaZIgARIgARIgARIwA0ClIBujBN7SQIkQAIiZEACJEACg0aAEnDQULIhEiABEiABEiABEnCFACWgKyMlwp6SAAmQAAmQAAmQwCARoAQcJJBshgRIgARIgASGggDbJIGhIUAJODRc2SoJkAAJkAAJkAAJpDABSsAUHhx2jQREyIAESIAESIAEhoIAJeBQUGWbJEACJEACJEACJLDmBIZhT0rAYYDMQ5AACZAACZAACZBAahGgBEyt8WBvSIAESECEDEiABEhgyAlQAg45Yh6ABEiABEiABEiABFKNACVgqo2ICHtEAiRAAiRAAiRAAkNMgBJwiAGzeRIgARIgARLoDwHWIYHhJUAJOLy8eTQSIAESIAESIAESSAEClIApMAjsAgmIkAEJkAAJkAAJDCcBSsDhpM1jkQAJkAAJkAAJkEAngRGMKAFHED4PTQIkQAIkQAIkQAIjQ4AScGS486gkQAIkIEIGJEACJDBiBCgBRww9D0wCJEACJEACJEACI0WAEnCkyIvwyCRAAiRAAiRAAiQwQgQoAUcIPA9LAiRAAiSQmQR41iSQGgQoAVNjHNgLEiABEiABEiABEhhGApSAwwibhyIBETIgARIgARIggVQgQAmYCqPAPpAACZAACZAACaQzgRQ8N0rAFBwUdokESIAESIAESIAEhpYAJeDQ8mXrJEACJCBCBiRAAiSQcgQoAVNuSNghEiABEiABEiABEhhqApSAQ01YhEcgARIgARIgARIggRQjQAmYYgPC7pAACZAACaQHAZ4FCaQ2AUrA1B4f9o4ESIAESIAESIAEhoAAJeAQQGWTJCBCBiRAAiRAAiSQygQoAVN5dNg3EiABEiABEiABlwg41FdKQIcGi10lARIgARIgARIggcEhQAk4OBzZCgmQAAmIkAEJkAAJOEOAEtCZoWJHSYAESIAESIAESGCwCFACDhZJEbZEAiRAAiRAAiRAAo4QoAR0ZKDYTRIgARIggdQkwF6RgJsEKAHdHDf2mgRIgARIgARIgATWggAl4FrA464kIEIGJEACJEACJOAiAUpAF0eNfSYBEiABEiABEhhJAmlwbErANBhEngIJkAAJkAAJkAAJDIwAJeDAeLE2CZAACYiQAQmQAAk4T4AS0Pkh5AmQAAmQAAmQAAmQwEAJUAIOlJgI9yABEiABEiABEiABxwlQAjo+gOw+CZAACZDA8BDgUUggvQhQAqbXePJsMo9A+7Il7SuWDvV5RyPh2hcea/nqU4lGh/pYXdqPRLqspsVK05wPa559uOmTDwZ0NpHmpro3X4x2tPexV+OHby++6h91rz3XRx1uIgESIAFLgBLQcqAngdUQSM3NkAVL77h68ZV/X/HQ7ZHmxm6dXHjJn77+43mwhf/9fcKwCmuc9Xa3yn2vNn/+8aqXnlx+/y19V0u6df4/f44j9rTaF5+w9RveewNbl993k13t9NHooqv/ufjqf7Ytmt+Z9EXYNDC79kLf3iMWNn86q+7VZyAEB9SDZffeWPPkfQv/8/u6V5+FIk++bzDYVr1o1StPhhvqkldglgRIgATiBCgB4yS4JAEHCZis7IItt5VAoOH9Nxde+uf6t170n0RH7Qq72rGqJmE2E2lptkE/fdPHOmWVt9EWYkw/d/FXC+aPyl1ng4TljJ/s39pbXD/ztfali8ON9VmVY5PWaVuycGC2OLmUROPRcLjhg7eW33fzkhv+W33TJcvvv3WgEg2NDKmV7n1o3vqbhJsaa559aNGlf2n65MOehyvYbGrOxHUjLS01Tz/Yc2vPTLh+1Td/+yn09/wLfmENMWz5A7fayg3vqzpHxm6FRwyrvuVyW4GeBNKeQBqfICVgGg8uTy39CZhQqGTPg8b94Be5626EGcGVT9y38tG7E6edPW4i4tHTT5n4s38kDBIByd4M9xkhILoZRFjTxzOxS/4mW3bb1NsqKvsta8z4qpPOSVjpAUf4tyaNMY+16vnHsAkniNNE0JtNPP+vibPrLRhz2o962x15zHEuuvRPKx68rfGjd1vnf9Xy9ReYJV1293VLrv9Pz7lV1B8Rw2hWHvf9yuO/l1VRBXG//P6bO+pqe/akdN9DkWyc/V5HzXIEfVs0Eom2651lXDzWbP1oW0ssaG+zgd0KH1ttHdhbCLsXPQmQQEoRoARMqeFgZ0hgTQiESsqrTjir/DvHh4pLC3faq7MJ74N0gYJRgdzchJmsrM4KPaIVD9+J+7bdbMGFv8YkGeouve3Kbpt6Ww03NaB+N2tfXl332nP9kSbYERNyaASCddTWO2G1DzPZnWeXOM1ugcnK7a2FujdfXHrXtZglDZWNLt5t/4rDTyw76EhIatRvXTB32T03IFgD66hd2fLVp0mt1ZuMbFu8IOlWJLGptyPmrbfJ2O//vGjnfYp3PyBUVNKzGohlj5+UXTUWGrrn1qQZXDaTf/tfa2UHH92zTv7GW9it8JUzvtuzAjMkQAIuEqAEdHHU2GcSSEJg1Le2H//D32aVjU5sCzfUIw4WFML304KjCiEIulli3275PlaNSfK3pXX+3JpnHuztg32JoyCoeer+lnmf4/ZxxfSTsTqEFok0z/lAIpGiXfYdd9YvSvY6uGCLbQu33Q2SetTWO+K46EYfggwVerOG916vvvXypIa729irffmSpFuRrHnyPlTozUwgWLrvYcW77gvxuuS6i3qadHSgTs1TD3TbhLvbvbXJPAmQQAYSSPJnOgMpJD9lZknAOQKBzmd0+4ql4fpVJjsb9w37fx6l+00bf97v/Vayj95YDBYWTfr1Rf5833EgL7//B+1Ws+7VZ+reeEECgfJpxyed6OpWH3cte7sfnchH22N3Nrvti6NUnnB25YwzS/c5FLLJv7V4t2/b1dYFc20wIB8sGJVVXpnUEu0k3YpksLgsUaePoGPlstaFX/e0tupFPZPItFUv6KM1biIBEsg0Ap0vGJl25jxfEkgDAuFGnedLeiKrXtLv2+ZO2VC6foHDhPRGcKS1tXMv735x56ovikbCtd4H8op33a+bQvLVGsyw9vlHap59BC1CgeWtvymC1dr8C37Z2/3oRH7J9f/prR0TDOZtuFnPrao+PXTRttjn4XrW6SNTuP0e487+VVLDfC12xHRj0q1IVhx+Air4zX5iz59BjNvBY049r5vlb7oVNgVHFXfLY7Xi8JOwibZ6AqxBAplBgBIwM8aZZ5mOBNpXLlt06Z+W3PBf3Kzsdn64Rdg4aybEX/HO+3TbFCopR6b2hcdj8ugfP//6rz9FU0j2tFUvPNZRsxw3fAs23zYxo5YkaG6MNDcllSk92+wj0/z57FUvPy0SHbXNLiV7HNBHTbspZ/zkAdo6dsf++OYvP7E/gpg9flJ/6g9hnUhkyc0XL77mgqaP3/cfBeOSM3HdbpZdOQ51sioqu+Wxml2lm7CVRgIkQAIgQAkICDQS6CTgUNSxvNpk57TO/6r65kurb73CfsIs3NSw/P5bvQ+TRYt22AMv/N3OqGjHPfU3ViLhmJJrbZFoJJJsNrFtycJVrzyD3TtW1cy/4BcxyfjPnycJLvglKiy94ypUXhvL22Czwh32KNxut/JkX0ro2fKY0//fwOy0vr4X7G8f06srHrkLGUyj5q6zAYIRNNzTjzQ1tS1esOx/Nyy+7sKWb74cwc7w0CRAAmlDgBIwbYaSJ5JxBPI23Hz8Ob8t2nlvk5XV8tUcnSWaM6tx1ruNs97G/B/0X+n+h/eEEiopG/f9/5v0q4tiv5/y839M+tWFPZVitK11+X03292DhcVQjX2YrbYGHhOHqkTb9K50pK0VcckeB5XsdQiCbhaNhNeg/TXbBTJ66a2Xh+tXYZqt4vAT16yRQdwra/SY8Wf/qvTbRwTyRrUt/Kb6pktWvfTk4LRv/68X0/sLQez/glmTH4McnB6yFRIYLgIZeJzen/kZCIOnTAKuEYD4K933O5B0mKkKFBTmrbshlF/x7t+uOuEsKIY+zsYEg7GfTcnJTfIhv2h02f9ubF++xLZQtPM+OEQfVv6d423NgVUHzBgAABAASURBVPrqmy/BnKI3ZynNn81G3Js1fvC2bbxpzqwFF/22i/3r111Wu23tZRVznLbBbh53tKtvvKStehG0ctXJ5wZHFXWrsNpViFfMm/Zh4Ub9xRxMNPZRB5ugRDuPFQhgZMf/8DejttrBBEN562/SuWngUUftim/+9lPYwov/gL0Tq8jY35UEZMSwlU/ciwpNcz5EbG3pnVcj07boG7ua5P90wWYaCZCACwQoAV0YJfaRBPokECqtqDrpnDGn/Rj3hVExq6JqxUO3J/5HuN6Cr/94Hu4q4vYiduli0Sjm/5q/+DiQl589dkKXTWu3EpvJCwYTzQSLikPFpYlVxCYUsquI/RbIif2wX7SjLdxY18Wa6sMNq9S65ftclbB/WtEeUyQSWXrH1dC+OHTVST+ECoxvGMCy5tmHemNu82CL5lq++tSu9uaX/+9GVPMbVHv5YceNO+uX2ePW7uOJUYl2dMASjSO21i3TbRV1umeSYkxUYkACJJDCBCgBU3hw2DUSGAiBUFHsh4IjrS2YQ1qtoe326kXBwiIECcOd2aV3X9s4eybmF0cffTqUEDZhlk4/bnjzpb35FQ/ehmqrtWibfrU2kJuXqDn6qNPHn/f78kOPRaZg86mIJ57/dxPKChYUIvab/aKrV22byb/5T6f96iJIVeTRSGfSX6GXOOmXPOrefrl1wVwTCo2e8d0103/oSSAUghZPathqLenWnklbuZv3d6zuzRcXX3uh32pf0P9SpWXe5/4k4mX/6/yN61BpuWU1xvtwZM6EKXYV3v40tP4WtMetxPtSzqipO2OTNfvT0NCgdnX0Uad16x5XSYAEXCFACSjiylixnyTQJwHMsdn/MWzUVjuO/+FvxfuNQLzGxz7z97PO/yMOGbzqo7H8jbf03+jEPdDFV/+z+dOPMOU2evopuZPXRx1rkBR9mK2zWh/1PvMXyOmUgD13gfTMGTcp3FiPw/Xc2jPT9NlsdBuiLaERe9bpf8Z+5TZvw83X5suzuAU/6f/+2dNK9z0MPTHB4NjTf9Jza89M5YwzUT+p4T6szeMeLmK/2Ty8P4kYch/JbhauW4VMoKAAPqnZe9bBglFJtzJJAiTgNAFKQKeHj50ngU4C9W+/suSaf7UunAeRgWke++Nztc8/FvD973A2bprzgc51ZWUV797lh1cCefk5k9bFJFPVKT+CDEo0XbLnQVCNfVjZgdMTlfsIIOywNTiqEL4Ps2KuYeZrfdRJbGr86B3EOZPWD8TvFGN1jQ1qEvtmjR7z/9m7Dzgp6vv/47Plbm+vcXAcSAdFEARFxYa9YG8x0ZhomskvURNjkr+maDTF9BiNiYkptiTWJPbeRY0KKoKAggoK0utxvWz5v/fmWPbudu/2bndnZ2ZfPL63TPnO9/v5PL8IH2du9/Sa3aaRt8+JfVJjxcxDu92AbFq6qPmDd9OfTkNtuP3PetavSyoPPGL4Fy5ObLqBp+O+0vLEg9pO+r4W3SpWZ3/qT6IO1df23kFnaQgg4FABSkCHLhxhI9BFIBoO1736nGqscH29eaLqmFN9pRUtH77X7d2j7Vs2bnvqfvUZdMjsoqHDtZHY9BxwxP9dVjx8ROJBT6CPH8LrKe78Rr3Eq3put2/d5A0EfBWDep5KPFK294G6F9i0bFG4qTOXxLOJ25HWVvO+XcuKpSuv/lZv7aeXrPzpJR//+nuJl/fcrj7tHD3oLJu2X89TGR7Z8ug9Wh3/4KFal8ShGua/uunfN22+97a2DesSj/eyXfvC45GW5lDtVvXxV1WXjJuY2Ip3GaPjRcNGJB7Uth7d6ni3FqrbpiMaRK9JW6TjZwz6q9L6aSVJR+AgAgjYVoASsOfScAQB5wnUz50TrqvV48vSPaab0ftKy4ec+mnD46md83jjotitMh1X3bDhX39SAREYs+ugQ2frSLfm8fp0L7DbwQx3g7tPVwliRKNt6z4uGjZKo/nKB5Xvc5Ae+Gq7Z1OZWLrHjGh7+/bnH+95NvFI83uLzF1fRaW/sqqXtqNblbmR6jUwekJw0p5F1cNSdRjY8aYlb6lU1d3ZoWec6/HHfjpLfJzyfQ8u3XOfSGvLpnv+FmlujB9PtaH7dg0LXtWyVh1zSqo+6R8PbduszrpnrNekLbS94y7gkKFJz3IQAQQcLUAJ6OjlI3gEYgKR1ta6V57VVmXXnwVSOnn6oMOOU+215aG76ue92LZ21YZ//EGVYtHQXYZ95mvmNwvqqj7btifvW/Xz7/TSen87yNBPnKcwmt9/R08wi6pr1t30u7Z1q6tP/Uxw96mppq466kQVTA0L5/V+b6xs+sxxV12vNvrbVye+d6TnduVBR2ou/5DYT0bRhpVN4Fse+49mrDryJFXe2ujWhp5+rmp3Vecb77lJi9XtbLfdbU/eq+I4uPuegVH9+Ekn3QaJ75rfIBgYNS5+JHEj3FAXbtjuLSnxd/xEmcRTLt0mLQQKS4ASsLDWm2xdKbB9zmPhpgaVESqJuiWosqNi5qHRcGjrE/eq9tI9pKKaXYafd6H+Xe/Ws/ddf/WwXlrv15pn6+bOUVWnYMK1W7c8dHv75g3m8aSvqjnK9zk4Gmrf8sA/o9n4UOi2DWs1UfHwUXrtpbWu+ajuf8+Etm3ppU+/Tin4Tf+5Rbf3gpOmVR5ybNJrdV+w5uwv695n66oVtc8/mrSPebBlxbKmpYvFaL6zxDw44Nf2LRv158E/aLCvvMu7wuMDqmrXdlHHT5zTBg0BBFwmQAnosgUlnYEKOPY63T2qf/N/Cr/q6ORPBstnHKiz8VZ54JG+yj6eh8Y7mxuDjz9z5AXf76X1+dHQegza8uGy0qn7Fo8cW33Gubptufne26LJPlKubf0aVauqhAbPPsNfNUSl2+b/3trnvTEzzpSvega9frXOprrdpVNqKok23PbHbc8+vP6W6xI/AE+nBty2Pnx365qV/sFDh57R208ZUYeqY0/XLNtffrp5+VJtJGmRyNYn7jOMaNmMg3p+E2eS/n0dalw8X10C43a+71u7ia35vcXaDU6YpFcaAgi4T4AS0H1rSkaFJbC148lgYOyuejjYLXPdxdnwrz+tu/naxONbHrl73c2/a3jrVd1jSzyeu+3WNR9tefgub7B8yAmxH1gXnDi1fO8DVNtte+ZBc9Joe+zzAlWRrP3Lr9b97TexZ9brVnuKioae+XlPcXHT0kUb/32Tqkaz8wBeVVTpGbTH7y+ZMLmXy9vXr4mGQ+oQbqwP12/XRoat9rlH9CxbD9x1065t7crGxW/Wv/7i9jlPbH3yvs33377x7r+tv/V6pbzm+h9//Jvvbe14WKwZ9VRd0WqjW6t96an2zeu9wdLBKWr9bv372I1GGxfOVZ+yqTP02rPpvrLcdLx0z330SkPAtQIFnBglYAEvPqk7X0BFXvOyJYbHM2R2rLpSQiqVmpYtUp23+rorN97115YP31Ppo4eqo//fz8Z899fl+xyk3bY1q7Y8fPfq665SIdKwcJ4qHl2YSYu2tcQu7/gkwthGwpfG33jHX/Q8tObT53uDnZ8/N/j4T/pKyxvmv6I6Q7Prtp95RfvGdUXVwwYfc+rQT35BRwKjJww94/Mef1HzssXxCknH+9cike0vPaVLSsbtrrJSG6layW5T/ENqdLZk/O69vENCHdJp2566X7f0Yj0jET0L3nD7nzff98+tj99bO+fx+rlzGhe93vzektaPVyhlPY3VqnlLgr7SCvUPN9TFQbRrNnWre+UZbQ86/ARvsFQbglXt2LPV7vho6J6ndKT+9Zd0rVrDgrm6f+yvqu7+fw6RiM4ahqfu5af1Pwm6b1o0tMtH5OigoV8efdEQQMDZApSAzl4/oi9wAd1giz0ZnLZf8ajYTwxr37pp9bVXbLrnpob5r4br6/yDBlcefNSoi6+qPvUcX1mFt6Sk+tTPjLzoivJ9DlYZoVtNKkR0z2n1dT9SLdJfyU3/vmnVLy6N/aDYX1y69XE9oDT8PZ4vq7JR+aUabtg5Xy0Zu1t8Cm8gUDX79GHnXugrLW9bs9I8XrLrHsPPu2jk16+oPORY744P+SvdY/qw8y5UTVZ98llmtz5f19987eprfrjmhp+tvfGXuse2+vcd2Xk8lYcm+Va8xNHkM+qiy0dd8uPhn/9G4vGBbQu880KvV2mqtA2MGq96q2yv/bUoVUefWn3aZ4Z95qsj/u+y0d+5etwPrxtz2S9HX/qz6tM+q6t0v7B19YfaiLe2jWuNSLR4l1GVBxxuHoy0NKt27NnMs3rteUpHtOg6FW1vr332EW1UaDRPrJpT0al7w6LbPudJHVerf+NlvcY66DfD0KP5GOmffq7/u9CBVN8+qFM0BBBwigAloFNWijgRSCJQdeRJQ884b3DHt5HpdNGQmrKp++i2TcXMQ4efd5GqmcGzz+j2r7W/akj1qeeM/vbVurBsz319FYMq9psVGLOrLu9XK50yw1Dx4PF4/D7/oKrSPfaq6vGAUqXe8HMvHPG175b0+H4yPQs2i8LgpGmlU/Ye9a2fDFept2uSB7XqpprMUxxIMzwNGG5pDG3d1L5pvW6e6Uaj7u0NPePcktTf9LZzZK/Xn/Azi3ce7/9W2V4HVJ9yzqhvXjXu8t+NvvTnKm13+fK3VfOJXYsy6NBjy2ccpIqweMTo2AJ5RBmbo3zGgcUjx2oRfZU7f3SyTpRN22/YeRdWn3yOsbPnQaMuvrK/reLAIzSa7oZWHHh48fCR8YJSxaXuDbeuWRlpb9EfhsHHnVF1zGmSj7/BKDBmQox0y8Zoe5surDr8RI1DQwABRwsUcgno6IUjeAQ6BXRXyVdR2bljGNWnnzvyoh8MOemskmTlVLybx+/XhXreOvrbPx1yQsqf7VFz9lfGXXV9ZUfdEL/W3FBxMPYH14z9/m/1fHnUJT+uOfvLsVLGPJfwqieqO++HJRxP3Kw56/yedxATO/Rre9Bhx4374XVjL/+dAlMbe/k1o77xw7Lp+/drkMw7a1HK9z3YX1VtJHs+3sv4u5z/La1LTxCVsOa9XvNaldf+wUP727w7bq9KafgXLonHVr7XAWO+9+uxl/9Oa7rLly5R2Fr0YZ/7ujmXXoO7TenocM3YK64d8bXvqXLVQRoCCDhagBLQ0ctH8AhkQ2DHjaVsjGWXMVTj6sGumsfrs0tM6cVhWcDC2RmR16vqUGg7jxiGx5dA5/F0dOjyudaGU38RNwIIxAQoAWMKfCGAAAIIIIAAAgUlQAlYUMtNsoaBAQIIIIAAAggYBiUgfwoQQAABBBBAwO0C5NdDgBKwBwkHEEAAAQQQQAABtwtQArp9hckPAQQMAwMEEEAAgW4ClIDdQNhFAAEEEEAAAQTcL1AIJaD7V5EMEUAAAQQQQACBfglQAvaLi84IIIAAAk4RIE4EEOhNgBKwNx3OIYAAAggggAACrhSgBHTlspKUYWCAAAIIIIAAAqkFKAFT23AGAQQQQABV8SYEAAAQAElEQVQBBJwlQLRpC1ACpk1FRwQQQAABBBBAwC0ClIBuWUnyQAABw8AAAQQQQCBNAUrANKHohgACCCCAAAIIuEfATSWge1aFTBBAAAEEEEAAgZwKUALmlJfBEUAAAQRyLcD4CCAwEAFKwIGocQ0CCCCAAAIIIOBoAUpARy8fwRsGBggggAACCCDQfwFKwP6bcQUCCCCAAAII5FeA2TMWoATMmJABEEAAAQQQQAABpwlQAjptxYgXAQQMAwMEEEAAgQwFKAEzBORyBBBAAAEEEEDAeQJOLAGdp0zECCCAAAIIIICArQQoAW21HASDAAIIIJBKgOMIIJBNAUrAbGoyFgIIIIAAAggg4AgBSkBHLBNBGgYGCCCAAAIIIJA9AUrA7FkyEgIIIIAAAghkV4DRciZACZgzWgZGAAEEEEAAAQTsKkAJaNeVIS4EEDAMDBBAAAEEciRACZgjWIZFAAEEEEAAAQTsK2DnEtC+akSGAAIIIIAAAgg4WoAS0NHLR/AIIICA+wTICAEErBCgBLRCmTkQQAABBBBAAAFbCVAC2mo5CMYwMEAAAQQQQACB3AtQAubemBkQQAABBBBAoHcBzlouQAloOTkTIoAAAggggAAC+RagBMz3CjA/AggYBgYIIIAAAhYLUAJaDM50CCCAAAIIIIBA/gXsUALmX4EIEEAAAQQQQACBghKgBCyo5SZZBBBAwD4CRIIAAvkUoATMpz5zI4AAAggggAACeRGgBMwLO5MaBgYIIIAAAgggkD8BSsD82TMzAggggAAChSZAvrYRoAS0zVIQCAIIIIAAAgggYJUAJaBV0syDAAKGgQECCCCAgE0EKAFtshCEgQACCCCAAAIIWCdgZQloXVbMhAACCCCAAAIIINCLACVgLzicQgABBBDIXIAREEDAjgKUgHZcFWJCAAEEEEAAAQRyKkAJmFNeBjcMDBBAAAEEEEDAfgKUgPZbEyJCAAEEEEDA6QLEb3sBSkDbLxEBIoAAAggggAAC2RagBMy2KOMhgIBhYIAAAgggYHMBSkCbLxDhIYAAAggggAAC2RfIRQmY/SgZEQEEEEAAAQQQQCCLApSAWcRkKAQQQKCQBcgdAQScJEAJ6KTVIlYEEEAAAQQQQCArApSAWWFkEMPAAAEEEEAAAQScI0AJ6Jy1IlIEEEAAAQTsJkA8jhWgBHTs0hE4AggggAACCCAwUAFKwIHKcR0CCBgGBggggAACDhWgBHTowhE2AggggAACCCAwcIFMSsCBz8qVCCCAAAIIIIAAAnkUoATMIz5TI4AAAk4UIGYEEHCDACWgG1aRHBBAAAEEEEAAgX4JUAL2i4vOhoEBAggggAACCDhfgBLQ+WtIBggggAACCORagPFdJ0AJ6LolJSEEEEAAAQQQQKAvAUrAvoQ4jwAChoEBAggggIDLBCgBXbagpIMAAggggAACCPQtkE4J2Pco9EAAAQQQQAABBBBwkAAloIMWi1ARQAABKwWYCwEE3CxACejm1SU3BBBAAAEEEEAgqQAlYFIWDhoGBggggAACCCDgXgFKQPeuLZkhgAACCCDQXwH6F4wAJWDBLDWJIoAAAggggAACOwQoAXdI8DsCCBgGBggggAACBSJACVggC02aCCCAAAIIIIDAToHEEnDnUbYQQAABBBBAAAEEXCxACejixSU1BBBAIB0B+iCAQCEKUAIW4qqTMwIIIIAAAggUuAAlYIH/ATAMABBAAAEEEECg8AQoAQtvzckYAQQQQAABBApegBKw4P8IAIAAAggggAAChSdACVh4a07GCBgGBggggAACBS5ACVjgfwBIHwEEEEAAAQQKRSAxT0rARA22EUAAAQQQQACBghCgBCyIZSZJBBBAwDAwQAABBHYKUALutGALAQQQQAABBBAoEAFKwAJZaMMgUQQQQAABBBBAYIcAJeAOCX5HAAEEEEDAfQJkhEAKAUrAFDAcRgABBBBAAAEE3CtACejetSUzBAwDAwQQQAABBJIKUAImZeEgAggggAACCCDgVIF04qYETEeJPggggAACCCCAgKsEKAFdtZwkgwACCBgGBggggEDfApSAfRvRAwEEEEAAAQQQcJkAJaDLFtQwSAgBBBBAAAEEEOhLgBKwLyHOI4AAAgggYH8BIkSgnwKUgP0EozsCCCCAAAIIIOB8AUpA568hGSBgGBgggAACCCDQLwFKwH5x0RkBBBBAAAEEELCLQCZxUAJmose1CCCAAAIIIICAIwUoAR25bASNAAIIGAYGCCCAwMAFKAEHbseVCCCAAAIIIICAQwUoAR26cIZB4AgggAACCCCAwEAFKAEHKsd1CCCAAAIIWC/AjAhkSYASMEuQDINAIQm0trQuWrRow4aNqZJe+dFKdUh1NvF4Y2NTJBJJPGJuh8Nhjd9Q32DuDuB17Zq1K1asiF/Y1ta2bt36+K6VG8pl+QfL9Stbk7a3t2/atKmhodEcULvmBq8IIIBA+gKUgOlb0ROB/AvYJIL2UGjZ0vca6utTxbN58xZ1iEajqTqYxyPh8AvPv/Dmm/PN3cTXUCj00osvbdiwIfFgt23ViI888mi39t//3Pv8cy+0NLfU1dW9NX+BSkxd1dra+tqrc+fNnVe3vU67Pdu22m3vv/dBOk1jvv76Gxs3pix/ew7u8XhWrfp44cJFTU1NPc8O4EhjU+OcF15cvXq1rv3g/eVPPfl0v+LRVTQEEECAEpA/AwggkDeB9es3NDc3jx49WhGEQ6H+3s0qKy2dMmWPxLbH5D00lAq+4kDx+PHjvV7vsmXv6W7Zyy/9r7Gh4YijDq8cVKkOPVt9XcPC9H7pZp7ucW7bWttzkFRHFMbkyZNU73644qNUffp13Ofxxfp3VNhjxo6uqRn64pyXdKMxdpAvBBBwo0AucqIEzIUqYyLgToFXX3nNvOX2xONPKMO33lpg7vZ8/fDDD9Xh4YceeaTjLl3S+3y6R/jee+9XVlYOG1ajzu+8s/TBBx7qVxVYXlE+rGbYhAkTdtvxq6jIr6Gm7jlVVVdJsGSvvaavWL5c0ZaUBI486siqQVU6q5tnmzdv1kZiGz161CfOPCOdduhhh+pCn6+jCNNWem34LsPLy8rqU983TW+Yzl4eb+yvbgFqPxAIzNx/5qTJu2s51q5dpyM0BBBAIB2B2N8j6fSjDwIIIDB06NBx48aq1dTEijaBaDtp0ym1ceNjndVh8OAq7XZr69auUym2x5TJaZZTmzZtCofDiYPobt8Lc+a89tpcPfbV8Yb6hrffXqTYVM9pV238hPFm2bf33nsHS4M6oht4eiKsKtCsn3TEbCoZFUY6zev16BKfL8lfnksWv7Mgxa9FixYFS0tLgoEU5xfU1tZqWDUFpjS7tu570Y7vnmxpaTVP6JIpU6YccMD+KqZ1RIPQEEAAgT4Fkvwt1uc1dEAAgcIU2H3SxOkdv/abuZ8E9tlnRsdekhfdmVOHvXQXruPkrrvuqt3E1tbW9sorr+rIqFGj9NpL0wPidevWq/OcF158d8m7iT11A+zII45oamx64YU5qiZVCxoeQ7F5vZ1/s/l8vgMO3N/v97/xxpuqF999593XX39DEaki9HhilVziaKoL31v2fp/tfy+/oqv8HbcbtZHY1q5Z+8H7y1M1lbCpTul4c3OLOZQK2fvve6D39uSTT6mzHknHu+kG6rx5rz9w/4PvdCVSNxoCCCCQVKDzL8qk5zhoCwGCQMCNAsuWLVNaelarKk0bqZoebj7+xJP/e/l/GzvefRyKhLr1rKisOOzwQ4uKil54fo5upM2cObM0WBIJh+OtvLxsv5n7qkDUU+klS96ZNevgyXtM8ni6138adu3adW+n8cu8zeb3F+mSbm328cd+6qxPJjYVwepzxJGHJx5Muj1ixC7qqaZcJvf1S8+91VOtZ8ch1YN1nIYAAgj0KUAJ2CcRHRBAILmA6jPzW/16vprfC5j8MsPYunXrsqXv6eyE8RP02q3pBuGqVR8/9dTT5vGaoUMPOfSQk085SbvFRcV67dlCoVhpqMJIxeJ99z3Qrc19bd6uu+1mXlU1OMkjafPUzJn7nXnmGX023UFU/6JkdwF1vFtTLjpSVJSkXtTxpE1l8fS9pvXe9tl3ht/vHzZsWM9ufd5VTTopB+0oQEwI5FiAEjDHwAyPgKsFJk+anLT1knRLc4tqsp4dokbs0wHfmv/WI488Nm/uvNaWVvVR6XbgQQfqDpn5bNfj6X73To9NX37x5fr6ehVwe06beuBBByRt06fvqQ4a8Nlnn1uzZo02ejZN4fX5+mzmpxgWJbsL2HPMtvZ2HfT7Ym9S0UYWW0VFhXLP4oAMhQAChSbgpBJQf9+tX7eehkAhCAw4Ryv/Cttnnxm7T5qYtJnfC9gzmPb29rlz5zU2Nh5w4AHxs01NTcs/WP7esvd1RPf/Jowfryenp5xysnYHVVboNdY6PgBFVVpsu+NLD2SXL1/+9NPPNLe2qP/4CeOLi4vHpPil+3DqcPgRh3s93ldfeU03C/VouGMYQyWdjqTfFi1apAtffPEl8xLd0dRuqtbYEPvoRN2xiyQ8m45v6+alskh1be/Hy8vLm5qbwh23P3vvmcWzA/5jyYUIFIiACpUs/heX66GcVALKopVfCCCQWkDVjP4zsXNThPUN9dOm7akbe2acmzZteuzRx/VM2dw94cQT9JSzpqbG0/HGW/OgXnd8xnTnXcBoNKpS8q35C0aPGX3sMce0tLTo0XOfrThQdMwxR+82cbd169Y3NTVrWLP5/L5UTbcM1RLP6kmrnsAOHz7MPOjxdIZkDtXttbZ2u47oQXm3Z9Pm7gP3P/jK/2JvLlGfxLZx48ann3wmVXtxzkvqXFZaqtfmjnul2rCmaflS/+njDAIIxJ5dZPgfo5WXO6kELK8oHzd+HA0BBFIJjBw10sq/PlS3qbhJ2lSNJY0kEAgcfPBBk/eYHD9bPWTI1KlTjpl99B6TYwcDgeTf7afiQ5f4fJ2fxufxePbaay/d/Nt//5klwRLdQZz/5ltLlryTqi16e/Gbb8yv3Vqrzrp5edxxs8eOHaMB1XRn8YAD9lebPGmSTmkjsamD2n777Rs/ePCsgw455ODRo0drah0cPHiwOiRt5r+HOjV5j0k92+6TJupUoKREr92a0tRfd2arb2zYXrddzdzVa2lZrPgLlsU+46a5OTs/bqRbAKl29Qcs1Z89jiOAgAT0X2iq/3xseNxJJaAN+QgJgQIXmNL1h3PEd3thqa6uVgEX7+D1+abuOXVw1eAd9/niZ7psRMKx7xT0+Xb+lVVeXqabhfFOQ4YM0bPjVO3Agw6M99RGz58RopuCL7ww59mnn13b9dOVfb5Y0blq5SpdZba2trZ589545ZVXP/roI/NIqtemjh8Hp1ue05P9mjp1qi4sLk58p4gOxJqIVGiaTdWqDu29997mrl7Nb2qsqIg9Im9saNRZGgIIIDAAgZ1/nw7gDUtZtwAAEABJREFUYi5BAIFCFtA9s91S/Er1vYAD5mpujT23LU5xj3DAw8YvLC0NHn30UWXlZa/87xXdUGzveBuHzg7r+Mklixcvaegotuq217005yU9Gp4xY8b48ePVoZe2ZfNWnR1UVaXXnq29PfYu5kBxoOepdI6UlZWr27baWr3SEEAAgQEIUAIOAC03lzAqAggkE1i7Zu0bb7z55rz5OjmocpBek7ZQKLQt9a/6utjbMpJeGD9YUVkx65BZe+45dcWKFQsWLDSPh0Kxn0dSXFz85htv6vhzzz2v6vDIo46YuPtuifcyzc7dXlevXq0jQ4Ykf1Lc2hb7LOgBF7VlZaWlweCG9Rs0BQ0BBBAYgAAl4ADQuASBQhRQKWa+B1avzz7zrAjeemuBtpM283sBX331tfjZ2tpaXTKAFigJ6HFn5aCKQw49pJfvs9m+ffuzzzyXqi1YsCCdqfXYd8rUKbNmHTx92jTd9lv67tJNmzaVBEv23W8fbeju4MiRI448+qihQ4f2OdrWrVs3b96su6GBQPL7fG2tsc+LSXW2z/HVYcSoEQ0NDYnVrfkdkzpFc5IAsSKQJwFKwDzBMy0CThOIGtFQqN1surM1fPgwNXO356tOqYVD+tV5SX+rE91jq66uLioq0usRRx5+wIEHjBixSyQcuyfXU+6QQ2addPKJBx184IEHHaCNbi32vo0DDzjxpBNHje7jh9FpZMXp8/vefvvtJx5/4t13l+pIe1t7TU2NAtB2NGp4ensHsLp0tqVLYz/+ZNz4sZ37PX5r7vhOwUxKwOHDYz9QZNPmTfGxdd/x/fc+UArxI2wggAACqQQoAVPJcBwBKwQcNMeoUaMOO/ywxKaqaNiw4fvsu2/iwal7Tp00ebJqssSD2h4yZEi/ktWz16OOPnLsuC4l1LPPPK+bkT3H0Y26kkBg0duLVQCVlJSUJvwKBoPvLXv/g/c/0HNTFZQ9r40faWpq1uVPPvHUSy++vHnT5n32maFSUnVnUcc7NsaOHbPffvt+/PHHzz/3gu4Ixq9KuqFuinPMmNG93C9s6PjmwmAw9vbepIN0O6jCrm57nYo8pfnhig91tqZmqDJa/v4KndKumu4sLly40FChqh0aAggg0KsAJWCvPJxEAIFkAq0trR+8v/ypp55etGjR0qVL4yVIe3u7Hp6+9OJLjz/+pDbMKifZAEmOeTx93F5ra2vbXrc91Zhen2/S5N31+HXLli2Jo+tprK7q/Vv3Nm7cOG/e6489+pjqJ1WPB8866PgTjttt4m7dbtFN2HWC7ke2h9rnvPDivLnzUj3a3rZt2+uvv6kSds9p0xIj6batOPXcWYVpt+Px3Wg0qmRVSuqIAnvwgYcE/tqrc1euWhmJxt4frfpv4sSJym7Njp930tTcWF5WJgpdQkMAATsL2CE2SkA7rAIxIOAMARUlqm/0kPTRRx9bsGBBzdCaI486YubM/bzezr9JVJQccughs2cfO27cOD1F1bPU119/Q0VYOukVFcV+ilpbW+w75JL2N0uuivjPC+nRacSIETq2YV2Xd0h89NHK0mBQtzB1KlXTPb9VK1eNnzBe6ajIU2efPxaM0eNXTU3N7GOPVUG5atXHzzz97LPPPKdKLrGXqskX57ykB9b7H7B/eXlZ4qnEbdV2upU4cuSIOF38rOrpZUvfU6n38EOPCFDa5qmJE3fTk+4TTjj+5JNP2m233cyDu+22q2rNhQvf3la7TauzaePmocNqzFO8IoAAAr0LdP7F3XsnziKAAAIqWZ568mkVPSuWf7j77hOPO262KpKkDzoHVQ2aNn3Pk04+Uc9SN2zY8MLzc3RfsLW1j8/NrxwUe7ev7q4tWfyO7iB2a4sXLXl93hsqd6qrOx8oNzY2fdj1l+aqrq4OlJTED6t+WvnRyqbm5lWrVsUPmhstzbE35JrLuvuk3U886UTVsmY6usepinDVqo917fbaukBRl/dzlARLZsyYofSnTJkSDJbEPxpad0AXvb1I9V84HJ416+ARI2LfqGeOr1fVfCqad6S29PV5r+vgmLFdHnPriJqKwi1bt6xfv37YsGF77723StJTTj3lU2d9cvpe00ePHl1eUe7xeNTNbApGYSuXZ59+7pmnn1WBPnz4MPMUrwgggEDvApSAvftwFgEEOgVUXen2mB6SnnzKSSpHKgdVdp5I8ZueoupZ6vHHH7fXXntNmz5Nuyk6dh7eZZfhKhk9Hs8HH3ywePGSbm358uWDBlXOOuRgVYHmBU1NjW++Mb9b0z05PTONH3yv4+cOq3/8SHzD/KBBnVIrKioq6/iRG9pWKyou0i1MFaNvvjk/WBrcc1rsM5x1PLEpfR2fdcgsVWzmcZ/X29zSoiLy6GOOGtnjx7TojuDmjVveffddMy/ViyrvdBfQvLbb63777Xva6aeqwt590kTddywp6VKDduusuQ4/4jBVfq1trXoUrjXq1oFdBBBAIKkAJWBSllweZGwEnCmgWkf39lRhqGBKPwN1Vl0Sv1UWv1DHdWdLVVT8iIo/lYx6FHv6GafpVLemg4cedqgKrHh/1Ubd+vRrd3BV8o/r0/jKdPZxx575yU+oHXX0kaqxdLDP5vX5VMIefvihVVVVSTsfe9wxn/zUmWeeeYaGPe742SrvknbTQZXLikEbaTbdLzzs8MNOOeVkVdv9ujDN8emGAAKuFKAEdOWykhQCCGQkoEJKrb9DqK5VIdjLVSpz1WEAI/cyJqccI0CgCNhMgBLQZgtCOAgggAACCCCAQO4FKAFzb8wMCBgGBggggAACCNhKgBLQVstBMAgggAACCCDgHgE7Z0IJaOfVITYEEEAAAQQQQCAnApSAOWFlUAQQQMAwMEAAAQTsK0AJaN+1ITIEEEAAAQQQQCBHApSAOYI1DAZGAAEEEEAAAQTsKkAJaNeVIS4EEEAAAScKEDMCDhGgBHTIQhEmAggggAACCCCQPQFKwOxZMhIChoEBAggggAACjhCgBHTEMhEkAggggAACCNhXwImRUQI6cdWIGQEEEEAAAQQQyEiAEjAjPi5GAAEEDAMDBBBAwHkClIDOWzMiRgABBBBAAAEEMhSgBMwQ0DAYAAEEEEAAAQQQcJoAJaDTVox4EUAAAQTsIEAMCDhcgBLQ4QtI+AgggAACCCCAQP8FKAH7b8YVCBgGBggggAACCDhagBLQ0ctH8AgggAACCCBgnYCbZqIEdNNqkgsCCCCAAAIIIJCWACVgWkx0QgABBAwDAwQQQMA9ApSA7llLMkEAAQQQQAABBNIUoARME8ow6IgAAggggAACCLhFgBLQLStJHggggAACuRBgTARcKkAJ6NKFJS0EEEAAAQQQQCC1ACVgahvOIGAYGCCAAAIIIOBKAUpAVy4rSSGAAAIIIIDAwAUK4UpKwEJYZXJEAAEEEEAAAQS6CFACduFgBwEEEDAMDBBAAAH3C1ACun+NyRABBBBAAAEEEOgmQAnYDcQwOIAAAggggAACCLhdgBLQ7StMfggggAAC6QjQB4ECE6AELLAFJ10EEEAAAQQQQMAwKAH5U4BATCCTr82hxhWtmxc3rXurcbWaNrSrg5mMybW5E9i6bdvKVR8vXfb+4iXvqmlDuzqYuxkZecAC0agRiUbVwpFoOByJtUhsNxLVrwGPyoUIIBAToASMKfCFQJoC69rrHqtd8us1T5+//I4jlvx+17d+FJz77dFvXjF1wc9mLvr1wYuvUdOGdnVQp9RB3dRZl+hCXZ7mRHTLisCGjZuefX7ODTfe9K1Lf3jGWZ8/4NDZ4ybNmHHAkYcdc8pxp3zqpDPOUdOGdnVQp9RB3dRZl+hCXZ6VMBgkTYFIJNoeCre0tjc1tzY2NjU0NNTX1dZvj7WGutqG+u2xtuOITqmDuqmzLtGFujzNieiGQFygkDcoAQt59ck9LYFNofo7t7zx1RV37bnwZxPmX3Xmsr//aPWjd25+fW7DR2vbtkeNaKpRdEod1E2ddYku1OUaRENpQA2b6kKOZyKwecvW+x585NLvX3X4safuf8ixX/rqN39z7R/ve/Dh+QsWrt+wsZd7RzqlDuqmzrpEF+pyDaKhNKCGzSQqrk0lEIlE2kKh5pbWhsYmVXVNDXWtzQ3trU2h9tZwqF2LkupCnVIHdVNnXaILdbkG0VBt7SENm+pCjiOAgClACWg68IpAd4GN7fV/Xv/i8e/eMObNK8//4PZ/bpq7vGVz907939cgGkoDalgNrik0Uf+H4YruApu3bLn1n3ee87mv7HvQUd+69Ip/3/vgRytXde/Ufb/vfQ2ioTSghtXgmkIT9X0ZPfoSCEciKtQam1oa6uubG+rbWprC7a2JF3k8Xp+/uChQWhwsD5RWlpRVlZR3tLIq7eqgTqmDuiVepUE0VHNjvYbV4JpCEyV2YBsBBOIClIBxCjYQ6BR4ovadcz+4bez8K7+z8r45dR90Hs3BbxpcU2giTfdE7Ts5mKEghnzuhZcu/OZl+x509I+u/vUrr72eu5w1uKbQRJpOk+ZuIhePrFt3emKrR7d6yqtCLdTWHI1GzHxV8amqC1YMKRu8S2XNmMphY8urR5ZWDQtWDi2pGBIorwqo+FMrr9KuDuqUOqibOusSXajLNYg5mobV4JpCE2k6TRqNprxhb17CKwKFJlDAJWChLTX5piFw66bXDlz82zOW/e3eLQvS6J61LppOk2pqBZC1QQtgoLv+fd+Jp5/9xf/7xqOPP2VluppOk2pqBWDlvI6eSxVYa3uoqblZT2z16DYSCZvp+ItLVL2VDxlZMXS0qrri0kod0R1A82w6r+qsS3ShLtcgGkoD6oh5rSbSdJpUUysAhWEe5xUBBCgB+TOAQEzglk2vTlv48wtX3L2wcU1sPx9fmloBKAwFk4/5nTTnXf++94jZp37vip8seWdZvuLW1ApAYSiYfMXgiHkj0dgz36bmlpbG+lBb59NePcNVoWbewFP15isqzlYuGkoD6r5gZc0YTaGJzJE1tQJQGG3tIYVkHuQVgUIWoAQs5NUn95jAo9uWzFr8u4tW3PNBy6bYfr6/FIaCUUgKLN+x2HH+Z56bc/InPvO9K3764UdZ+Fa/zDNUGApGISmwzEdz3wht7eHm5lY9kA21tZjZFZWUqT7TM1wVarqBZx7MxasG1xSaSNNpUnMKhaFgFJICM4/wikDBClACFuzSF2jiiWmvaav94vLbP/ne3+c3fpx43A7bCkmBKTwFaYd47BDDuvUbLrn08vO/9s1Fi233fZMKSYEpPAVpBys7xBAOR5pbVPzVqeoy4ykOlldUjyodVBN/Smsez/WrptOkmloBmHMppObGOoWnIM0jvCJQgAKUgAW46KQcE7ht02sz3v7l3ZvfiO3Y9UvhKUiFatcArYvr7v/cf/QJZ9z/4KPWTdn/mRSeglSo/b/UVVdEo9HW9lBzc3NbS5OZWFGgNPYtepVDvf4i84j1r5o6WDlUYSgYc3aFp78LAckAABAASURBVCAVqgI2j/DqYgFS6ylACdjThCMuF2gKt395+R0XrLi7Ptz5bUl2TlhBKlQFrLDtHGfuYmtubvn2ZT/87uU/bmzsrCcMG/9SkApVAStsG4eZw9DC4XBLa3tLY3041KZpfP7i0qphar7sfbefhh1wUxgKRk2BaRAFqVAVsMLWLg2BghKgBCyo5SZZY27DR4cs+d0dm3P40SG5UFbAClvB52JwO485f8Hbp5z52XsfeDizIK2+WgErbAVv9cT5nq89Vv+1trU0moEESgeVV4+M33UzD9rhVSEpMIVnBqOAW1pbFby5yysCBSJACVggC02aMYE7t7xxxJLfv9u8PrbjtC+FreCVgtMCH3i89z34yBlnfe79D5YPfIj8XamwFbxSyF8IVs/c2h5qaWoKdbzn1+vzlw0eXlIx2Oog+jOfwlOQClUXKWwFrxS0TUOgQAQKoAQskJUkzb4Erln77Pkf3N5XL7ufVwpKxO5RZiO+P//1lm9dekU2RsrnGEpBieQzAkvmjkajepba2twYCYc0oXmPzV8c1LbNm4LU7UAFrDgVvFJQIkpHuzQEXC9ACej6JSbBmMCVHz/yw48d9jAxFneyLyWidJKdcc+xX11zvZo78lEiau7IJXkWUUM3z1qbG6KR2M/5KCmrKq0a5vHY4B+X5OF2P6pQFbDC1gmloESUjsFPEhEHze0Cjvmv1O0LQX45FPjeqgd+u/aZHE5g+dBKR0lZPq1FE179y2tcdudM6Sgpi/isnSYaNVra21ubGsxpSzp+kpu57azXQMfPnTNjVjpKSqmZu7wi4FYBSkC3rmyh5xXP/4pVD12/7oX4rms2lJRSc0068UR++dvf//2Wf8V3XbOhpJSaa9IxE9ED09a2nfVfaVVNoLTScOwvBV9aVWN0/FIVqNSUYMceLwi4U4AS0J3rSlamwK/WPPW7dc+Z2+57VWpK0E15/fHPf7/xb7e6KaPEXJSaEkw84vTt1raQHpuaWehZalGgzNx27qtSUCJm/EpNCZrbvDpIgFDTF6AETN+Kng4T+MemuT9e/ZjDgu5nuEpQafbzIpt2v+e/D/z2uhtsGlyWwlKCSjNLg+V5mLb2UNuOD39R2VQUKM1zQFmaXokoHXMwJag0zW1eEXCfACWg+9aUjGICL9Z98LUVd8W23P6lNJWs07N8de4bl/3gRxln4YABlKaSdUCgvYbYHo60tjSbz0mDlUNVNvXa3WEnlY6SUtBKUGkqWW3TEHCfACWg+9aUjIzNoYavFkb9Zy62klXK5rYTX7du23bp969yYuQDi1nJKuWBXWuHq8IR1X8tkY7Pfykpr4r/4F07xJatGJSUUtNoSrO1pSXc8WZn7dIQcJOAi0pANy0LuWQmcPGH//modUtmYzjpaiWrlJ0UcddYL7/qZx+vXtP1mJv3lKxSdmiGsRtjre3h9tgPV1SdFCircmgifYat1JSguinZ1tZ2Ja5tGgJuEqAEdNNqkktM4I/r59y/dWFsq5C+lLISd2LGN992x2NPuOoje9JZBaWsxNPpabc+bbHyL/bDmn3+4pKKaluEl7MglKDS1PDtrU1KXBs0BNwkQAnoptUkF+OdpvWXrby/MCGUuNJ3Vu7vvb/8Jz//jbNizla0SlzpZ2s0a8YJhcNtbS3mXMHKao/HY2679dXj8ShNMzslrvTNbV4RcIcAJaA71rFws+iW+RVu+REg3fJKc9dx6f/iN79PMzVXdnNY+lGjrS0UCbVrLUrKq3xFAW24vilNJas0lbjS56eGiILmGgFKQNcsJYkYt29+/fHaJYUMofSF4BSB/97/8HMvvOiUaHMRp9IXQi5GzsWYraGQnodqZD0BDrj3WwCVYLemZJWyDip9IWiDZgcBYshcgBIwc0NGsIVA1IhevfpxW4SS1yCEIIq8hpDW5NFo9Nrr/5xWV1d3EoIo7J9iOBxub2sz41RJZG4Uzms8ZSGIonASJ1N3C1ACunt9Cyi7X695emXr1gJKOEWqQhBFipM2OnzDjTetXrN2oAG55zohiML++YTCkXDnu4ArzFti9o85ixEq5eJghQYUQigc0QYNARcIUAK6YBFJwagPt163/nkgTAFRCMTctudrQ0PTX266zZ6xWR+VKARi/bzpzxgJR9rbYp8C4/F4zG+MS/9a1/RU4kpf6YhCINqgIeB0AQeWgE4nJ/4cCPxx3QvbQ83ZHfjgyt0vGnVsrtuFI4+ZUjoyu5GLQiDZHTO7o91827/q6xuyO6ZzRxOFQOwcf3s4HO54F0igdJDH67NzqLmLTYkrfY0vCoFog4aA0wUoAZ2+gsQfE/jLxpdjv2Xp65LRxy8/8NrnZvzg2t3OzXW7buJ5b+x39eL9f3Xe8EOyFH5smOyCxEbM6tc/br8nq+M5fjA7g0QiugUY+y5A3QMrKo09DM0bd74nVvpCUBTtbW1i0QYNAUcLUAI6evkIPiZwy6ZXN7bXx7ay8fW1kUf/fMLZowKDPYYnG+P1PYbP450YHH79xM+dOGTvvnun10MgYkmvr9W97vr3vZu32Ppnt4wdO9ZiFIGIxeJJ05yuPRwJh2IlYHGwwluotwBNK6UvBG0LRCzaoCHgaAFKQEcvXwEF30uqt22Y28vZ/p76xqjZfk8e/rso8wW+P/aU/kbbS//ssvQyUX9P3fOfB/p7iTX9TzrpxDvu+Ndbb82/777/WjNj4iy2ZQm1xT4IUKEWF/YtQAmoxRHiLDpIQ8ChAnn4p86hUoRtT4E3GlbNa/woi7GNCeTtZ15NKBmWxUTEIpwsDpiVoRa+vXj+grezMlR2B7nyyh9ed921hx12WHX1EPNhX3bH73M0sQinz24WdwjFvgkw9uNAigKlXl+RxbPbcDohiEKBhdpbhKMNWq4FGD93ApSAubNlZCsE/rv1rSxOM71sTIk3b//O6UZgFnPRUNnF0YCZt4cfezLzQbI+wu23/+srX/lyMBg0R964cZO5YfGrDXFCobCJUBQsNzd4jVOEQnw6DH8cnC1ACejs9SP6B7ba8ZaSTdbFhjiPP/lsf3Cs6HvjjX86/PDDEu/8LVq0yIqJe8xhN5xoJBoKhRSmx+M1b31p2+KmKuulZaE/PNn2zX82f+aGppN/2zj7l40n/LrxE9c1feEvzVf+p+XWOW3vrbe0FBOFQOQQCrWLSBs0BBwqQAno0IUj7JjA3IaPPmq19RsLYlHm70s4Isrf/N1n1rPOj1ev6X40r/vHHnvs8ccfnxjC1q1br7suPz+5WDgiSgwmv9vhaCTc8XHQRSWl1kdS3xL923Ntn/x904/vbX3wzfYlqyMb66It7UYkarSHjbrm6OqtkVfeD9/+v/YLb2n+/I1Nd77SrnrRmjhNEOGIyJoZmQWBXAjYuATMRbqM6S6Bp7YvdVdC2c/GVkRzXnol+xlmNuIXv/gFv98fHyMSidx5513r1q2LH7F4w1ZEYVVbHfn7A1aXgKrnPvun5ntea29oiXaE0MfLmm3Rm19oUyH44tLYbcs+emd8Og4SJ8p4SAZAIA8ClIB5QGfKbAk8X7ssW0O5dRxbEb38v9fs5rz77hPjIUWj0QceePA3v/lt/Ij1G7Yiir/p1V/c+V2SFoC0howf3duieq6pNa3iLzGkDdujP72/9Z01OX8uHAeJEyWGwTYCThGgBHTKShFnd4HmSPsrDR92P8p+VwERCarrsfzstbS0vv5mNt+7k5U0hgwZYo7T2Nj497/f9K1vfdvczderiASVr9kT541EouGOjwP0F5ckfqNkYp+sb+tJ7ndub355Wed7UJKOX+Qz/Kn/4YpGjbkf5PxGoEDEovBEJCht0BBwokDq/5KcmA0xO18g/QzmNXyUfudC7mkTqPn5/iyY8vKynn8MVPnpse8zzzxz7rnn/exnP1eHmTNnnnbaqdZ/OrSmNlveocwwVNlEo7Hbaf6iEvOIBa/XPta6dG1s0m5z7THS+9Wji//8peAT3ytTe/L7ZY99t0y7X59dvN8En4rCxP6jBlvx75rJIiJBJc7ONgIOErDiPxUHcRCqgwTmN652ULR5DNUmUIsWv2M9wrRp06655rfPPffMokULFy9e9N57S+fOffWOO/71la982Qxmxox9Dzzw4DvvvPsLX/jCM888pQ733fffG27445w5z8+f/8bdd9/12c9+1uxp2WteoHpmF3+jg7eouOfZXBx5ZnHoybe738AbPsjzq3NK/vTF4KcPKpo8whuv9gJ+Q7tn7l/0m8+U3H5R6Yl7+wMdn+akivDY6Tu/uTMXcZpjxlniUOZxXgcswIXWC1ACWm/OjNkReLvRXu8tzU5WORjFJlDvvPteDpLrbchf/eoX//733WeffdbEiRMHDRrk9XpLSkpGjBhx2GGHXXXVlW+++boqvNtuu+XVV1+5+ea/f+ITZ0yaNEkdzBF9Pt/QoUNnzTr4l7/8+b33/mfKlD3M4xa8Wg+VNKlIuPNprL8okLRDdg/qAe4/X+78MSTxkXcd5tWtvv139cWPJN0YWuG59OTAf79Z+o8LgqoIvZ6kvbJ8MM4Sh8ryBAyHQO4FKAFzb8wMuRFY2rI+NwO7bVSbQL2/fHlq2eyfueuuO3QDr7w85Qca19TUqMI7+uijR40a6fGkrBo8Hs/+++9/22237r333tmPMtmIFkMlCyF2LBKJlYAej9fj7aMCi/XO+OvpRaE1W7s8Aq4Men5+dklVacql6TZnacAzeoh1/6KJRTiKwYTSBg0BxwlY9x+M42gI2OYCy1s32zxCm4RnE6iPVq6yDOSWW2465JBDsjid7h1eeeUVWRywl6GshOoljEjHh0L7rHoK/NBb3W8BfnZW0bDKdOu/XhLJ3SkTx4TK3SyMjEDuBGxQAuYuOUZ2r8DmUENdqMW9+WUzM0GJK5sj9n+srdu2NTQ09v+6gVxx4YUX6N7eQK5Mfc3777//i1/8MvX5bJ4RlLiyOWL/x4rE3uYQuwvo9VnxfXX1zdFlXd8FUhbwnL5fxzf39T94y64wcXQXUFyWTcpECGRRgBIwi5gMZZ3AurY66yZz/kx559qwwbpbtp/+9Ke93mz+zbZ48eJjjpk9f751n2hjJVfSP90dbwWOnfF4cvkUODZD7Outld0/Ynmf8d5iK4rP2OwD/orjxLkGPBQXIpAXgWz+RZmXBJi0MAXyflvLWex559q6bas1Ymed9aldd52Qxbnq6uquuurHWRwwnaEs40oVTPy2ltdnRQm4YmOX7wJUVFNHWTGvJsqkeX2dQca5MhmNaxGwXoAS0HpzZowJZPhVG2rOcISCujzvXNu311sDfvzxx2d3okceefSNN97I7ph9jmYZV+pIdvxkDo8V/0Zs2L5juh0BjRva27x1zdF31kQWrw53a8vWRVq7f6rMjhFz8ftOnO7x52I2xkQg6wK9/WeW9ckYEIFsCTRF2rI1VCGMk3eu5haLSvYJE8ZncUFDodBdd92dxQHTHMojryY/AAAQAElEQVQyrlTxxCsaT1YfqaearucPAh5clvKNIPfOaz/r+qaL/9F8yT9burWLbm3+9B+alqyOfRdjqrmyeDyOE+fK4uAuHorU7CNACWiftSCSfgi0RS36W74fMdm4a9652tq6v98zR1rV1dVZHHnt2rULFy7M4oBpDmUZV+p4UlZgqS8Z+Jn2Hv81B1J/I+CDb7aHuj833jl1fUv033Mt+sO2c1bDUq6EedlEICMBSsCM+Lg4XwJRg//x7oe9DbjM9epHzAPrWlpaOrALk17V1pavm80WcSXN2vqDPd/50diaUqCkqI96q7bJ+gyYEQFHClACOnLZCNpvyRsVXeOcdy6fJZ8tovXy+1PfPtLpfrZoNGUh0s+R+tfdMq7UYVmaeLC4e1VXn/oTny44pniXKo+n+xUJqVgauzlvHqY0J+YVgUwELCwBMwmTaxHoKlDiyea/9F3HduFe3rlKAlb8kDGtXKjjA421kZVWU1OTlXH6O4hlXCkD21HSRCOpn7mmvLjfJ3p+BPSqzSnn3XeC746LSp/5Qdmzl+9sFcFeSsJ+x5PmBTtxdnCleSHdELCJACWgTRaCMPonUOkr6d8Fhd0771zl5WXWrEBzczbfd1JVVXXKKadYE3niLJZxJU6auB1/q2s0Kx95lzh0su3xPd7/+27XT4pOdlH+j8Vx4lz5j4kIEOiPACVgf7ToaxuBIUUWlRS2yTijQPLONXhwVUYJpH1xbW1t2n3T6vj5z38urX5Z7WQZV6qoPZ4dN9UsKQH3HueLT2iG9PaqcM/3iJinbPS6A8fj2cFlo+AIBYG+BSgB+zaiRyYCObp2F39ljkZ25bB556oZOtQa2LVr12Z3opkz9/vUpz6Z3TH7HM0yrlSRxCuaSLjHm3VTXZPB8aEVnm43Amuboo8tsP6Nvf3LIY4T5+rf9fRGIN8ClID5XgHmH5DA6GKL7ioNKDrbXZR3rpEjhluDsnTpsuxO5Pf7r7jiiqz/0OHeg7SMK1UYXt3WUjOMSNiij1o+cmr37+69/eX2bY22/ia7ThyPJ8aVirKAj5O6/QUoAe2/RkSYREB/5+5aYtGNpSTTO+qQoMSV35C9Xu+4sWMsiOHOO+9qb8/y3aPq6iHXXPObL33pixbErykEJS5t5LF5PB6fr0gBRMJZxtSYSduZM/2lXd8XvLUx+uN7W5va7FsFmjiCElfSpDiIgM0FKAFtvkCEl1JgctCiG0spI8j2idZITv65zQdUEprddp2Q5Gi2D73//vsffLA826MaQ4cO/dGPrrr99n9lfeSeA1oD1XPebkc8Xp+O6EZX1JJPxikNeM45OFZ0atJ4W7w6/PVbW5atS/nu4HhPS2KMzxbbEItwtGVCaYOGgOMEKAEdt2QE3CmwZ3CXzi23/LambVsuUrEJ1ORJu+Uiu55jPv300z0PZn7EsjtzlkH1buLzxUpA9YmELPp87M/MKpo0ovs/Sau2RL5+a/N372p5aH77+toudwTbw8b76yP3vd7+43tbev6IOUWe0xZniUPldDoGRyAXAt3/e8vCHAyBgCUCM0pHWzKPRZOEo5F/rn85F5PZBGr6nlNykV3PMa+55nebNm3qeTzDI6FQ6Oabb85wkHQutwyq92DiJW+ovbX3ntk66/UYPz+7pOdnBKrue/PD8PVPtJ3756bjf934yd83nXND0yeuazrxN40X3NL8p6fbXlrW9T0rlrw7I84Sh8qWA+MgYJkAJaBl1EyUZYH9y8dlecT8DdcUafv7uuf/uOapXIRgE6gZe0/PRXZJx3zooYeTHs/k4IIFC55//oVMRkjzWiuhegnJ7+v81yGcfgnYy3DpnRpS5vn954Jje3xMYPzqUNiobYpuqovWNetJbPxwl41BwS67OdqJs8ShcjQRwyKQO4HO/8hzNwEjI5AjgXGBIbta9Y4Q3aJ7cuvb9256PRftutVPnLrod9/64PZcQIlIULkYub9jjh41cpwl7whRYD/5yU9XrlypjWy1SCRy++13Zmu0XsYRkaB66WDZKa/X4/UXa7p4raNtC9rwQZ4/f7HkpBl+3RQcwHQej3HSjO7fUziAcfq8xGQRkaD67EwHBOwpQAloz3VxXlR5ifiIiokWzKv678a1z56++Lpz3/1zLtoPVtzzv+3v5SgRa4jSDH7WQfun2TPzbjfe+Fc9us18HHOEt95667777jO3c/pqJVGfifiLYrVUJBwyy50++2erQ7DY8/9OCvz5S8Ejpvj9nd+R2PfYVaWeY/b0//68koMmpn1N36Mm7yEQseicSaQNGgJOFKAEdOKqEXOnwLFVe3Ru5ew3s/67dLkVd4BykYQFROmHffihB6ffOcOed95552OPPZ7hIObltbW1P/7xT83tXL9aSdRnLj5vZy0Vasvmj93rc16zw+67eK/6ROD+b5X+4LTACXv59xztHVrhKS32+LyGmjZqKjxTRnlV9n3t6GLVi/+9pPTy0wPTxvjMy3P6GgeJE+V0OpsPTnjOFaAEdO7aEblxwqCpHsOTOwin138ewyOi3Pn0d+SjjjjM4/H096oB9//GNy5esuSdAV9uXqhbiX/7298XLlxo7ub01ePxiCinU/RrcL/P6/XFPrG5vaWpXxdmsXNpwHPsNP9lpwT+8PngPReXPnxp6VPfL1PTxt0Xl97whaDKvrMPKpo8wuux7k+WYYIIR0RZTJahELBYgBLQYnCmy6ZAma/49CG5epOB0+s/QQtHRNrIWevfwKWlwROOO7p/12TW++tf//rHH3884DHC4fCtt952ww1/GvAI/bpQOCLq1yU57ez1evzmtwOG2sLtFn00TE4zysrgogh3fFCO318koqyMySAI5EWAEjAv7EyaNYEzh8zI2lgJA7mg/lM2OcLRyANuJ59w3ICvHcCFK1Z8eOmlly1f3v3Dordv3z5v3jw9LL7uut/ffPMtTzzx5OrVq7u9xbSxsfGWW269+uqfDWDegV1iMU46Qfr8sbuA6tne0qBXmgTiFD5/7HsldYSGgEMFMigBHZoxYbtL4OzqfQf7S7ObkzvqP7EIJ7symY922iknVFUNynyc9Ed49dXXjjrqmFtvvVUPc5csWfLiiy/94he/nD5970996uzvf/9ylYA/+clPv/rVr82adeiFF16kWvDtt99+992l2rjgggutrP/EIpz087Kmp9/n8XYUOm3NlICd5CaFWITTeYjfEHCmACWgM9eNqBMEPl9zQMJeppvuqP+kkF0WDZitdtaZp2VrqPTH+dGPfnLqqaefeOLJ5533ub/85a9JL3zsscdVC55yymnHH3+CNubMeTFptxwdzAtLn7l4vSp1Yh8NE41GzNIndkkBfwlBFALwFxULRxs0BJwrQAno3LUj8k6BL9Uc1LmV8W+haPjGtc869/2/iQBZZEkcNvPtz5x9ZuaDuG8E27IU+XweT+xfiramOvex9zcjE0EgYunvtfRHwG4Csf+w7RYT8dhZwIax7RHc5azqfbIS2LtNa91R/wlELFkxyfogE3fb9dSTj8/6sI4eUCBisWcKfr/PX1yi2MKhtvbWvL01WAHkvSl9ISgMgYhFGzQEHC1ACejo5SP4ToGv73J45xa/dQjYHOT8L3y2I0xeOgVsDlJU3PmmkNaG2s6IC/K3ePpxkIJiIFn3CVACum9NCzGjg8onfLI6J28NdqKmKARi58j322fGySda+tZgO2uIQiB2jlAPPYtLYm+60j2wtkJ9X4gSV/paJlEIRBs0BJwuQAno9BUk/k6BS0cc27lV8L/lgCL7pl//2vnZH9SZIzqCosjv93T8sJDWxtpoNOpM6YFHrZSVuK4Xgii0QUPABQKUgC5YRFKICexTNvrC4YfFtgr7SwiisL/BtD2nfPFzn7F/nLmOUAiiyPUsmY/v9/uKArHvCIyEQ60N2zIf0FkjKGUlrpiFIApt0BBwgUAaJaALsiSFwhC4avSJ1f6ywsg1eZZKXwjJz9nv6P+75KIhgwfbLy7rIlL6QrBuvsxmCvj9Pn/sA2Jam+pCbS2ZDeakq5WsUlbESj/g7/y2SO3SEHC6ACWg01eQ+HcKDPaX/nzsqTv3C29L6QvBKXkPGlT5/csucUq0uYhT6QshFyNnZ8yuo3h93qJAwDzWUr/V3CiE13iySl8IhZAyORaIACVggSx0oaT5xZqDzhyyd6Fk2zVPJa70ux6z+945Z33ipBNm2z3K3MSnxJV+bsbO1agBvx4HBzV6WHfGCqMKVP2nZJVyUSCo9LVBQ8A1ApSArlnKLCfi3OGuHX9mjb/CufEPLHKlrMQHdm1+r/rpVd+rHjIkvzFYP7tSVuLWz5vpjB5PoLjIV9T5OLjN7e8OVoKdj4CLigPFRYbHkykg1yNgJwFKQDutBrFkQ2CXokE37Hp2NkZy0hhKWYk7KeIdsQ6rqfnl1Vfu2CuU35WyEnditj6fLxAoMYuh5rrNuhvoxCzSiVmpKcFYTxW+gRIlHtsugC9SLBwBSsDCWesCyvT0wdOvGH1C4SSsZJWyc/M94bijv/3NC50bf38jV7JKub9X2ad/kd8XCJab8ahIMt8qa+665lVJKTUzHSWrlM1tXhFwkwAloJtWk1x2Clw56oRzambu3HfvltJUslnKL2/DfPviCz5x+sl5m97CiZWmkrVwwpxMFSjyFZfE3n2vUqmpdmM0GsnJNHkaVOkoKaWm+ZWmktUGDQH3CVACum9NyahT4LZdzzu8YmLnjkt/U4JK0x3JXX/NLw4+0OVVuxJUmi5YL4+ejRb7ioo73xqiginqls+LViJKp/MtIMXBQLFPybpgyUgBgZ4CCSVgz5McQcDhAndO+tLeZaMcnkTK8JWaEkx52oEnbvzjNXtOnezAwNMKWakpwbS6OqGT1+sLBIr8xSUKNtTW0lS7QcWTth3dlIISUTrKQqkpQaWpbRoCrhSgBHTlspJUp8BQf9l/Jn1lSnCXzn0X/aaklJoSdFFOxpDBg2+68frdJ+7mpqTMXJSUUlOC5q69Xgcajc/nCxQX+4tjHxaosqlx2/poJDzQwfJ/nYJXCkpEoSipQHGxEtQ2DQG3ClACunVlyatTYGzx4Acmf3V66cjOfVf8pnSUlFJzRTZdkhg1csQ/brphyh67dznq8B2lo6SUmsPzSBK+368qMODvqALD7a0NW9fpNUk/2x9S2PHglU6gOOD3+2wfNQEikJGAN6Orudj5AoWQwbjAkEenXDirYoI7klUiSkdJuSOdnlmMHjXyjtv+uv9++/Q85cQjSkTpKCknBp9OzCqVSvTEtOP7AiPhkG6ktTnt8wIVsMJW8Mq3qDiodJSUtmkIuFuAEtDd60t2nQLD/BVPTbn4TOf/4BCloESUTmdiLv1taHX1PbffdJLzf3CIUlAiSselC9WZlh6YqggsCpRqPxqNNtdtbnHOzw5RqApYYSt4paBElI62Xd9IEAFKQP4MFIqA3+O9c/cvXTryGOcmrOCVC3+4zQAAEABJREFUghJxbgrpR+73+//yx2su/Or56V9it54KXikoEbsFlot4VDYFS1RBlZvvn21tqtN9tXB7Wy7mytaYCk9BKlQNqLADpeVKQYlol4ZAIQhQAhbCKpPjToGfjTn1lt3OLfPFfsLVzqO231LAClvBZylSxwzzg8su+f01Py8tjX34iGOCNgwFrLAVvINizjxUj8dbUlxUUlbu9RVptFBbS8PWta2Ntdq2YVNgCk9BKjafv1hhK3iloF0aAgUiQAlYIAtNmjsFPjt0/7nTLps9aI+dh+y9pVAVsMK2d5i5iu7M0095/MF/H3HYrFxNkO1xFaoCVtjZHtgZ4xX7/cHSoO4HmuG2NNQ2bFnb3tpk7trhVcEoJAVmBqNQS4IlCtvc5RUBVwt0SY4SsAsHOwUiMLGk5uE9Lvj12DP8Hlu/6U/hKUiFqoALZGmSpjlh/Nh/3XLjlT+41OYPVRWeglSoCjhpIgVy0O/zBQNFJaUVXp9fKYdDbU21G9XMW246kq+mABSGmkJSDApPQSpUBaxdGgKFJkAJWGgrTr47BS4ZceSivS//dPW+Ow/ZaUuBKTwFaaeg8hnL/53/uReeevD0U080bPlLgSk8BWnL6DqCsvDF4/UGiv2lpaXFJbH3iGhm3Xtr3LZe5Zc2tGtx06SaWgFow5xagSk8BalQzSO8IlBoApSAhbbi5NtFYEKg+h8TP//w5AsOq7DRxxErGIWkwBRel3ALfmfsmNF/vPZXus120AH72QdDwSgkBabw7BOVHSLx6XZgSaC0vNLf8UNEFJIqMJVieg7b2lQXyf3nSGsKTaTpNKmmVgBqRcVBhRQsCSg87dIQKFgBSsBCWXry7EVgdtUeT0+9+O7dzz8034WgAlAYCkYh9RJwgZ864rBZ/77jlr/ecO2B++f5Dq4CUBgKRiEV+KL0kn6R31cWDAbLKlR7md30HLalfmv9po9VmbU1N6hQM49n61UDalgNrik0kaYzR1YAKv5KgyUKyTzCKwKFLEAJWMirT+5dBM4YstczUy9+bI8LP1Wdhw8l1qSaWgEojC5hsZNC4MTjj/nPnbfe+Y+/nnLScSm65PCwJtXUCkBh5HAa1wztMYqL/KXB2B3B4kBp/Nmr7sw1121WoaZHtK2NtaG2ZiMaGWDS0Ygu1yAaSgNqWA1uDuXxeDVpR/EXiBV/HvNwobySJwKpBLypTnAcgcIUOHrQ5NsnfmH5vj/52ZhT9ykbnWsETaGJNJ0m1dS5ns594x8666A/X//buS899YPLLpm255RcJ6gpNJGm06SaOtfTuW18j0cVWDAYKCurLCkt9xXFfriwmWOoraWlobZx24btG1fVb16tG3i6e6dnuO2tTeH21kioPRIORSNhNW1oVwd1Sh3UTZ11iS7U5Rok1NZijqlXTaGJysorgx1v+jA8VH9SoSHQKUAJ2AnBbwgkCowqGnTpyGNenXbpG9O/d/WYUw6vnJh4NvNtDahhNbim0ESaLvMxk41QKMdG7DL8wq+e/9gDdz/1yH+/f+klBx84M7uZa0ANq8E1hSbSdNkdv9BG8/k8geKi8rLS8opBJaUV/uKS+H1BUajISyzvGrauq9+yRkVe3aaP1bShXR1U5af6T1WgOusSXWg2DaUBNawG1xSaSNOZp3hFAIFEAUrARA22EeguMK10xGUjj31qyje27v/bh/e44MrRJ54yeNoAfj6vLtGFulyDaCgNqGE1ePf52M9MYI/Ju1/0tfPvuf3mZW/P/dctN37nmxfOPuaIAfx8Xl2iC3W5BtFQGlDDavDMouPq7gI+X+yNw2WlwfLyyrKKQYFgeVEg6PMXG0a/btd5dIku1OUapFxD6ZFzsV+DG/xCoEAF0kqbEjAtJjohUOotmj1ojytGHf/fSV9ZNuOqzTN/PW/ad++d9H9/mnD2T8ac/J0RR18w/LAvDztYTRva1UGdUgd1U2ddogt1uQbRUHjmWiAYLDnisFnfuviCm//yh1deePzdBa8+8dB/bvnrH371syu/+52LL/i/L37hvHM+++lPqWlDuzqoU+qgbuqsS3ShLtcgGirX0TK+1+vx+7wlgaLSYEl5eVlFZWV5RVVZxaBguR4ZVwSCZWrFJWVq2lDTTT6dUgd1i3XW7b5giS7XIBoKTwQQSEeAEjAdJfog0F2g3BfYq2zkyYP3/PKwWd8bOfsXY0/7/fhP/mnCp9W0oV0d1Cl1UDd17n49+9YKlJWVTp0y6dijj1DN940Lv3L5d7999Y9+oJpPTRva1UGdUgd1U2dro8v2bM4fz+v1+nyxorDY7wsU+0sCxWrBkmI1bajpoE6p4FM3r5d/yJy/5GSQDwH+y8mHOnMigAACCCCAAAJ5FaAEzCt/DiZnSAQQQAABBBBAoE8BSsA+ieiAAAIIIICA3QWID4H+ClAC9leM/ggggAACCCCAgOMFKAEdv4QkgIBhYIAAAggggED/BCgB++dFbwQQQAABBBBAwB4CGUVBCZgRHxcjgAACCCCAAAJOFKAEdOKqETMCCCBgGBgggAACGQhQAmaAx6UIIIAAAggggIAzBSgBnbluhkHcCCCAAAIIIIDAgAUoAQdMx4UIIIAAAghYLcB8CGRLgBIwW5KMgwACCCCAAAIIOEaAEtAxS0WgCBgGBggggAACCGRHgBIwO46MggACCCCAAAII5EYgJ6NSAuaElUERQAABBBBAAAE7C1AC2nl1iA0BBBAwDAwQQACBHAhQAuYAlSERQAABBBBAAAF7C1AC2nt9DIP4EEAAAQQQQACBrAtQAmadlAERQAABBBDIVIDrEci1ACVgroUZHwEEEEAAAQQQsJ0AJaDtloSAEDAMDBBAAAEEEMitACVgbn0ZHQEEEEAAAQQQSE/A0l6UgJZyMxkCCCCAAAIIIGAHAUpAO6wCMSCAAAKGgQECCCBgoQAloIXYTIUAAggggAACCNhDgBLQHutgGMSBAAIIIIAAAghYJkAJaBk1EyGAAAIIINBdgH0E8iVACZgveeZFAAEEEEAAAQTyJkAJmDd6JkbAMDBAAAEEEEAgPwKUgPlxZ1YEEEAAAQQQKFQBW+RNCWiLZSAIBBBAAAEEEEDASgFKQCu1mQsBBBAwDAwQQAABGwhQAtpgEQgBAQQQQAABBBCwVoAS0Fpvw2A+BBBAAAEEEEAg7wKUgHlfAgJAAAEEEHC/ABkiYDcBSkC7rQjxIIAAAggggAACORegBMw5MRMgYBgYIIAAAgggYC8BSkB7rQfRIIAAAggggIBbBGydByWgrZeH4BBAAAEEEEAAgVwIUALmQpUxEUAAAcPAAAEEELCxACWgjReH0BBAAAEEEEAAgdwIUALmxtUwGBcBBBBAAAEEELCtACWgbZeGwBBAAAEEnCdAxAg4RYAS0CkrRZwIIIAAAggggEDWBCgBs0bJQAgYBgYIIIAAAgg4Q4AS0BnrRJQIIIAAAgggYFcBR8ZFCejIZSNoBBBAAAEEEEAgEwFKwEz0uBYBBBAwDAwQQAABBwpQAjpw0QgZAQQQQAABBBDITIASMDM/w+B6BBBAAAEEEEDAcQKUgI5bMgJGAAEEEMi/ABEg4HQBSkCnryDxI4AAAggggAAC/RagBOw3GRcgYBgYIIAAAggg4GwBSkBnrx/RI4AAAggggIBVAq6ahxLQVctJMggggAACCCCAQDoClIDpKNEHAQQQMAwMEEAAARcJUAK6aDFJBQEEEEAAAQQQSE+AEjA9J8OgHwIIIIAAAggg4BoBSkDXLCWJIIAAAghkX4AREXCrACWgW1eWvBBAAAEEEEAAgZQClIApaTiBgGFggAACCCCAgDsFKAHdua5khQACCCCAAAIDFSiI6ygBC2KZSRIBBBBAAAEEEEgUoARM1GAbAQQQMAwMEEAAgQIQoAQsgEUmRQQQQAABBBBAoKsAJWBXD8NgHwEEEEAAAQQQcL0AJaDrl5gEEUAAAQT6FqAHAoUmQAlYaCtOvggggAACCCCAgEEJyB8CBAzDAAEBBBBAAIHCEqAELKz1JlsEEEAAAQQQ2CFQ0L9TAhb08pM8AggggAACCBSmACVgYa47WSOAgGFggAACCBSwACVgAS8+qSOAAAIIIIBAoQoUbglYqCtO3ggggAACCCCAAO8I5s8AAggggEAhCZArAgiYAtwFNB14RQABBBBAAAEECkiAErCAFptUDQMDBBBAAAEEEIgJUALGFPhCAAEEEEAAAfcKkFkSAUrAJCgcQgABBBBAAAEE3C1ACeju9SU7BBAwDAwQQAABBHoIUAL2IOEAAggggAACCCDgdgH3l4BuX0HyQwABBBBAAAEE+i1ACdhvMi5AAAEEELC/ABEigEDvApSAvftwFgEEEEAAAQQQcKEAJaALF5WUDAMDBBBAAAEEEOhNgBKwNx3OIYAAAggggIBzBIi0HwKUgP3AoisCCCCAAAIIIOAOAUpAd6wjWSCAgGFggAACCCCQtgAlYNpUdEQAAQQQQAABBNwi4J4S0C0rQh4IIIAAAggggEDOBSgBc07MBAgggAACuRNgZAQQGJgAJeDA3LgKAQQQQAABBBBwsAAloIMXj9ANAwMEEEAAAQQQGIgAJeBA1LgGAQQQQAABBPInwMxZEKAEzAIiQyCAAAIIIIAAAs4SoAR01noRLQIIGAYGCCCAAAIZC1ACZkzIAAgggAACCCCAgNMEnFcCOk2YeBFAAAEEEEAAAdsJUALabkkICAEEEECgpwBHEEAguwKUgNn1ZDQEEEAAAQQQQMABApSADlgkQjQMDBBAAAEEEEAgmwKUgNnUZCwEEEAAAQQQyJ4AI+VQgBIwh7gMjQACCCCAAAII2FOAEtCe60JUCCBgGBgggAACCORMgBIwZ7QMjAACCCCAAAII2FXAviWgXcWICwEEEEAAAQQQcLwAJaDjl5AEEEAAATcJkAsCCFgjQAlojTOzIIAAAggggAACNhKgBLTRYhCKYWCAAAIIIIAAAlYIUAJaocwcCCCAAAIIIJBagDN5EKAEzAM6UyKAAAIIIIAAAvkVoATMrz+zI4CAYWCAAAIIIGC5ACWg5eRMiAACCCCAAAII5Fsg/yVgvgWYHwEEEEAAAQQQKDgBSsCCW3ISRgABBOwgQAwIIJBfAUrA/PozOwIIIIAAAgggkAcBSsA8oDOlYWCAAAIIIIAAAvkUoATMpz5zI4AAAgggUEgC5GojAUpAGy0GoSCAAAIIIIAAAtYIUAJa48wsCCBgGBgggAACCNhGgBLQNktBIAgggAACCCCAgFUC1pWAVmXEPAgggAACCCCAAAJ9CFAC9gHEaQQQQACBTAS4FgEE7ClACWjPdSEqBBBAAAEEEEAghwKUgDnEZWjDwAABBBBAAAEE7ChACWjHVSEmBBBAAAEEnCxA7A4QoAR0wCIRIgIIIIAAAgggkF0BSsDsejIaAggYBgYIIIAAArYXoAS0/RIRIAIIIIAAAgggkG2B7JeA2Y6Q8frm3owAAAgNSURBVBBAAAEEEEAAAQSyLEAJmGVQhkMAAQQKU4CsEUDAWQKUgM5aL6JFAAEEEEAAAQSyIEAJmAVEhjAMDBBAAAEEEEDASQKUgE5aLWJFAAEEEEDATgLE4mABSkAHLx6hI4AAAggggAACAxOgBByYG1chgIBhYIAAAggg4FgBSkDHLh2BI4AAAggggAACAxUYeAk40Bm5DgEEEEAAAQQQQCDPApSAeV4ApkcAAQScJUC0CCDgDgFKQHesI1kggAACCCCAAAL9EKAE7AcWXQ0DAwQQQAABBBBwgwAloBtWkRwQQAABBBDIpQBju1CAEtCFi0pKCCCAAAIIIIBA7wKUgL37cBYBBAwDAwQQQAAB1wlQArpuSUkIAQQQQAABBBDoS6DvErCvETiPAAIIIIAAAggg4DABSkCHLRjhIoAAAtYIMAsCCLhbgBLQ3etLdggggAACCCCAQBIBSsAkKBwyDAwQQAABBBBAwM0ClIBuXl1yQwABBBBAoD8C9C0gAUrAAlpsUkUAAQQQQAABBEwBSkDTgVcEEDAMDBBAAAEECkaAErBglppEEUAAAQQQQACBHQI7S8AdR/gdAQQQQAABBBBAwOUClIAuX2DSQwABBHoX4CwCCBSmACVgYa47WSOAAAIIIIBAQQtQAhb08hsG6SOAAAIIIIBAIQpQAhbiqpMzAggggEBhC5A9AgYlIH8IEEAAAQQQQACBghOgBCy4JSdhBAwIEEAAAQQKXoASsOD/CACAAAIIIIAAAoUg0DVHSsCuHuwhgAACCCCAAAIFIEAJWACLTIoIIICAYWCAAAIIJApQAiZqsI0AAggggAACCBSEACVgQSyzYZAmAggggAACCCCwU4AScKcFWwgggAACCLhLgGwQSClACZiShhMIIIAAAggggIBbBSgB3bqy5IWAYWCAAAIIIIBACgFKwBQwHEYAAQQQQAABBJwokF7MlIDpOdELAQQQQAABBBBwkQAloIsWk1QQQAABw8AAAQQQSEeAEjAdJfoggAACCCCAAAKuEqAEdNVyGgbpIIAAAggggAACfQtQAvZtRA8EEEAAAQTsLUB0CPRbgBKw32RcgAACCCCAAAIIOF2AEtDpK0j8CBgGBggggAACCPRTgBKwn2B0RwABBBBAAAEE7CCQWQyUgJn5cTUCCCCAAAIIIOBAAUpABy4aISOAAAKGgQECCCCQiQAlYCZ6XIsAAggggAACCDhSgBLQkctmGISNAAIIIIAAAggMXIAScOB2XIkAAggggIC1AsyGQNYEKAGzRslACCCAAAIIIICAUwQoAZ2yUsSJgGFggAACCCCAQJYEKAGzBMkwCCCAAAIIIIBALgRyMyYlYG5cGRUBBBBAAAEEELCxACWgjReH0BBAAAHDwAABBBDIhQAlYC5UGRMBBBBAAAEEELC1ACWgrZfHMAgPAQQQQAABBBDIvgAlYPZNGREBBBBAAIHMBLgagZwLUALmnJgJEEAAAQQQQAABuwlQAtptRYgHAcPAAAEEEEAAgRwLUALmGJjhEUAAAQQQQACBdASs7UMJaK03syGAAAIIIIAAAjYQoAS0wSIQAgIIIGAYGCCAAAJWClACWqnNXAgggAACCCCAgC0EKAFtsQyGQRgIIIAAAggggIB1ApSA1lkzEwIIIIAAAl0F2EMgbwKUgHmjZ2IEEEAAAQQQQCBfApSA+ZJnXgQMAwMEEEAAAQTyJEAJmCd4pkUAAQQQQACBwhSwR9aUgPZYB6JAAAEEEEAAAQQsFKAEtBCbqRBAAAHDwAABBBCwgwAloB1WgRgQQAABBBBAAAFLBSgBLeU2DKZDAAEEEEAAAQTyL0AJmP81IAIEEEAAAbcLkB8CthOgBLTdkhAQAggggAACCCCQawFKwFwLMz4ChoEBAggggAACNhOgBLTZghAOAggggAACCLhDwN5ZUALae32IDgEEEEAAAQQQyIEAJWAOUBkSAQQQMAwMEEAAATsLUALaeXWIDQEEEEAAAQQQyIkAJWBOWA2DYRFAAAEEEEAAAfsKUALad22IDAEEEEDAaQLEi4BjBCgBHbNUBIoAAggggAACCGRLgBIwW5KMg4BhYIAAAggggIBDBCgBHbJQhIkAAggggAAC9hRwZlSUgM5cN6JGAAEEEEAAAQQyEKAEzACPSxFAAAHDwAABBBBwogAloBNXjZgRQAABBBBAAIGMBCgBM+IzDC5HAAEEEEAAAQScJ0AJ6Lw1I2IEEEAAgXwLMD8CjhegBHT8EpIAAggggAACCCDQXwFKwP6K0R8Bw8AAAQQQQAABhwtQAjp8AQkfAQQQQAABBKwRcNcslIDuWk+yQQABBBBAAAEE0hCgBEwDiS4IIICAYWCAAAIIuEmAEtBNq0kuCCCAAAIIIIBAWgKUgGkxGQbdEEAAAQQQQAAB9whQArpnLckEAQQQQCDbAoyHgGsFKAFdu7QkhgACCCCAAAIIpBKgBEwlw3EEDAMDBBBAAAEEXCpACejShSUtBBBAAAEEEBiYQGFcRQlYGOtMlggggAACCCCAQIIAJWACBpsIIICAYWCAAAIIFIIAJWAhrDI5IoAAAggggAACXQQoAbtwGAa7CCCAAAIIIICA+wUoAd2/xmSIAAIIINCXAOcRKDgBSsCCW3ISRgABBBBAAAEEKAH5M4CAYWCAAAIIIIBAgQlQAhbYgpMuAggggAACCJgChf1KCVjY60/2CCCAAAIIIFCQApSABbnsJI0AAoaBAQIIIFDIAv8fAAD///itKvIAAAAGSURBVAMACd1G8SKiqI4AAAAASUVORK5CYII= \ No newline at end of file diff --git a/docs/verification/login-page.png b/docs/verification/login-page.png new file mode 100644 index 0000000..1072809 Binary files /dev/null and b/docs/verification/login-page.png differ diff --git a/docs/verification/mood-page.png b/docs/verification/mood-page.png new file mode 100644 index 0000000..e1bd8fc Binary files /dev/null and b/docs/verification/mood-page.png differ diff --git a/docs/verification/parent-page.png b/docs/verification/parent-page.png new file mode 100644 index 0000000..eb5d6d4 Binary files /dev/null and b/docs/verification/parent-page.png differ diff --git a/docs/verification/profile-page.png b/docs/verification/profile-page.png new file mode 100644 index 0000000..ffd38e4 Binary files /dev/null and b/docs/verification/profile-page.png differ diff --git a/docs/verification/search-page.png b/docs/verification/search-page.png new file mode 100644 index 0000000..879f476 Binary files /dev/null and b/docs/verification/search-page.png differ diff --git a/docs/verification/settings-page.png b/docs/verification/settings-page.png new file mode 100644 index 0000000..40232d9 Binary files /dev/null and b/docs/verification/settings-page.png differ diff --git a/docs/verification/stickers-page.png b/docs/verification/stickers-page.png new file mode 100644 index 0000000..6247790 Binary files /dev/null and b/docs/verification/stickers-page.png differ diff --git a/docs/verification/teacher-page.png b/docs/verification/teacher-page.png new file mode 100644 index 0000000..7c562e5 Binary files /dev/null and b/docs/verification/teacher-page.png differ diff --git a/docs/verification/templates-page.png b/docs/verification/templates-page.png new file mode 100644 index 0000000..61e100c Binary files /dev/null and b/docs/verification/templates-page.png differ