feat: add Flutter mobile clients and staff delivery API
This commit is contained in:
143
apps/service_app/lib/ui/features/preflight/preflight_page.dart
Normal file
143
apps/service_app/lib/ui/features/preflight/preflight_page.dart
Normal file
@@ -0,0 +1,143 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
import '../../../app/dependencies.dart';
|
||||
import '../../../data/repositories/service_repository.dart';
|
||||
import '../../../domain/models/service_models.dart';
|
||||
|
||||
class PreflightPage extends StatefulWidget {
|
||||
const PreflightPage({required this.session, required this.repository, super.key});
|
||||
|
||||
final StaffSession session;
|
||||
final ServiceRepository repository;
|
||||
|
||||
@override
|
||||
State<PreflightPage> createState() => _PreflightPageState();
|
||||
}
|
||||
|
||||
class _PreflightPageState extends State<PreflightPage> {
|
||||
late Future<PreflightResult> _future;
|
||||
bool _busy = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_future = widget.repository.preflight();
|
||||
}
|
||||
|
||||
Future<void> _attendance(String action) async {
|
||||
setState(() => _busy = true);
|
||||
try {
|
||||
await widget.repository.attendance(action, widget.session.deviceIdentity);
|
||||
setState(() => _future = widget.repository.preflight());
|
||||
} catch (error) {
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(error.toString())));
|
||||
}
|
||||
} finally {
|
||||
if (mounted) setState(() => _busy = false);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => Scaffold(
|
||||
appBar: AppBar(title: const Text('作业前检查')),
|
||||
body: FutureBuilder<PreflightResult>(
|
||||
future: _future,
|
||||
builder: (context, snapshot) {
|
||||
if (!snapshot.hasData) {
|
||||
if (snapshot.hasError) return Center(child: Text(snapshot.error.toString()));
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
}
|
||||
final result = snapshot.data!;
|
||||
return ListView(
|
||||
padding: const EdgeInsets.all(18),
|
||||
children: [
|
||||
Card(
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(22),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(roleName(result.roleCode), style: const TextStyle(color: Colors.white70)),
|
||||
const SizedBox(height: 6),
|
||||
Text(
|
||||
result.canWork ? '可以开始今日作业' : '仍有前置条件未完成',
|
||||
style: const TextStyle(
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.w900,
|
||||
fontSize: 24,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
...result.checks.entries.map((entry) {
|
||||
final detail = entry.value is Map
|
||||
? Map<Object?, Object?>.from(entry.value as Map)
|
||||
: const <Object?, Object?>{};
|
||||
final status = detail['status']?.toString() ?? 'blocked';
|
||||
final passed = status == 'passed';
|
||||
return Card(
|
||||
child: ListTile(
|
||||
leading: Icon(
|
||||
passed
|
||||
? Icons.check_circle
|
||||
: status == 'not_configured'
|
||||
? Icons.info_outline
|
||||
: Icons.cancel,
|
||||
color: passed
|
||||
? Colors.green
|
||||
: status == 'not_configured'
|
||||
? Colors.orange
|
||||
: Colors.red,
|
||||
),
|
||||
title: Text(_label(entry.key)),
|
||||
subtitle: Text(
|
||||
status == 'not_configured'
|
||||
? '平台暂未启用,不冒充校验通过'
|
||||
: passed
|
||||
? '已通过服务端校验'
|
||||
: '未通过',
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
const SizedBox(height: 18),
|
||||
if (result.workStatus != 'on_duty')
|
||||
ElevatedButton.icon(
|
||||
onPressed: _busy ? null : () => _attendance('clock_in'),
|
||||
icon: const Icon(Icons.location_on),
|
||||
label: const Text('定位并上班打卡'),
|
||||
)
|
||||
else ...[
|
||||
ElevatedButton(
|
||||
onPressed: result.canWork ? () => context.go('/work') : null,
|
||||
child: const Text('进入工作台'),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: _busy ? null : () => _attendance('clock_out'),
|
||||
child: const Text('下班打卡'),
|
||||
),
|
||||
],
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
String _label(String value) => switch (value) {
|
||||
'account' => '账号状态',
|
||||
'role' => '岗位',
|
||||
'organization' => '所属组织',
|
||||
'credential' => '人员资质',
|
||||
'attendance' => '上班状态',
|
||||
'daily_training' => '每日培训',
|
||||
'service_area' => '服务区域',
|
||||
'authorized_device' => '授权设备',
|
||||
_ => value,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user