docs: 更新协议文档,删除废弃计划文档
- 更新 http-error-codes, user-points-chat-data-protocol - 更新 divination-run-protocol, profile-protocol - 删除废弃的后端和前端设计计划文档
This commit is contained in:
@@ -3,6 +3,7 @@ import 'dart:math';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:onboarding_overlay/onboarding_overlay.dart';
|
||||
|
||||
import '../../../../l10n/app_localizations.dart';
|
||||
import '../../../../shared/theme/design_tokens.dart';
|
||||
@@ -16,6 +17,7 @@ import '../../../../shared/widgets/date_time_picker/date_time_picker_bottom_shee
|
||||
import '../../../../shared/widgets/toast/toast.dart';
|
||||
import '../../../../shared/widgets/toast/toast_type.dart';
|
||||
import '../../data/models/divination_backend_models.dart';
|
||||
import '../../data/apis/divination_api.dart';
|
||||
import '../../data/models/divination_params.dart';
|
||||
import '../../data/models/divination_result.dart';
|
||||
import '../../data/services/divination_run_service.dart';
|
||||
@@ -26,11 +28,13 @@ class ManualDivinationScreen extends StatefulWidget {
|
||||
super.key,
|
||||
required this.params,
|
||||
required this.runService,
|
||||
this.divinationApi,
|
||||
required this.onCompleted,
|
||||
});
|
||||
|
||||
final DivinationParams params;
|
||||
final DivinationRunService runService;
|
||||
final DivinationApi? divinationApi;
|
||||
final Future<void> Function(DivinationResultData result) onCompleted;
|
||||
|
||||
@override
|
||||
@@ -43,6 +47,16 @@ class _ManualDivinationScreenState extends State<ManualDivinationScreen>
|
||||
final List<YaoType?> _selectedYaos = List<YaoType?>.filled(6, null);
|
||||
late final AnimationController _blinkController;
|
||||
bool _submitting = false;
|
||||
final GlobalKey<OnboardingState> _onboardingKey =
|
||||
GlobalKey<OnboardingState>();
|
||||
final ScrollController _scrollController = ScrollController();
|
||||
final GlobalKey _timeCardKey = GlobalKey();
|
||||
final GlobalKey _yaoCardKey = GlobalKey();
|
||||
final GlobalKey _analyzeButtonKey = GlobalKey();
|
||||
final FocusNode _guideStep1Focus = FocusNode();
|
||||
final FocusNode _guideStep2Focus = FocusNode();
|
||||
final FocusNode _guideStep3Focus = FocusNode();
|
||||
final FocusNode _guideStep4Focus = FocusNode();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@@ -56,7 +70,12 @@ class _ManualDivinationScreenState extends State<ManualDivinationScreen>
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_scrollController.dispose();
|
||||
_blinkController.dispose();
|
||||
_guideStep1Focus.dispose();
|
||||
_guideStep2Focus.dispose();
|
||||
_guideStep3Focus.dispose();
|
||||
_guideStep4Focus.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@@ -66,6 +85,55 @@ class _ManualDivinationScreenState extends State<ManualDivinationScreen>
|
||||
Widget build(BuildContext context) {
|
||||
final colors = Theme.of(context).colorScheme;
|
||||
final l10n = AppLocalizations.of(context)!;
|
||||
final guideSteps = [
|
||||
OnboardingStep(
|
||||
focusNode: _guideStep1Focus,
|
||||
titleText: l10n.manualGuideStep1Title,
|
||||
bodyText: l10n.manualGuideStep1Body,
|
||||
titleTextColor: Colors.white,
|
||||
bodyTextColor: Colors.white,
|
||||
hasArrow: false,
|
||||
hasLabelBox: true,
|
||||
fullscreen: true,
|
||||
overlayColor: Colors.black.withValues(alpha: 0.7),
|
||||
),
|
||||
OnboardingStep(
|
||||
focusNode: _guideStep2Focus,
|
||||
titleText: l10n.manualGuideStep2Title,
|
||||
bodyText: l10n.manualGuideStep2Body,
|
||||
titleTextColor: Colors.white,
|
||||
bodyTextColor: Colors.white,
|
||||
hasArrow: true,
|
||||
hasLabelBox: true,
|
||||
arrowPosition: ArrowPosition.top,
|
||||
delay: const Duration(milliseconds: 320),
|
||||
overlayColor: Colors.black.withValues(alpha: 0.7),
|
||||
),
|
||||
OnboardingStep(
|
||||
focusNode: _guideStep3Focus,
|
||||
titleText: l10n.manualGuideStep3Title,
|
||||
bodyText: l10n.manualGuideStep3Body,
|
||||
titleTextColor: Colors.white,
|
||||
bodyTextColor: Colors.white,
|
||||
hasArrow: true,
|
||||
hasLabelBox: true,
|
||||
arrowPosition: ArrowPosition.top,
|
||||
delay: const Duration(milliseconds: 320),
|
||||
overlayColor: Colors.black.withValues(alpha: 0.7),
|
||||
),
|
||||
OnboardingStep(
|
||||
focusNode: _guideStep4Focus,
|
||||
titleText: l10n.manualGuideStep4Title,
|
||||
bodyText: l10n.manualGuideStep4Body,
|
||||
titleTextColor: Colors.white,
|
||||
bodyTextColor: Colors.white,
|
||||
hasArrow: true,
|
||||
hasLabelBox: true,
|
||||
arrowPosition: ArrowPosition.top,
|
||||
delay: const Duration(milliseconds: 320),
|
||||
overlayColor: Colors.black.withValues(alpha: 0.7),
|
||||
),
|
||||
];
|
||||
return Scaffold(
|
||||
backgroundColor: colors.surface,
|
||||
appBar: AppBar(
|
||||
@@ -74,49 +142,80 @@ class _ManualDivinationScreenState extends State<ManualDivinationScreen>
|
||||
backgroundColor: colors.surface,
|
||||
surfaceTintColor: colors.surface,
|
||||
),
|
||||
body: SingleChildScrollView(
|
||||
padding: const EdgeInsets.all(AppSpacing.xl),
|
||||
child: Column(
|
||||
children: [
|
||||
_buildInstruction(),
|
||||
const SizedBox(height: AppSpacing.lg),
|
||||
_TimeCard(selectedTime: _selectedTime, onPickTime: _pickTime),
|
||||
const SizedBox(height: AppSpacing.lg),
|
||||
_YaoSelectionCard(
|
||||
selectedYaos: _selectedYaos,
|
||||
blinkAnimation: _blinkController,
|
||||
onSelect: _onSelectYao,
|
||||
onNeedTip: _showOrderTip,
|
||||
),
|
||||
const SizedBox(height: AppSpacing.xl),
|
||||
AnimatedBuilder(
|
||||
animation: _blinkController,
|
||||
builder: (context, _) {
|
||||
final base = colors.primary;
|
||||
return SizedBox(
|
||||
width: double.infinity,
|
||||
height: 50,
|
||||
child: FilledButton(
|
||||
onPressed: _allSelected && !_submitting ? _submitRun : null,
|
||||
style: FilledButton.styleFrom(
|
||||
backgroundColor: _allSelected
|
||||
? base.withValues(
|
||||
alpha: 0.6 + _blinkController.value * 0.4,
|
||||
)
|
||||
: base,
|
||||
),
|
||||
child: _submitting
|
||||
? const SizedBox(
|
||||
width: 18,
|
||||
height: 18,
|
||||
child: CircularProgressIndicator(strokeWidth: 2),
|
||||
)
|
||||
: Text(l10n.manualStartResolve),
|
||||
body: Onboarding(
|
||||
key: _onboardingKey,
|
||||
steps: guideSteps,
|
||||
onChanged: _onGuideStepChanged,
|
||||
child: SingleChildScrollView(
|
||||
controller: _scrollController,
|
||||
padding: const EdgeInsets.all(AppSpacing.xl),
|
||||
child: Column(
|
||||
children: [
|
||||
_buildInstruction(),
|
||||
const SizedBox(height: AppSpacing.lg),
|
||||
Container(
|
||||
key: _timeCardKey,
|
||||
child: Focus(
|
||||
focusNode: _guideStep2Focus,
|
||||
child: _TimeCard(
|
||||
selectedTime: _selectedTime,
|
||||
onPickTime: _pickTime,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: AppSpacing.lg),
|
||||
Container(
|
||||
key: _yaoCardKey,
|
||||
child: Focus(
|
||||
focusNode: _guideStep3Focus,
|
||||
child: _YaoSelectionCard(
|
||||
selectedYaos: _selectedYaos,
|
||||
blinkAnimation: _blinkController,
|
||||
onSelect: _onSelectYao,
|
||||
onNeedTip: _showOrderTip,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: AppSpacing.xl),
|
||||
Container(
|
||||
key: _analyzeButtonKey,
|
||||
child: Focus(
|
||||
focusNode: _guideStep4Focus,
|
||||
child: AnimatedBuilder(
|
||||
animation: _blinkController,
|
||||
builder: (context, _) {
|
||||
final base = colors.primary;
|
||||
return SizedBox(
|
||||
width: double.infinity,
|
||||
height: 50,
|
||||
child: FilledButton(
|
||||
onPressed: _allSelected && !_submitting
|
||||
? _submitRun
|
||||
: null,
|
||||
style: FilledButton.styleFrom(
|
||||
backgroundColor: _allSelected
|
||||
? base.withValues(
|
||||
alpha: 0.6 + _blinkController.value * 0.4,
|
||||
)
|
||||
: base,
|
||||
),
|
||||
child: _submitting
|
||||
? const SizedBox(
|
||||
width: 18,
|
||||
height: 18,
|
||||
child: CircularProgressIndicator(
|
||||
strokeWidth: 2,
|
||||
),
|
||||
)
|
||||
: Text(l10n.manualStartResolve),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -126,26 +225,40 @@ class _ManualDivinationScreenState extends State<ManualDivinationScreen>
|
||||
final l10n = AppLocalizations.of(context)!;
|
||||
return DivinationInstructionCard(
|
||||
text: l10n.manualYaoInstruction,
|
||||
onTap: () {
|
||||
showDialog<void>(
|
||||
context: context,
|
||||
builder: (_) {
|
||||
return DivinationGuideDialog(
|
||||
title: l10n.manualSelectYaoTitle,
|
||||
guideImages: const [
|
||||
['assets/images/tutorial/tutorial_1.png'],
|
||||
['assets/images/tutorial/tutorial_2.png'],
|
||||
['assets/images/tutorial/tutorial_3.png'],
|
||||
],
|
||||
instructions: [
|
||||
l10n.divinationManualGuideStep1,
|
||||
l10n.divinationManualGuideStep2,
|
||||
l10n.divinationManualGuideStep3,
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
onTap: _showGuide,
|
||||
);
|
||||
}
|
||||
|
||||
void _showGuide() {
|
||||
_scrollToGuideStep(0);
|
||||
Future<void>.delayed(const Duration(milliseconds: 120), () {
|
||||
if (!mounted) {
|
||||
return;
|
||||
}
|
||||
_onboardingKey.currentState?.show();
|
||||
});
|
||||
}
|
||||
|
||||
void _onGuideStepChanged(int currentIndex) {
|
||||
_scrollToGuideStep(currentIndex + 1);
|
||||
}
|
||||
|
||||
void _scrollToGuideStep(int stepIndex) {
|
||||
final GlobalKey? targetKey = switch (stepIndex) {
|
||||
1 => _timeCardKey,
|
||||
2 => _yaoCardKey,
|
||||
3 => _analyzeButtonKey,
|
||||
_ => null,
|
||||
};
|
||||
final targetContext = targetKey?.currentContext;
|
||||
if (targetContext == null) {
|
||||
return;
|
||||
}
|
||||
Scrollable.ensureVisible(
|
||||
targetContext,
|
||||
duration: const Duration(milliseconds: 260),
|
||||
curve: Curves.easeOut,
|
||||
alignment: 0.12,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -253,6 +366,7 @@ class _ManualDivinationScreenState extends State<ManualDivinationScreen>
|
||||
params: widget.params.copyWith(divinationTime: _selectedTime),
|
||||
yaoStates: _selectedYaos.cast<YaoType>(),
|
||||
runService: widget.runService,
|
||||
divinationApi: widget.divinationApi,
|
||||
onCompleted: widget.onCompleted,
|
||||
),
|
||||
),
|
||||
@@ -337,7 +451,10 @@ class _YaoSelectionCard extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
final colors = Theme.of(context).colorScheme;
|
||||
final l10n = AppLocalizations.of(context)!;
|
||||
final rowNames = DivinationTerms.yaoNames.reversed.toList();
|
||||
final rowNames = List<String>.generate(
|
||||
6,
|
||||
(i) => DivinationTerms.yaoName(l10n, i),
|
||||
).reversed.toList();
|
||||
return Card(
|
||||
margin: EdgeInsets.zero,
|
||||
color: colors.surface,
|
||||
|
||||
Reference in New Issue
Block a user