对接连续任务列表接口和连续任务下发,便捷操作中暂停任务的对接,底部导航tab项的设置根据用户角色显示和设置

This commit is contained in:
2026-11-11 16:51:21 +08:00
parent 47f518f94c
commit 985bcf4b55
54 changed files with 5017 additions and 0 deletions

View File

@@ -0,0 +1,209 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../../../../core/localization/app_localizations.dart';
import '../../../v2/device_list/presentation/widgets/autonomous/work_ui.dart';
import '../cubit/area_task_create_cubit.dart';
class AreaDispatchSection extends StatelessWidget {
const AreaDispatchSection({super.key, required this.onReload});
final VoidCallback onReload;
@override
Widget build(BuildContext context) {
final cubit = context.read<AreaTaskCreateCubit>();
final state = context.watch<AreaTaskCreateCubit>().state;
String text(String key) =>
AppLocalizations.of(context).translate('work_v2.plan_create.$key');
Widget heading(String label, IconData icon) => Row(
children: [
Icon(icon, color: kWtBlue, size: 18),
const SizedBox(width: 8),
Expanded(
child: Text(
label,
style: const TextStyle(
fontSize: 13,
fontWeight: FontWeight.w800,
color: kWtNavy,
),
),
),
],
);
Widget hint(String label) => Text(
label,
style: const TextStyle(fontSize: 12, color: kWtGray, height: 1.5),
);
if (state.loading) {
return const Padding(
padding: EdgeInsets.all(24),
child: Center(child: CircularProgressIndicator(strokeWidth: 2)),
);
}
if (!state.loaded) {
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
hint(state.loadError ?? text('area_load_hint')),
Align(
alignment: Alignment.centerLeft,
child: TextButton.icon(
onPressed: onReload,
icon: const Icon(Icons.refresh_rounded),
label: Text(text('area_reload')),
),
),
],
);
}
final routes = state.availableRoutes;
final names = {for (final route in state.routes) route.id: route.name};
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Row(
children: [
Expanded(
child: heading(
text('area_select_title'),
Icons.crop_free_rounded,
),
),
IconButton(
tooltip: text('area_reload'),
onPressed: state.isSubmitting ? null : onReload,
icon: const Icon(Icons.refresh_rounded, size: 19, color: kWtGray),
),
],
),
hint(text('area_select_hint')),
const SizedBox(height: 8),
if (state.regions.isEmpty)
hint(text('area_empty'))
else
Wrap(
spacing: 8,
runSpacing: 6,
children: [
for (final region in state.regions)
FilterChip(
label: Text(
region.name.isEmpty ? '#${region.id}' : region.name,
),
selected: state.selectedRegions.contains(region.id),
onSelected: state.isSubmitting
? null
: (_) => cubit.toggleRegion(region.id!),
selectedColor: kWtSoftBlue,
checkmarkColor: kWtBlue,
),
],
),
const SizedBox(height: 18),
heading(text('area_routes_title'), Icons.alt_route_rounded),
const SizedBox(height: 6),
hint(text('area_routes_hint')),
const SizedBox(height: 8),
if (state.selectedRegions.isEmpty)
hint(text('area_choose_first'))
else if (routes.isEmpty)
hint(text('area_routes_empty'))
else
Wrap(
spacing: 8,
runSpacing: 6,
children: [
for (final route in routes)
FilterChip(
label: Text(route.name.isEmpty ? '#${route.id}' : route.name),
selected: state.orderedRouteIds.contains(route.id),
onSelected: state.isSubmitting
? null
: (_) => cubit.toggleRoute(route.id),
selectedColor: kWtSoftBlue,
checkmarkColor: kWtBlue,
),
],
),
const SizedBox(height: 18),
heading(text('area_order_title'), Icons.format_list_numbered_rounded),
const SizedBox(height: 6),
hint(text('area_order_hint')),
const SizedBox(height: 8),
if (state.orderedRouteIds.isEmpty)
hint(text('area_order_empty'))
else
ReorderableListView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
buildDefaultDragHandles: false,
itemCount: state.orderedRouteIds.length,
onReorder: cubit.reorder,
itemBuilder: (context, index) {
final id = state.orderedRouteIds[index];
return Container(
key: ValueKey('area_task_route_$id'),
margin: const EdgeInsets.only(bottom: 8),
decoration: BoxDecoration(
color: kWtSegBg,
borderRadius: BorderRadius.circular(10),
border: Border.all(color: kWtBorder),
),
child: ListTile(
dense: true,
contentPadding: const EdgeInsets.only(left: 10, right: 2),
leading: CircleAvatar(
radius: 14,
backgroundColor: kWtBlue,
child: Text(
'${index + 1}',
style: const TextStyle(fontSize: 12, color: Colors.white),
),
),
title: Text(
names[id] ?? '#$id',
style: const TextStyle(
fontSize: 12.5,
fontWeight: FontWeight.w700,
color: kWtNavy,
),
),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
tooltip: text('area_remove_route'),
onPressed: state.isSubmitting
? null
: () => cubit.toggleRoute(id),
icon: const Icon(
Icons.close_rounded,
size: 17,
color: kWtGray,
),
),
ReorderableDragStartListener(
index: index,
enabled: !state.isSubmitting,
child: const Padding(
padding: EdgeInsets.symmetric(
horizontal: 12,
vertical: 16,
),
child: Icon(
Icons.drag_handle_rounded,
color: kWtGray,
),
),
),
],
),
),
);
},
),
],
);
}
}