feat(apps): establish shared mobile design system

This commit is contained in:
2026-08-10 23:31:44 +08:00
parent 7242048abf
commit a2383fc8a1
34 changed files with 2007 additions and 751 deletions

View File

@@ -5,6 +5,7 @@ import 'package:uuid/uuid.dart';
import '../../../app/dependencies.dart';
import '../../../data/offline/encrypted_draft_store.dart';
import '../../../data/repositories/service_repository.dart';
import '../../core/widgets.dart';
class EvidencePage extends StatefulWidget {
const EvidencePage({
@@ -160,66 +161,108 @@ class _EvidencePageState extends State<EvidencePage> {
@override
Widget build(BuildContext context) => Scaffold(
appBar: AppBar(title: const Text('现场取证')),
body: ListView(
padding: const EdgeInsets.all(18),
children: [
const Card(
child: Padding(
padding: EdgeInsets.all(18),
body: SafeArea(
top: false,
child: ListView(
padding: const EdgeInsets.fromLTRB(20, 12, 20, 32),
children: [
Container(
padding: const EdgeInsets.all(18),
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.primaryContainer,
borderRadius: BorderRadius.circular(16),
),
child: Row(
children: [
Icon(Icons.lock_outline),
SizedBox(width: 12),
Expanded(child: Text('照片与签名先按当前账号加密暂存;上传成功前仅显示“已暂存”。')),
Icon(Icons.lock_outline, color: Theme.of(context).colorScheme.primary),
const SizedBox(width: 12),
const Expanded(child: Text('照片与签名先按当前账号加密暂存;上传成功前仅显示“已暂存”。')),
],
),
),
),
const SizedBox(height: 10),
..._requiredStages.map(
(stage) => Card(
child: ListTile(
leading: Icon(
_evidence.containsKey(stage) ? Icons.check_circle : Icons.camera_alt_outlined,
color: _evidence.containsKey(stage) ? Colors.green : null,
),
title: Text(_stageName(stage)),
subtitle: Text(_evidence.containsKey(stage) ? '已加密暂存' : '尚未采集'),
trailing: TextButton(
onPressed: _busy ? null : () => _capture(stage),
child: Text(_evidence.containsKey(stage) ? '重拍' : '拍摄'),
),
const SizedBox(height: 28),
SectionHeader(
title: '取证进度',
description: '已完成 ${_evidence.length}/${_requiredStages.length} 项必需材料',
),
const SizedBox(height: 12),
LinearProgressIndicator(
value: _requiredStages.isEmpty ? 0 : _evidence.length / _requiredStages.length,
minHeight: 6,
borderRadius: BorderRadius.circular(3),
),
const SizedBox(height: 16),
SurfaceSection(
padding: EdgeInsets.zero,
child: Column(
children: [
for (var index = 0; index < _requiredStages.length; index++) ...[
ListTile(
minTileHeight: 68,
leading: Icon(
_evidence.containsKey(_requiredStages[index])
? Icons.check_circle_outline
: Icons.camera_alt_outlined,
color: _evidence.containsKey(_requiredStages[index])
? Theme.of(context).colorScheme.secondary
: Theme.of(context).colorScheme.onSurfaceVariant,
),
title: Text(_stageName(_requiredStages[index])),
subtitle: Text(
_evidence.containsKey(_requiredStages[index]) ? '已加密暂存' : '尚未采集',
),
trailing: TextButton(
onPressed: _busy ? null : () => _capture(_requiredStages[index]),
child: Text(_evidence.containsKey(_requiredStages[index]) ? '重拍' : '拍摄'),
),
),
if (index < _requiredStages.length - 1) const Divider(indent: 56),
],
],
),
),
),
const SizedBox(height: 12),
TextField(
controller: _result,
maxLines: 4,
onChanged: (_) => _save(),
decoration: const InputDecoration(labelText: '现场结果说明'),
),
const SizedBox(height: 12),
DropdownButtonFormField<String>(
initialValue: _conclusion,
decoration: const InputDecoration(labelText: '结论'),
items: const [
DropdownMenuItem(value: 'qualified', child: Text('合格')),
DropdownMenuItem(value: 'noncompliant', child: Text('不合格')),
DropdownMenuItem(value: 'high_risk', child: Text('高风险')),
],
onChanged: (value) {
if (value == null) return;
setState(() => _conclusion = value);
_save();
},
),
const SizedBox(height: 22),
ElevatedButton(
onPressed: _busy ? null : _submit,
child: Text(_busy ? '正在上传并等待服务端确认…' : '在线提交结果'),
),
],
const SizedBox(height: 28),
const SectionHeader(title: '作业结论', description: '说明现场处理结果,并选择对应结论'),
const SizedBox(height: 12),
TextField(
controller: _result,
maxLines: 4,
minLines: 3,
onChanged: (_) => _save(),
decoration: const InputDecoration(
labelText: '现场结果说明',
helperText: '请填写处理过程、现场状态和后续建议',
alignLabelWithHint: true,
),
),
const SizedBox(height: 16),
DropdownButtonFormField<String>(
initialValue: _conclusion,
decoration: const InputDecoration(labelText: '结论'),
items: const [
DropdownMenuItem(value: 'qualified', child: Text('合格')),
DropdownMenuItem(value: 'noncompliant', child: Text('不合格')),
DropdownMenuItem(value: 'high_risk', child: Text('高风险')),
],
onChanged: (value) {
if (value == null) return;
setState(() => _conclusion = value);
_save();
},
),
const SizedBox(height: 28),
FilledButton.icon(
onPressed: _busy ? null : _submit,
icon: _busy
? const SizedBox.square(
dimension: 20,
child: CircularProgressIndicator(strokeWidth: 2),
)
: const Icon(Icons.cloud_upload_outlined),
label: Text(_busy ? '正在上传并等待服务端确认…' : '在线提交结果'),
),
],
),
),
);