main
hs 2025-07-11 11:01:27 +08:00
parent 2d2480d702
commit 7c529de846
25 changed files with 2342 additions and 294 deletions

BIN
assets/images/close.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 480 B

View File

@ -0,0 +1,50 @@
import 'package:flutter/material.dart';
///
class CustomButton extends StatelessWidget {
final String text; //
final Color backgroundColor; //
final double borderRadius; // 5
final VoidCallback? onPressed; //
final EdgeInsetsGeometry? padding; //
final EdgeInsetsGeometry? margin; //
final double? height; //
final TextStyle? textStyle; //
const CustomButton({
super.key,
required this.text,
required this.backgroundColor,
this.borderRadius = 5.0,
this.onPressed,
this.padding,
this.margin,
this.height,
this.textStyle,
});
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: onPressed,
child: Container(
height: height ?? 50, // 50
padding: padding ?? const EdgeInsets.symmetric(horizontal: 20), //
margin: margin ?? const EdgeInsets.symmetric(horizontal: 8), //
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(borderRadius),
color: backgroundColor,
),
child: Center(
child: Text(
text,
style: textStyle ?? const TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
),
),
),
),
);
}
}

View File

@ -19,6 +19,7 @@ class DashedLineText extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Expanded(

View File

@ -0,0 +1,72 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class DateTimePickerBottomSheet extends StatefulWidget {
final DateTime? initialDateTime;
final ValueChanged<DateTime> onDateTimeSelected;
const DateTimePickerBottomSheet({
super.key,
this.initialDateTime,
required this.onDateTimeSelected,
});
@override
State<DateTimePickerBottomSheet> createState() =>
_DateTimePickerBottomSheetState();
}
class _DateTimePickerBottomSheetState extends State<DateTimePickerBottomSheet> {
late DateTime _selectedDateTime;
@override
void initState() {
super.initState();
_selectedDateTime = widget.initialDateTime ?? DateTime.now();
}
@override
Widget build(BuildContext context) {
return Container(
height: 300,
padding: const EdgeInsets.all(16),
child: Column(
children: [
//
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('取消', style: TextStyle(color: Colors.grey)),
),
TextButton(
onPressed: () {
widget.onDateTimeSelected(_selectedDateTime);
Navigator.pop(context);
},
child: const Text('确定', style: TextStyle(color: Colors.blue)),
),
],
),
const SizedBox(height: 10),
//
Expanded(
child: CupertinoDatePicker(
mode: CupertinoDatePickerMode.dateAndTime,
initialDateTime: _selectedDateTime,
onDateTimeChanged: (DateTime newDateTime) {
setState(() {
_selectedDateTime = newDateTime;
});
},
use24hFormat: true,
minuteInterval: 1,
),
),
],
),
);
}
}

View File

@ -0,0 +1,349 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class EditListItems {
static Widget createColumnTextItem({
required String title,
required String text,
}) {
return Column(
spacing: 5,
children: [
_leftLitle(false, title, Colors.black),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Expanded(child: Text(text, style: TextStyle(fontSize: 13, color: Colors.black54),))
],
)
],
);
}
static Widget createRowSpaceBetweenItem({
required String leftText,
required String rightText,
double verticalPadding = 10,
double horizontalPadding = 0,
Color textColor = Colors.black,
bool isRight = false,
bool isImportent = false,
void Function()? onTap,
}) {
return Padding(
padding: EdgeInsets.symmetric(
vertical: verticalPadding,
horizontal: horizontalPadding,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
_leftLitle(isImportent, leftText, textColor),
if (isRight)
GestureDetector(
onTap: onTap,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
rightText,
style: TextStyle(fontSize: 15, color: Colors.grey),
),
SizedBox(width: 2),
Icon(
Icons.arrow_forward_ios_rounded,
color: Colors.black45,
size: 15,
),
],
),
)
else
Text(rightText, style: TextStyle(fontSize: 15, color: Colors.black87)),
],
),
);
}
///
static Widget bottomTipWidget() {
return Container(
padding: const EdgeInsets.all(15),
color: Colors.white,
child: Text(
' 严禁在本互联网非涉密平台处理、传输国家秘密和工作秘密,请确认扫描、传输的文件资料不涉及国家秘密和工作秘密',
style: TextStyle(fontSize: 14, color: Colors.red),
),
);
}
///
static Widget createBuildMultilineInput({
required String label,
required String hint,
required TextEditingController controller,
bool isImportent = false,
///
bool isVal = false,
///
bool isEdit = true,
}) {
const cellPadding = EdgeInsets.symmetric(vertical: 0);
final labelWidget = _leftLitle(isImportent, label, Colors.black);
if (isVal) {
return Padding(
padding: cellPadding,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
labelWidget,
TextField(
readOnly: !isEdit,
controller: controller,
keyboardType: TextInputType.multiline,
minLines: 1, // 3
maxLines: 1, // 6
style: const TextStyle(fontSize: 15),
decoration: InputDecoration(
hintText: hint,
hintStyle: const TextStyle(color: Colors.red),
border: InputBorder.none,
),
),
],
),
);
} else {
return Padding(
padding: cellPadding,
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
labelWidget,
const SizedBox(width: 8),
Expanded(
child: TextField(
readOnly: !isEdit,
controller: controller,
textAlign: TextAlign.right,
decoration: InputDecoration(
hintText: hint,
hintStyle: const TextStyle(color: Colors.red),
border: InputBorder.none,
isDense: true, //
contentPadding: const EdgeInsets.symmetric(vertical: 8),
),
style: const TextStyle(fontSize: 15),
maxLines: 1,
),
),
],
),
);
}
}
/// 3
static Widget createTextImageItem({
required String text,
required List<String> imageUrls,
double imageHeight = 90,
double verticalPadding = 10,
double horizontalPadding = 0,
// index
void Function(int index)? onImageTapped,
}) {
return Padding(
padding: EdgeInsets.symmetric(
vertical: verticalPadding,
horizontal: horizontalPadding,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_leftLitle(false, text, Colors.black),
const SizedBox(height: 10),
Wrap(
spacing: 8, //
runSpacing: 8, //
children: List.generate(imageUrls.length, (i) {
final url = imageUrls[i];
Widget img;
if (url.startsWith('http')) {
img = Image.network(
url,
height: imageHeight,
width: imageHeight ,
fit: BoxFit.cover,
);
} else {
img = Image.asset(
url,
height: imageHeight,
width: imageHeight * 3 / 2,
fit: BoxFit.cover,
);
}
return GestureDetector(
onTap: () {
if (onImageTapped != null) onImageTapped(i);
},
child: ClipRRect(
borderRadius: BorderRadius.circular(4),
child: img,
),
);
}),
),
],
),
);
}
///
static Widget createRadioRow({
required String label,
required List<String> options,
required int selectedValue,
required ValueChanged<int> onChanged,
bool isImportent = false,
}) {
const cellPadding = EdgeInsets.symmetric(vertical: 10, horizontal: 0);
final labelWidget = _leftLitle(isImportent, label, Colors.black);
return Padding(
padding: cellPadding,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
labelWidget,
const SizedBox(height: 6),
Wrap(
spacing: 5,
runSpacing: 5,
children: List.generate(options.length, (i) {
return _buildRadioOption(
isSelected: selectedValue == i,
label: options[i],
value: i,
onChanged: (v) => onChanged(v!),
);
}),
),
],
),
);
}
///
static Widget createYesNoSection({
required String title,
required String yesLabel,
required String noLabel,
required bool groupValue,
required ValueChanged<bool> onChanged,
double verticalPadding = 15,
double horizontalPadding = 10,
bool isImportant = true,
}) {
return Padding(
padding: EdgeInsets.only(top: 0, right: horizontalPadding, left: horizontalPadding, bottom: verticalPadding),
child: Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(5),
),
child: Row(
children: [
Expanded(
child: Row(
spacing: 3,
children: [
if (isImportant)
Text("*", style: TextStyle(color: Colors.red, fontSize: 20)),
Text(
title,
style: TextStyle(
fontSize: 15,
color: Colors.black,
),
),
],
)
),
Row(
children: [
Row(
children: [
Radio<bool>(
activeColor: Colors.blue,
value: true,
groupValue: groupValue,
onChanged: (val) => onChanged(val!),
),
Text(yesLabel),
],
),
const SizedBox(width: 16),
Row(
children: [
Radio<bool>(
activeColor: Colors.blue,
value: false,
groupValue: groupValue,
onChanged: (val) => onChanged(val!),
),
Text(noLabel),
],
),
],
),
],
),
),
);
}
// Radio
static Widget _buildRadioOption({
required bool isSelected,
required String label,
required int value,
required ValueChanged<int?> onChanged,
}) {
return InkWell(
onTap: () => onChanged(value),
splashColor: Colors.blue.withAlpha(50),
borderRadius: BorderRadius.circular(20),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
isSelected ? Icons.check_circle : Icons.circle_outlined,
size: 22,
color: isSelected ? Colors.blue : Colors.grey,
),
const SizedBox(width: 8),
Text(
label,
style: TextStyle(
fontSize: 15,
color: isSelected ? Colors.blue : Colors.grey[700],
fontWeight: isSelected ? FontWeight.bold : FontWeight.normal,
),
),
const SizedBox(width: 8),
],
),
);
}
static Widget _leftLitle(bool isImportant, String title, Color textColor) {
return Row(
spacing: 3,
children: [
if (isImportant)
Text("*", style: TextStyle(color: Colors.red, fontSize: 20)),
Text(title, style: TextStyle(fontSize: 15, color: textColor)),
],
);
}
}

View File

@ -0,0 +1,120 @@
import 'package:flutter/material.dart';
import 'package:dotted_border/dotted_border.dart';
import 'package:qhdkfq_regulatory_flutter/Custom/edit_list_items.dart';
import 'package:qhdkfq_regulatory_flutter/home/department_picker.dart';
import '../mock/mock_data.dart';
class HelpDeptPersonSection extends StatefulWidget {
const HelpDeptPersonSection({Key? key}) : super(key: key);
@override
HelpDeptPersonSectionState createState() => HelpDeptPersonSectionState();
}
class HelpDeptPersonSectionState extends State<HelpDeptPersonSection> {
final List<Map<String, String>> _entries = [
{'dept': '请选择', 'person': '请选择'},
];
// id
String? _selectedCategoryId;
///
void addEntry() {
setState(() => _entries.add({'dept': '', 'person': ''}));
}
///
List<Map<String, String>> get entries => List.unmodifiable(_entries);
void _removeEntry(int i) {
setState(() => _entries.removeAt(i));
}
@override
Widget build(BuildContext context) {
return Column(
children: [
for (var i = 0; i < _entries.length; i++) ...[
Stack(
clipBehavior: Clip.none, //
children: [
DottedBorder(
color: Colors.grey,
strokeWidth: 1,
dashPattern: [4, 2],
borderType: BorderType.RRect,
radius: const Radius.circular(8),
child: Padding(
padding: const EdgeInsets.all(12),
child: Column(
children: [
EditListItems.createRowSpaceBetweenItem(
leftText: '帮扶部门',
rightText: _entries[i]['dept']!,
isImportent: true,
isRight: true,
onTap: () {
// TODO:
showModalBottomSheet(
context: context,
isScrollControlled: true,
barrierColor: Colors.black54,
backgroundColor: Colors.transparent,
builder:
(ctx) => DepartmentPicker(
data: mockCategories,
onSelected: (selectedId) {
setState(() {
_selectedCategoryId = selectedId;
});
},
),
);
print(mockCategories.map((e) => e.title));
},
),
const Divider(height: 8),
EditListItems.createRowSpaceBetweenItem(
leftText: '帮扶人员',
rightText: _entries[i]['person']!,
isImportent: true,
isRight: true,
onTap: () {
// TODO:
},
),
],
),
),
),
if (_entries.length > 1)
//
Positioned(
top: -2,
right: -2,
child: GestureDetector(
onTap: () => _removeEntry(i),
child: Container(
width: 24,
height: 24,
decoration: const BoxDecoration(
color: Colors.red,
shape: BoxShape.circle,
),
child: const Icon(
Icons.close,
size: 16,
color: Colors.white,
),
),
),
),
],
),
const SizedBox(height: 12),
],
],
);
}
}

View File

@ -0,0 +1,298 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:wechat_assets_picker/wechat_assets_picker.dart';
import 'package:photo_manager/photo_manager.dart';
///
enum MediaType { image, video }
///
class MediaPickerRow extends StatefulWidget {
final int maxCount;
final MediaType mediaType;
final ValueChanged<List<File>> onChanged;
const MediaPickerRow({
Key? key,
this.maxCount = 4,
this.mediaType = MediaType.image,
required this.onChanged,
}) : super(key: key);
@override
_MediaPickerRowState createState() => _MediaPickerRowState();
}
class _MediaPickerRowState extends State<MediaPickerRow> {
final ImagePicker _picker = ImagePicker();
final List<File> _files = [];
Future<void> _showPickerOptions() async {
showModalBottomSheet(
context: context,
builder:
(_) => SafeArea(
child: Wrap(
children: [
ListTile(
leading: Icon(
widget.mediaType == MediaType.image
? Icons.camera_alt
: Icons.videocam,
),
title: Text(
widget.mediaType == MediaType.image ? '拍照' : '拍摄视频',
),
onTap: () {
Navigator.of(context).pop();
_pickCamera();
},
),
ListTile(
leading: Icon(
widget.mediaType == MediaType.image
? Icons.photo_library
: Icons.video_library,
),
title: Text(
widget.mediaType == MediaType.image ? '从相册选择' : '从相册选择视频',
),
onTap: () {
Navigator.of(context).pop();
_pickGallery();
},
),
ListTile(
leading: const Icon(Icons.close),
title: const Text('取消'),
onTap: () => Navigator.of(context).pop(),
),
],
),
),
);
}
Future<void> _pickCamera() async {
if (_files.length >= widget.maxCount) return;
try {
XFile? picked =
widget.mediaType == MediaType.image
? await _picker.pickImage(source: ImageSource.camera)
: await _picker.pickVideo(source: ImageSource.camera);
if (picked != null) {
setState(() => _files.add(File(picked.path)));
widget.onChanged(_files);
}
} catch (e) {
debugPrint('拍摄失败: $e');
}
}
Future<void> _pickGallery() async {
if (_files.length >= widget.maxCount) return;
final permission = await PhotoManager.requestPermissionExtend();
if (permission != PermissionState.authorized &&
permission != PermissionState.limited) {
ScaffoldMessenger.of(
context,
).showSnackBar(const SnackBar(content: Text('请到设置中开启相册访问权限')));
return;
}
try {
final remaining = widget.maxCount - _files.length;
final List<AssetEntity>? assets = await AssetPicker.pickAssets(
context,
pickerConfig: AssetPickerConfig(
requestType:
widget.mediaType == MediaType.image
? RequestType.image
: RequestType.video,
maxAssets: remaining,
gridCount: 4,
),
);
if (assets != null) {
for (final asset in assets) {
if (_files.length >= widget.maxCount) break;
final file = await asset.file;
if (file != null) _files.add(file);
}
setState(() {});
widget.onChanged(_files);
}
} catch (e) {
debugPrint('相册选择失败: $e');
}
}
void _removeFile(int index) {
setState(() {
_files.removeAt(index);
});
widget.onChanged(_files);
}
@override
Widget build(BuildContext context) {
//
final children = <Widget>[
for (int i = 0; i < _files.length; i++) _buildMediaItem(i),
if (_files.length < widget.maxCount) _buildAddButton(),
];
return Padding(
padding: const EdgeInsets.symmetric(vertical: 10),
child: Wrap(
spacing: 8, //
runSpacing: 8, //
children: children,
),
);
}
Widget _buildMediaItem(int index) {
return Stack(
clipBehavior: Clip.none, //
children: [
ClipRRect(
borderRadius: BorderRadius.circular(5),
child: widget.mediaType == MediaType.image
? Image.file(
_files[index],
width: 80,
height: 80,
fit: BoxFit.cover,
)
: Container(
width: 80,
height: 80,
color: Colors.black12,
child: const Center(
child: Icon(Icons.videocam, color: Colors.white70),
),
),
),
Positioned(
top: 0, //
right: 0,
child: GestureDetector(
onTap: () => _removeFile(index),
child: Image.asset(
"assets/images/close.png",
width: 24,
height: 24,
),
),
),
],
);
}
Widget _buildAddButton() {
return GestureDetector(
onTap: _showPickerOptions,
child: Container(
width: 80,
height: 80,
decoration: BoxDecoration(
border: Border.all(color: Colors.black12),
borderRadius: BorderRadius.circular(5),
),
child: Center(
child: Icon(
widget.mediaType == MediaType.image
? Icons.camera_alt
: Icons.videocam,
color: Colors.black26,
),
),
),
);
}
}
///
class RepairedPhotoSection extends StatelessWidget {
final int maxCount;
final MediaType mediaType;
final String title;
final ValueChanged<List<File>> onChanged;
final VoidCallback onAiIdentify;
final bool isShowAI;
final double horizontalPadding;
const RepairedPhotoSection({
Key? key,
this.maxCount = 4,
this.mediaType = MediaType.image,
required this.title,
this.isShowAI = false,
required this.onChanged,
required this.onAiIdentify,
this.horizontalPadding = 10,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
color: Colors.white,
padding: const EdgeInsets.only(left: 5, right: 10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
//
Padding(
padding: EdgeInsets.symmetric(horizontal: horizontalPadding),
child: Text(
title,
style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
),
),
const SizedBox(height: 8),
//
MediaPickerRow(
maxCount: maxCount,
mediaType: mediaType,
onChanged: onChanged,
),
if (isShowAI)
Padding(
padding: const EdgeInsets.only(top: 20),
child: Stack(
children: [
GestureDetector(
onTap: onAiIdentify,
child: Container(
width: double.infinity,
// 便
height: 36,
decoration: BoxDecoration(
color: const Color(0xFFDFEAFF),
borderRadius: BorderRadius.circular(18),
),
alignment: Alignment.centerLeft,
padding: const EdgeInsets.symmetric(horizontal: 15),
child: const Text('AI隐患识别与处理'),
),
),
// Positioned
Positioned(
top: 0,
right: 0,
child: Image.asset(
'assets/images/ai_img.png',
width: 20,
height: 20,
),
),
],
),
),
],
),
);
}
}

View File

@ -0,0 +1,30 @@
import 'package:flutter/material.dart';
import 'package:photo_view/photo_view.dart';
import '../tools/my_appbar.dart';
//
class SingleImageViewer extends StatelessWidget {
final String imageUrl;
const SingleImageViewer({Key? key, required this.imageUrl}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.black,
appBar: MyAppbar(
backgroundColor: Colors.transparent, title: '',
),
body: Center(
child: PhotoView(
imageProvider: NetworkImage(imageUrl),
backgroundDecoration: BoxDecoration(color: Colors.black),
minScale: PhotoViewComputedScale.contained,
maxScale: PhotoViewComputedScale.covered * 2,
onTapUp: (context, details, controllerValue) {
Navigator.of(context).pop();
},
),
),
);
}
}

View File

@ -0,0 +1,10 @@
import 'package:flutter/material.dart';
class ActionTaskPage extends StatelessWidget {
const ActionTaskPage({super.key});
@override
Widget build(BuildContext context) {
return const Placeholder();
}
}

View File

@ -1,21 +1,28 @@
import 'package:flutter/material.dart';
import 'package:qhdkfq_regulatory_flutter/home/home_page.dart';
class AdaptiveListItem extends StatelessWidget {
final String title;
final List<Map<String, String>> items;
final List<Widget>? actions;
final HomeCategroyType type;
final int selectTabIndex;
const AdaptiveListItem({
super.key,
required this.title,
required this.items,
required this.actions,
required this.type,
required this.selectTabIndex,
});
@override
Widget build(BuildContext context) {
// 2
final List<List<Map<String, String>>> rows = [];
final bool isAllowSelect = (type == HomeCategroyType.enterpriseInfo && selectTabIndex == 2) ||
(type == HomeCategroyType.keySafety && selectTabIndex == 1);
for (var i = 0; i < items.length; i += 2) {
rows.add(items.sublist(i, i + 2 > items.length ? items.length : i + 2));
}
@ -24,10 +31,17 @@ class AdaptiveListItem extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
//
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
title,
style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 16),
),
if (isAllowSelect)
Icon(Icons.arrow_forward_ios_rounded, size: 15, color: Colors.black26,)
],
),
const SizedBox(height: 8),
//

View File

@ -1,33 +0,0 @@
import 'package:flutter/material.dart';
import 'package:qhdkfq_regulatory_flutter/Custom/dashed_line_text.dart';
import 'package:qhdkfq_regulatory_flutter/tools/h_colors.dart';
import 'package:qhdkfq_regulatory_flutter/tools/my_appbar.dart';
class DangerEditPage extends StatefulWidget {
const DangerEditPage({super.key});
@override
State<DangerEditPage> createState() => _DangerEditPageState();
}
class _DangerEditPageState extends State<DangerEditPage> {
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: MyAppbar(title: "编辑"),
body: SafeArea(child: ListView(
padding: EdgeInsets.symmetric(horizontal: 15),
children: [
SizedBox(height: 20,),
DashedLineText(
text: "基本信息",
lineColor: h_mainBlueColor(),
textStyle: TextStyle(fontSize: 16, color: h_mainBlueColor()),
),
],
)),
);
}
}

View File

@ -1,19 +1,8 @@
import 'package:flutter/material.dart';
class Category {
final String id;
final String title;
final List<Category> children;
Category({
required this.id,
required this.title,
this.children = const [],
});
}
import '../models/department_category.dart';
class DepartmentPicker extends StatefulWidget {
final List<Category> data;
final List<DepartmentCategory> data;
final String? initialSelectedId;
final Set<String>? initialExpandedSet;
final ValueChanged<String?> onSelected;
@ -41,7 +30,7 @@ class _DepartmentPickerState extends State<DepartmentPicker> {
expandedSet = Set<String>.from(widget.initialExpandedSet ?? {});
}
Widget _buildRow(Category cat, int indent) {
Widget _buildRow(DepartmentCategory cat, int indent) {
final bool hasChildren = cat.children.isNotEmpty;
final bool isExpanded = expandedSet.contains(cat.id);
final bool isSelected = cat.id == selectedId;
@ -52,11 +41,9 @@ class _DepartmentPickerState extends State<DepartmentPicker> {
onTap: () {
setState(() {
if (hasChildren) {
if (isExpanded) {
expandedSet.remove(cat.id);
} else {
expandedSet.add(cat.id);
}
isExpanded
? expandedSet.remove(cat.id)
: expandedSet.add(cat.id);
}
selectedId = cat.id;
});
@ -66,28 +53,26 @@ class _DepartmentPickerState extends State<DepartmentPicker> {
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
//
SizedBox(width: 16.0 * indent),
// /
SizedBox(
width: 24,
child: hasChildren
? Icon(
isExpanded ? Icons.arrow_drop_down : Icons.arrow_right_outlined,
isExpanded
? Icons.arrow_drop_down
: Icons.arrow_right_outlined,
size: 30,
color: Colors.grey[600],
)
: const SizedBox.shrink(),
),
const SizedBox(width: 8),
//
Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 12),
child: Text(cat.title),
),
),
//
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Icon(
@ -101,13 +86,16 @@ class _DepartmentPickerState extends State<DepartmentPicker> {
),
),
),
if (hasChildren && isExpanded)
...cat.children.map((c) => _buildRow(c, indent + 1)),
// const Divider(height: 1),
...cat.children
.map((c) => _buildRow(c, indent + 1)),
],
);
}
@override
Widget build(BuildContext context) {
return Container(

View File

@ -0,0 +1,248 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:qhdkfq_regulatory_flutter/Custom/custom_button.dart';
import 'package:qhdkfq_regulatory_flutter/Custom/dateTime_picker_bottom_sheet.dart';
import 'package:qhdkfq_regulatory_flutter/Custom/edit_list_items.dart';
import 'package:qhdkfq_regulatory_flutter/Custom/photo_picker_row.dart';
import 'package:qhdkfq_regulatory_flutter/tools/my_appbar.dart';
import '../../Custom/dashed_line_text.dart';
import '../../Custom/single_image_viewer.dart';
import '../../tools/h_colors.dart';
import '../../tools/tools.dart';
enum DangerDetailType { detail, repeat }
class DangerDetailPage extends StatefulWidget {
const DangerDetailPage(this.type, {super.key});
final DangerDetailType type;
@override
State<DangerDetailPage> createState() => _DangerDetailPageState();
}
class _DangerDetailPageState extends State<DangerDetailPage> {
bool _isQualified = true;
String _repeatTime = "请选择";
DateTime _selectedDateTime = DateTime.now();
final _reasonEditController = TextEditingController();
void _showDateTimePicker(BuildContext context) {
showModalBottomSheet(
context: context,
builder: (BuildContext context) {
return DateTimePickerBottomSheet(
initialDateTime: _selectedDateTime, //
onDateTimeSelected: (selectedDateTime) {
//
print("选择的时间: $selectedDateTime");
//
},
);
},
);
}
//
List<Widget> _buildSectionItems(List<Widget> items) {
return List.generate(items.length * 2 - 1, (index) {
if (index.isEven) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 10),
child: items[index ~/ 2],
);
}
return const Divider(height: 15);
});
}
@override
Widget build(BuildContext context) {
//
final List<Widget> dangerMessages = _buildSectionItems([
EditListItems.createColumnTextItem(
title: "帮扶内容",
text: "测试数据测试数据测试数据测试数据测试数据测试数据测试数据测试数据测试数据测试数据测试数据测试数据测试数据",
),
EditListItems.createColumnTextItem(title: "隐患描述", text: "测试数据"),
EditListItems.createColumnTextItem(title: "隐患编码", text: "测试数据"),
EditListItems.createRowSpaceBetweenItem(
leftText: "隐患部位",
rightText: "测试数据",
verticalPadding: 0,
),
EditListItems.createRowSpaceBetweenItem(
leftText: "隐患级别",
rightText: "测试数据",
verticalPadding: 0,
),
EditListItems.createRowSpaceBetweenItem(
leftText: "帮扶人员",
rightText: "测试数据",
verticalPadding: 0,
),
EditListItems.createRowSpaceBetweenItem(
leftText: "发现时间",
rightText: "测试数据",
verticalPadding: 0,
),
EditListItems.createRowSpaceBetweenItem(
leftText: "整改截止时间",
rightText: "测试数据",
verticalPadding: 0,
),
EditListItems.createColumnTextItem(title: "整改意见", text: "测试数据"),
EditListItems.createRowSpaceBetweenItem(
leftText: "隐患状态",
rightText: "测试数据",
verticalPadding: 0,
),
EditListItems.createTextImageItem(
text: "隐患照片",
imageUrls: ["https://picsum.photos/id/237/200/300"],
onImageTapped: (index) {
present(
SingleImageViewer(imageUrl: "https://picsum.photos/id/237/200/300"),
context,
);
},
),
]);
//
final List<Widget> correctionMessages = _buildSectionItems([
EditListItems.createRowSpaceBetweenItem(
leftText: "整改人",
rightText: "测试数据",
verticalPadding: 0,
),
EditListItems.createRowSpaceBetweenItem(
leftText: "整改时间",
rightText: "测试数据",
verticalPadding: 0,
),
EditListItems.createColumnTextItem(title: "整改描述", text: "测试数据"),
EditListItems.createTextImageItem(
text: "整改图片",
imageUrls: ["https://picsum.photos/id/237/200/300"],
onImageTapped: (index) {
present(
SingleImageViewer(imageUrl: "https://picsum.photos/id/237/200/300"),
context,
);
},
),
EditListItems.createTextImageItem(
text: "本次整改报告照片",
imageUrls: ["https://picsum.photos/id/237/200/300"],
onImageTapped: (index) {
present(
SingleImageViewer(imageUrl: "https://picsum.photos/id/237/200/300"),
context,
);
},
),
]);
//
final List<Widget> reviewMessages = _buildSectionItems([
EditListItems.createYesNoSection(
title: "是否合格",
yesLabel: "",
noLabel: "",
horizontalPadding: 0,
verticalPadding: 0,
groupValue: _isQualified,
isImportant: true,
onChanged: (val) {
setState(() => _isQualified = val);
},
),
if (!_isQualified)
EditListItems.createBuildMultilineInput(label: "原因", hint: "请输入原因",isImportent: true,isVal: true, controller: _reasonEditController),
EditListItems.createRowSpaceBetweenItem(
leftText: "复查时间",
rightText: _repeatTime,
isImportent: true,
isRight: true,
verticalPadding: 0,
onTap: () {
_showDateTimePicker(context);
},
),
]);
return Scaffold(
backgroundColor: Colors.white,
appBar: MyAppbar(
title: widget.type == DangerDetailType.detail ? "隐患详情" : "复查详情",
),
body: SafeArea(
child: Column(
children: [
Expanded(
child: SingleChildScrollView(
//
padding: const EdgeInsets.symmetric(horizontal: 10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const SizedBox(height: 10),
_buildSectionHeader("隐患信息"),
...dangerMessages,
const SizedBox(height: 20),
if (widget.type == DangerDetailType.repeat) ...[
_buildSectionHeader("整改信息"),
...correctionMessages,
const SizedBox(height: 20),
_buildSectionHeader("隐患复查"),
...reviewMessages,
Divider(),
EditListItems.createRowSpaceBetweenItem(
leftText: "现场复查照片",
isImportent: true,
rightText: "",
isRight: false,
horizontalPadding: 10,
),
Padding(
padding: EdgeInsets.symmetric(horizontal: 10),
child: MediaPickerRow(
maxCount: 16,
onChanged: (List<File> images) {},
),
),
const SizedBox(height: 20),
CustomButton(
text: "保存",
backgroundColor: Colors.blue,
onPressed: () {},
),
const SizedBox(height: 20),
],
],
),
),
),
if (widget.type == DangerDetailType.repeat)
EditListItems.bottomTipWidget()
],
),
),
);
}
//
Widget _buildSectionHeader(String title) {
return Padding(
padding: const EdgeInsets.only(bottom: 10),
child: DashedLineText(
text: title,
lineColor: h_mainBlueColor(),
textStyle: TextStyle(fontSize: 16, color: h_mainBlueColor()),
),
);
}
}

View File

@ -0,0 +1,296 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:qhdkfq_regulatory_flutter/Custom/custom_button.dart';
import 'package:qhdkfq_regulatory_flutter/Custom/dashed_line_text.dart';
import 'package:qhdkfq_regulatory_flutter/Custom/edit_list_items.dart';
import 'package:qhdkfq_regulatory_flutter/Custom/help_department_person_item.dart';
import 'package:qhdkfq_regulatory_flutter/Custom/read_only_table.dart';
import 'package:qhdkfq_regulatory_flutter/tools/h_colors.dart';
import 'package:qhdkfq_regulatory_flutter/tools/my_appbar.dart';
import '../../Custom/photo_picker_row.dart';
enum HelpEditType {
edit,
help
}
class DangerEditPage extends StatefulWidget {
const DangerEditPage(this.type, {Key? key}) : super(key: key);
final HelpEditType type;
@override
State<DangerEditPage> createState() => _DangerEditPageState();
}
class _DangerEditPageState extends State<DangerEditPage> {
//
final _companyController = TextEditingController(text: '公司');
final _addressController = TextEditingController();
final _nameController = TextEditingController(text: '韩双');
final _phoneController = TextEditingController();
final _codeController = TextEditingController();
final _placeController = TextEditingController();
//
final List<String> _radioTitles = ['日常检查', '专项检查', '总和检查', '执法检查', '举报检查'];
int _selectedType = 0;
// GlobalKey State
final _sectionKey = GlobalKey<HelpDeptPersonSectionState>();
///
final String _helpTimeStr = "请选择";
@override
void dispose() {
_companyController.dispose();
_addressController.dispose();
_nameController.dispose();
_phoneController.dispose();
_codeController.dispose();
_placeController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
// 1.
final basicFields = <Widget>[
EditListItems.createRowSpaceBetweenItem(
leftText: '公司名称',
rightText: _companyController.text,
isImportent: true,
),
Divider(height: .5),
EditListItems.createBuildMultilineInput(
label: '地址',
hint: '',
controller: _addressController,
isImportent: true,
isVal: true,
),
Divider(height: .5),
EditListItems.createBuildMultilineInput(
label: '姓名',
hint: '',
controller: _nameController,
isImportent: true,
),
Divider(height: .5),
EditListItems.createBuildMultilineInput(
label: '联系电话',
hint: '',
controller: _phoneController,
isImportent: true,
),
Divider(height: .5),
EditListItems.createBuildMultilineInput(
label: '编号',
hint: '',
controller: _codeController,
isImportent: true,
),
Divider(height: .5),
EditListItems.createBuildMultilineInput(
label: '场所',
hint: '',
controller: _placeController,
isImportent: true,
isVal: true,
),
Divider(height: .5),
EditListItems.createRadioRow(
label: '检查类型',
options: _radioTitles,
selectedValue: _selectedType,
onChanged: (v) => setState(() => _selectedType = v),
isImportent: true,
),
Divider(height: .5),
EditListItems.createRowSpaceBetweenItem(
leftText: "帮扶时间",
isImportent: true,
rightText: _helpTimeStr,
isRight: true,
onTap: () {},
),
Divider(height: .5),
EditListItems.createYesNoSection(
title: "邀请专家",
yesLabel: "",
noLabel: "",
horizontalPadding: 0,
verticalPadding: 0,
groupValue: false,
onChanged: (val) {},
),
Divider(height: .5),
EditListItems.createRowSpaceBetweenItem(
leftText: "现场照片",
isImportent: true,
rightText: "",
isRight: false,
),
MediaPickerRow(
maxCount: 16,
onChanged: (List<File> images) {
// images
},
),
];
//
final helpHeader = Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
DashedLineText(
text: '帮扶内容',
lineColor: h_mainBlueColor(),
textStyle: TextStyle(fontSize: 16, color: h_mainBlueColor()),
),
SizedBox(height: 20),
],
);
//
final buttons = Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
CustomButton(
text: '选择标准',
height: 35,
backgroundColor: Colors.blue,
onPressed: () {},
),
SizedBox(width: 8),
CustomButton(
text: '手动录入',
height: 35,
backgroundColor: Colors.blue,
onPressed: () {},
),
],
);
final table = ReadOnlyTable(
headers: ['序号', '内容来源', '隐患描述', '操作'],
data: [
['1', '政府部门', '示例内容', '合格'],
['2', '社会组织', '示例', '不合格'],
],
onTapAction: (row) => print('点击表格第$row'),
);
//
final helpPersonHeader = Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
DashedLineText(
text: '帮扶人员',
lineColor: h_mainBlueColor(),
textStyle: TextStyle(fontSize: 16, color: h_mainBlueColor()),
),
SizedBox(height: 20),
],
);
final helpPerson = Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
CustomButton(
text: '添加',
height: 35,
backgroundColor: Colors.blue,
onPressed: () {
_sectionKey.currentState?.addEntry();
},
),
],
);
final dartPerson = HelpDeptPersonSection(key: _sectionKey);
final danger = Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
DashedLineText(
text: '隐患',
lineColor: h_mainBlueColor(),
textStyle: TextStyle(fontSize: 16, color: h_mainBlueColor()),
),
SizedBox(height: 20),
],
);
final dangerTable = ReadOnlyTable(
headers: ['序号', '所属帮扶', '隐患描述', '操作'],
data: [],
);
final bottomButtons = Row(
children: [
Expanded(
child: CustomButton(
text: "保存",
backgroundColor: Colors.blue,
height: 40,
),
),
const SizedBox(width: 12),
Expanded(
child: CustomButton(
text: "暂存",
backgroundColor: Colors.green,
height: 40,
),
),
],
);
//
final children = <Widget>[
//
DashedLineText(
text: '基本信息',
lineColor: h_mainBlueColor(),
textStyle: TextStyle(fontSize: 16, color: h_mainBlueColor()),
),
SizedBox(height: 20),
...basicFields,
SizedBox(height: 20),
helpHeader,
buttons,
SizedBox(height: 10),
table,
SizedBox(height: 20),
helpPersonHeader,
helpPerson,
SizedBox(height: 20),
dartPerson,
SizedBox(height: 20),
danger,
SizedBox(height: 20),
dangerTable,
SizedBox(height: 20),
bottomButtons,
];
return Scaffold(
backgroundColor: Colors.white,
appBar: MyAppbar(title: widget.type == HelpEditType.help ? '监管帮扶' : '编辑'),
body: SafeArea(
child: Column(
children: [
Expanded(
child: ListView(
padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 20),
children: children,
),
),
EditListItems.bottomTipWidget(),
],
),
),
);
}
}

View File

@ -1,9 +1,10 @@
import 'package:flutter/material.dart';
import 'package:qhdkfq_regulatory_flutter/Custom/dashed_line_text.dart';
import 'package:qhdkfq_regulatory_flutter/Custom/edit_list_items.dart';
import 'package:qhdkfq_regulatory_flutter/tools/h_colors.dart';
import 'package:qhdkfq_regulatory_flutter/tools/my_appbar.dart';
import '../Custom/read_only_table.dart';
import '../../Custom/read_only_table.dart';
class HelpDetailPage extends StatefulWidget {
const HelpDetailPage({super.key});
@ -54,7 +55,7 @@ class _HelpDetailPageState extends State<HelpDetailPage> {
itemCount: _infoList.length,
itemBuilder: (context, index) {
final InfoModel item = _infoList[index];
return _createRowSpaceBetweenItem(
return EditListItems.createRowSpaceBetweenItem(
horizontalPadding: 10,
verticalPadding: 15,
leftText: item.title,
@ -100,52 +101,7 @@ class _HelpDetailPageState extends State<HelpDetailPage> {
);
}
Widget _createRowSpaceBetweenItem({
required String leftText,
required String rightText,
double verticalPadding = 10,
double horizontalPadding = 0,
Color textColor = Colors.black,
bool isRight = false,
}) {
return Padding(
padding: EdgeInsets.symmetric(
vertical: verticalPadding,
horizontal: horizontalPadding,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
leftText,
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.bold,
color: textColor,
),
),
if (isRight)
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
rightText,
style: TextStyle(fontSize: 15, color: Colors.grey),
),
SizedBox(width: 2),
Icon(
Icons.arrow_forward_ios_rounded,
color: Colors.black45,
size: 15,
),
],
)
else
Text(rightText, style: TextStyle(fontSize: 15, color: Colors.grey)),
],
),
);
}
}

View File

@ -0,0 +1,216 @@
import 'package:flutter/material.dart';
import 'package:qhdkfq_regulatory_flutter/home/help/danger_edit_page.dart';
import 'package:qhdkfq_regulatory_flutter/home/help/help_detail_page.dart';
import 'package:qhdkfq_regulatory_flutter/home/home_page.dart';
import 'package:qhdkfq_regulatory_flutter/tools/my_appbar.dart';
import '../../tools/custom_button.dart';
import '../../tools/tools.dart';
import '../adaptive_list_item.dart';
class HelpRecordPage extends StatefulWidget {
const HelpRecordPage({super.key});
@override
State<HelpRecordPage> createState() => _HelpRecordPageState();
}
class _HelpRecordPageState extends State<HelpRecordPage> {
int _selectRadioIndex = 0;
// State build
final List<String> _radioTitles = [
"日常检查",
"专项检查",
"总和检查",
"执法检查",
"举报检查",
];
///
final List<Map<String, dynamic>> _enterpriseData = List.generate(15, (index) {
return {
'id': index,
'name': '${['化工', '制造', '能源', '食品', '建筑'][index % 5]}企业${index + 1}',
'creditCode': '9137${10000 + index}',
'industry': ['化工', '制造', '能源', '食品', '建筑'][index % 5],
'scale': ['大型', '中型', '小型'][index % 3],
'safetyLevel': ['A', 'B', 'C'][index % 3],
'lastCheck': '2025-0${6 + index % 3}-${10 + index % 20}',
'riskCount': index % 4,
'hiddenDangerCount': index % 5,
'isDivided': index % 3 == 0,
};
});
Widget _buildDynamicRadioRow({
required List<String> options, //
required int? selectedValue, //
required ValueChanged<int?> onChanged, //
}) {
return Wrap(
spacing: 5, //
runSpacing: 5, //
children: List.generate(options.length, (i) {
return _buildRadioOption(
isSelected: selectedValue == i,
label: options[i],
value: i,
onChanged: onChanged,
);
}),
);
}
Widget _listViewWidget() {
return ListView.builder(
itemCount: 3,
itemBuilder: (context, index) {
final enterprise = _enterpriseData[index];
return Card(
color: Colors.white,
margin: const EdgeInsets.symmetric(horizontal: 0, vertical: 6),
elevation: 0.5,
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.zero),
child: Padding(
padding: const EdgeInsets.all(15),
child: AdaptiveListItem(
title: "测试",
items: _getEnterpriseProperties(enterprise),
actions: _listItemButtons(),
type: HomeCategroyType.supervisionSupport,
selectTabIndex: 0,
),
),
);
},
);
}
List<Widget> _listItemButtons() {
return [
CustomButton(
height: 40,
text: "编辑",
backgroundColor: Colors.blue,
padding: EdgeInsets.symmetric(horizontal: 30),
onPressed: () {
pushPage(DangerEditPage(HelpEditType.edit), context);
},
),
CustomButton(
height: 40,
text: "查看",
backgroundColor: Colors.blue,
padding: EdgeInsets.symmetric(horizontal: 30),
onPressed: () {
pushPage(HelpDetailPage(), context);
},
),
CustomButton(
height: 40,
text: "删除",
backgroundColor: Colors.red,
padding: EdgeInsets.symmetric(horizontal: 30),
onPressed: () {
// TODO ---
},
),
];
}
/// Radio
Widget _buildRadioOption({
required bool isSelected,
required String label,
required int value,
required ValueChanged<int?> onChanged,
}) {
return InkWell(
onTap: () => onChanged(value),
splashColor: Colors.blue,
borderRadius: BorderRadius.circular(20),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
// -
SizedBox(
child:
isSelected
? const Icon(
Icons.check_circle,
size: 22,
color: Colors.blue,
)
: const Icon(
Icons.circle_outlined,
size: 22,
color: Colors.grey,
),
),
SizedBox(width: 10),
Text(
label,
style: TextStyle(
fontSize: 15,
color: isSelected ? Colors.blue : Colors.grey[700],
fontWeight: isSelected ? FontWeight.bold : FontWeight.normal,
),
),
SizedBox(width: 10),
],
),
);
}
List<Map<String, String>> _getEnterpriseProperties(
Map<String, dynamic> enterprise,
) {
return [
{
"title": "检查类型",
"value": ["常规", "突击", "专项"][enterprise['id'] % 3],
},
{
"title": "检查结果",
"value": ["合格", "需整改", "不合格"][enterprise['id'] % 3],
},
{"title": "检查日期", "value": enterprise['lastCheck']},
{
"title": "检查人",
"value": "${['', '主任', '专员'][enterprise['id'] % 3]}",
},
];
}
@override
Widget build(BuildContext context) {
late List<String> radioTitles = ["日常检查","专项检查","总和检查","执法检查","举报检查"];
/// radio
late int selectRadioIndex = 0;
return Scaffold(
backgroundColor: Colors.white,
appBar: MyAppbar(title: "帮扶记录"),
body: SafeArea(
child: Column(
children: [
Container(
width: double.maxFinite,
padding: EdgeInsets.all(15),
color: Colors.white,
child: _buildDynamicRadioRow(
options: _radioTitles,
selectedValue: _selectRadioIndex,
onChanged: (val) {
setState(() {
_selectRadioIndex = val!;
});
},
),
),
SizedBox(height: 10,),
Expanded(child: _listViewWidget()),
],
),
),
);
}
}

View File

@ -0,0 +1,25 @@
import 'package:flutter/material.dart';
import 'package:qhdkfq_regulatory_flutter/Custom/custom_button.dart';
import 'package:qhdkfq_regulatory_flutter/tools/my_appbar.dart';
class HelpStandardPage extends StatefulWidget {
const HelpStandardPage({super.key});
@override
State<HelpStandardPage> createState() => _HelpStandardPageState();
}
class _HelpStandardPageState extends State<HelpStandardPage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: MyAppbar(title: "帮扶标准"),
body: Column(
children: [
Expanded(child: ListView()),
CustomButton(text: "保存", backgroundColor: Colors.blue)
],
),
);
}
}

View File

@ -1,111 +0,0 @@
import 'package:flutter/material.dart';
import 'package:qhdkfq_regulatory_flutter/tools/my_appbar.dart';
class HelpRecordPage extends StatefulWidget {
const HelpRecordPage({super.key});
@override
State<HelpRecordPage> createState() => _HelpRecordPageState();
}
class _HelpRecordPageState extends State<HelpRecordPage> {
Widget _buildDynamicRadioRow({
required List<String> options, //
required int? selectedValue, //
required ValueChanged<int?> onChanged, //
}) {
return Wrap(
spacing: 5, //
runSpacing: 5, //
children: List.generate(options.length, (i) {
return _buildRadioOption(
isSelected: selectedValue == i,
label: options[i],
value: i,
onChanged: onChanged,
);
}),
);
}
/// Radio
Widget _buildRadioOption({
required bool isSelected,
required String label,
required int value,
required ValueChanged<int?> onChanged,
}) {
return InkWell(
onTap: () => onChanged(value),
splashColor: Colors.blue,
borderRadius: BorderRadius.circular(20),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
// -
SizedBox(
child:
isSelected
? const Icon(
Icons.check_circle,
size: 22,
color: Colors.blue,
)
: const Icon(
Icons.circle_outlined,
size: 22,
color: Colors.grey,
),
),
SizedBox(width: 10),
Text(
label,
style: TextStyle(
fontSize: 15,
color: isSelected ? Colors.blue : Colors.grey[700],
fontWeight: isSelected ? FontWeight.bold : FontWeight.normal,
),
),
SizedBox(width: 10),
],
),
);
}
@override
Widget build(BuildContext context) {
late List<String> radioTitles = ["日常检查","专项检查","总和检查","执法检查","举报检查"];
/// radio
late int? selectRadioIndex = 0;
return Scaffold(
appBar: MyAppbar(title: "帮扶记录"),
body: SafeArea(
child: Column(
children: [
Container(
width: double.maxFinite,
padding: EdgeInsets.all(15),
color: Colors.white,
child: _buildDynamicRadioRow(
options: radioTitles,
selectedValue: selectRadioIndex,
onChanged: (val) {
setState(() {
selectRadioIndex = val;
});
},
),
),
SizedBox(height: 10,),
ListView(
children: [
],
)
],
),
),
);
}
}

View File

@ -1,13 +1,17 @@
import 'package:flutter/material.dart';
import 'package:qhdkfq_regulatory_flutter/home/danger_edit_page.dart';
import 'package:qhdkfq_regulatory_flutter/home/help_detail_page.dart';
import 'package:qhdkfq_regulatory_flutter/home/help_record_page.dart';
import 'package:qhdkfq_regulatory_flutter/home/action/action_task_page.dart';
import 'package:qhdkfq_regulatory_flutter/home/help/danger_detail_page.dart';
import 'package:qhdkfq_regulatory_flutter/home/help/danger_edit_page.dart';
import 'package:qhdkfq_regulatory_flutter/home/help/help_detail_page.dart';
import 'package:qhdkfq_regulatory_flutter/home/help/help_record_page.dart';
import 'package:qhdkfq_regulatory_flutter/home/home_page.dart';
import 'package:qhdkfq_regulatory_flutter/home/work_list_page.dart';
import 'package:qhdkfq_regulatory_flutter/tools/custom_button.dart';
import 'package:qhdkfq_regulatory_flutter/tools/h_colors.dart';
import 'package:qhdkfq_regulatory_flutter/tools/my_appbar.dart';
import 'package:qhdkfq_regulatory_flutter/tools/tools.dart';
import '../mock/mock_data.dart';
import '../tools/date_tool.dart';
import 'adaptive_list_item.dart';
import 'department_picker.dart';
@ -47,30 +51,6 @@ class _HomeCategroyListPageState extends State<HomeCategroyListPage>
late TabController _tabController;
late List<String> _radioTitles = [];
///
final List<Category> data = [
Category(
id: '1',
title: '分类一1',
children: [
Category(id: '1-1', title: '子项 1-1'),
Category(id: '1-2', title: '子项 1-2'),
],
),
Category(id: '2', title: '分类二'),
Category(
id: '3',
title: '分类三',
children: [
Category(
id: '3-1',
title: '子项 3-1',
children: [Category(id: '3-1-1', title: '子项 3-1-1')],
),
],
),
];
///
final List<Map<String, dynamic>> _enterpriseData = List.generate(15, (index) {
return {
@ -110,9 +90,10 @@ class _HomeCategroyListPageState extends State<HomeCategroyListPage>
}).toList();
}
@override
///
Widget _listViewWidget() {
final bool isAllowSelect = (widget.type == HomeCategroyType.enterpriseInfo && _selectedTab == 2) ||
(widget.type == HomeCategroyType.keySafety && _selectedTab == 1);
return ListView.builder(
itemCount: _displayData.length,
itemBuilder: (context, index) {
@ -122,15 +103,35 @@ class _HomeCategroyListPageState extends State<HomeCategroyListPage>
margin: const EdgeInsets.symmetric(horizontal: 0, vertical: 6),
elevation: 0.5,
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.zero),
child: GestureDetector(
onTap: () {
if (isAllowSelect) {
pushPage(WorkListPage(type: widget.type), context);
}
/// item
},
child: Padding(
padding: const EdgeInsets.all(15),
child: AdaptiveListItem(
title: enterprise['name'],
items: _getEnterpriseProperties(enterprise),
actions:
widget.type == HomeCategroyType.supervisionSupport
? [
actions: _listItemButtons(),
type: widget.type,
selectTabIndex: _selectedTab,
),
),
),
);
},
);
}
List<Widget> _listItemButtons() {
List<Widget> buttons = [];
if (widget.type == HomeCategroyType.supervisionSupport) {
if (_selectedTab == 0) { //
buttons = [
CustomButton(
height: 40,
text: "查看",
@ -145,15 +146,98 @@ class _HomeCategroyListPageState extends State<HomeCategroyListPage>
text: "监管帮扶",
backgroundColor: Colors.blue,
padding: EdgeInsets.symmetric(horizontal: 30),
onPressed: () {},
),
]
: [],
),
),
);
onPressed: () {
pushPage(DangerEditPage(HelpEditType.help), context);
},
);
),
];
}else{ //
buttons = [
CustomButton(
height: 40,
text: "复查",
backgroundColor: Colors.blue,
padding: EdgeInsets.symmetric(horizontal: 30),
onPressed: () {
pushPage(DangerDetailPage(DangerDetailType.repeat), context);
},
),
CustomButton(
height: 40,
text: "查看",
backgroundColor: Colors.blue,
padding: EdgeInsets.symmetric(horizontal: 30),
onPressed: () {
pushPage(DangerDetailPage(DangerDetailType.detail), context);
},
),
CustomButton(
height: 40,
text: "删除",
backgroundColor: Colors.red,
padding: EdgeInsets.symmetric(horizontal: 30),
onPressed: () {
// TODO ---
},
),
];
}
}else if (widget.type == HomeCategroyType.specialInspection) { //
if (_selectedTab == 0) { //
buttons = [
CustomButton(
height: 40,
text: "查看",
backgroundColor: Colors.blue,
padding: EdgeInsets.symmetric(horizontal: 30),
onPressed: () {
pushPage(ActionTaskPage(), context);
},
),
CustomButton(
height: 40,
text: "检查情况",
backgroundColor: Colors.blue,
padding: EdgeInsets.symmetric(horizontal: 30),
onPressed: () {
pushPage(ActionTaskPage(), context);
},
),
];
}else{ //
buttons = [
CustomButton(
height: 40,
text: "复查",
backgroundColor: Colors.blue,
padding: EdgeInsets.symmetric(horizontal: 30),
onPressed: () {
// pushPage(DangerDetailPage(DangerDetailType.repeat), context);
},
),
CustomButton(
height: 40,
text: "查看",
backgroundColor: Colors.blue,
padding: EdgeInsets.symmetric(horizontal: 30),
onPressed: () {
// pushPage(DangerDetailPage(DangerDetailType.detail), context);
},
),
CustomButton(
height: 40,
text: "删除",
backgroundColor: Colors.red,
padding: EdgeInsets.symmetric(horizontal: 30),
onPressed: () {
// TODO ---
},
),
];
}
}
return buttons;
}
///
@ -280,7 +364,7 @@ class _HomeCategroyListPageState extends State<HomeCategroyListPage>
backgroundColor: Colors.transparent,
builder:
(ctx) => DepartmentPicker(
data: data,
data: mockCategories,
onSelected: (selectedId) {
setState(() {
_selectedCategoryId = selectedId;
@ -360,20 +444,20 @@ class _HomeCategroyListPageState extends State<HomeCategroyListPage>
isSelected
? const Icon(
Icons.check_circle,
size: 22,
size: 20,
color: Colors.blue,
)
: const Icon(
Icons.circle_outlined,
size: 22,
size: 20,
color: Colors.grey,
),
),
SizedBox(width: 10),
SizedBox(width: 5),
Text(
label,
style: TextStyle(
fontSize: 15,
fontSize: 12,
color: isSelected ? Colors.blue : Colors.grey[700],
fontWeight: isSelected ? FontWeight.bold : FontWeight.normal,
),
@ -429,7 +513,7 @@ class _HomeCategroyListPageState extends State<HomeCategroyListPage>
),
),
),
const SizedBox(width: 10),
// const SizedBox(width: 10),
SizedBox(
height: searchBarHeight,
child: TextButton(
@ -448,14 +532,14 @@ class _HomeCategroyListPageState extends State<HomeCategroyListPage>
});
},
style: TextButton.styleFrom(
padding: const EdgeInsets.symmetric(horizontal: 16),
padding: const EdgeInsets.symmetric(horizontal: 10),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(borderRadius),
),
),
child: const Text(
"搜索",
style: TextStyle(color: Colors.blue, fontSize: 16),
style: TextStyle(color: Colors.blue, fontSize: 14),
),
),
),
@ -510,12 +594,12 @@ class _HomeCategroyListPageState extends State<HomeCategroyListPage>
},
),
),
const SizedBox(height: 15),
const SizedBox(height: 5),
Container(
padding: const EdgeInsets.all(15),
padding: const EdgeInsets.all(10),
color: Colors.white,
child: Column(
spacing: 15,
spacing: 10,
children: [
_searchBar(),
if (widget.type == HomeCategroyType.enterpriseInfo ||
@ -567,7 +651,7 @@ class _HomeCategroyListPageState extends State<HomeCategroyListPage>
],
),
),
const SizedBox(height: 10),
const SizedBox(height: 5),
Expanded(child: _listViewWidget()),
],
),

View File

@ -13,7 +13,7 @@ enum HomeCategroyType {
supervisionSupport("监管帮扶", ["监管帮扶管理", "监管帮扶隐患管理"]), //
specialInspection("专项检查", ["专项检查管理", "专项检查隐患管理"]), //
disasterReduction("防灾减灾", []), // ;
supervisionRecord("帮扶记录", []); // ;
supervisionRecord("帮扶记录", []); // ;
final String title;
final List<String> tabLists;

View File

@ -0,0 +1,348 @@
import 'package:flutter/material.dart';
import 'package:qhdkfq_regulatory_flutter/home/home_page.dart';
import 'package:qhdkfq_regulatory_flutter/tools/custom_button.dart';
import 'package:qhdkfq_regulatory_flutter/tools/h_colors.dart';
import 'package:qhdkfq_regulatory_flutter/tools/my_appbar.dart';
import 'adaptive_list_item.dart';
class WorkListPage extends StatefulWidget {
const WorkListPage({super.key, required this.type});
final int selectTabIndex = 0;
final HomeCategroyType type;
@override
State<WorkListPage> createState() => _WorkListPageState();
}
class _WorkListPageState extends State<WorkListPage> {
final _searchController = TextEditingController();
var _navTitle = "";
///
List<Map<String, dynamic>> _displayData = [];
///
final List<Map<String, dynamic>> _enterpriseData = List.generate(15, (index) {
return {
'id': index,
'name': '${['化工', '制造', '能源', '食品', '建筑'][index % 5]}企业${index + 1}',
'creditCode': '9137${10000 + index}',
'industry': ['化工', '制造', '能源', '食品', '建筑'][index % 5],
'scale': ['大型', '中型', '小型'][index % 3],
'safetyLevel': ['A', 'B', 'C'][index % 3],
'lastCheck': '2025-0${6 + index % 3}-${10 + index % 20}',
'riskCount': index % 4,
'hiddenDangerCount': index % 5,
'isDivided': index % 3 == 0,
};
});
@override
void initState() {
super.initState();
_displayData = _enterpriseData; //
switch(widget.type) {
case HomeCategroyType.enterpriseInfo:
_navTitle = "持证人员信息列表";
case HomeCategroyType.dualPrevention:
// TODO: Handle this case.
throw UnimplementedError();
case HomeCategroyType.keySafety:
_navTitle = "危险作业";
case HomeCategroyType.supervisionSupport:
// TODO: Handle this case.
throw UnimplementedError();
case HomeCategroyType.specialInspection:
// TODO: Handle this case.
throw UnimplementedError();
case HomeCategroyType.disasterReduction:
// TODO: Handle this case.
case HomeCategroyType.supervisionRecord:
throw UnimplementedError();
}
}
@override
///
Widget _listViewWidget() {
return ListView.builder(
itemCount: _displayData.length,
itemBuilder: (context, index) {
final enterprise = _displayData[index];
return Card(
color: Colors.white,
margin: const EdgeInsets.symmetric(horizontal: 0, vertical: 6),
elevation: 0.5,
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.zero),
child: Padding(
padding: const EdgeInsets.all(15),
child: AdaptiveListItem(
title: enterprise['name'],
items: _getEnterpriseProperties(enterprise),
actions: _listItemButtons(),
selectTabIndex: 0,
type: widget.type,
),
),
);
},
);
}
List<Widget> _listItemButtons() {
List<Widget> buttons = [];
if (widget.type == HomeCategroyType.supervisionSupport) {}
return buttons;
}
///
List<Map<String, String>> _getEnterpriseProperties(
Map<String, dynamic> enterprise,
) {
switch (widget.type) {
case HomeCategroyType.enterpriseInfo:
return [
{"title": "统一信用代码", "value": enterprise['creditCode']},
{"title": "所属行业", "value": enterprise['industry']},
{"title": "企业规模", "value": enterprise['scale']},
{"title": "安全等级", "value": enterprise['safetyLevel']},
{"title": "最近检查", "value": enterprise['lastCheck']},
];
case HomeCategroyType.dualPrevention:
return [
{"title": "风险点数量", "value": "${enterprise['riskCount']}"},
{"title": "隐患数量", "value": "${enterprise['hiddenDangerCount']}"},
{"title": "最近检查", "value": enterprise['lastCheck']},
{"title": "安全等级", "value": enterprise['safetyLevel']},
];
case HomeCategroyType.keySafety:
return [
{"title": "重点区域", "value": "生产车间${enterprise['id'] % 5 + 1}"},
{
"title": "责任人",
"value": "${['', '', '', '', ''][enterprise['id'] % 5]}",
},
{"title": "上次检查", "value": enterprise['lastCheck']},
{"title": "状态", "value": enterprise['riskCount'] > 2 ? "需整改" : "正常"},
];
case HomeCategroyType.supervisionSupport:
return [
{
"title": "帮扶类型",
"value": ["技术指导", "安全培训", "设备支持"][enterprise['id'] % 3],
},
{
"title": "负责人",
"value": "${['', '主任', '经理'][enterprise['id'] % 3]}",
},
{"title": "开始日期", "value": "2025-06-${10 + enterprise['id'] % 20}"},
{
"title": "状态",
"value": ["进行中", "已完成", "待开始"][enterprise['id'] % 3],
},
];
case HomeCategroyType.specialInspection:
return [
{
"title": "检查类型",
"value": ["常规", "突击", "专项"][enterprise['id'] % 3],
},
{
"title": "检查结果",
"value": ["合格", "需整改", "不合格"][enterprise['id'] % 3],
},
{"title": "检查日期", "value": enterprise['lastCheck']},
{
"title": "检查人",
"value": "${['', '主任', '专员'][enterprise['id'] % 3]}",
},
];
case HomeCategroyType.disasterReduction:
return [
{"title": "防灾设施", "value": "${enterprise['id'] % 5 + 3}"},
{"title": "上次演练", "value": "2025-05-${20 + enterprise['id'] % 10}"},
{
"title": "应急预案",
"value": ["已备案", "待更新", "完善中"][enterprise['id'] % 3],
},
{
"title": "责任人",
"value": "${['主管', '经理', '专员'][enterprise['id'] % 3]}",
},
];
default:
return [
{"title": "统一信用代码", "value": enterprise['creditCode']},
{"title": "所属行业", "value": enterprise['industry']},
];
}
}
/// radio
Row _buildDynamicRadioRow({
required List<String> options, //
required int? selectedValue, //
required ValueChanged<int?> onChanged, //
}) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
for (int i = 0; i < options.length; i++) ...[
_buildRadioOption(
isSelected: selectedValue == i,
label: options[i],
value: i, // 使
onChanged: onChanged,
),
if (i < options.length - 1) const SizedBox(width: 5), //
],
],
);
}
/// Radio
Widget _buildRadioOption({
required bool isSelected,
required String label,
required int value,
required ValueChanged<int?> onChanged,
}) {
return InkWell(
onTap: () => onChanged(value),
splashColor: Colors.blue,
borderRadius: BorderRadius.circular(20),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
// -
SizedBox(
child:
isSelected
? const Icon(
Icons.check_circle,
size: 20,
color: Colors.blue,
)
: const Icon(
Icons.circle_outlined,
size: 20,
color: Colors.grey,
),
),
SizedBox(width: 5),
Text(
label,
style: TextStyle(
fontSize: 12,
color: isSelected ? Colors.blue : Colors.grey[700],
fontWeight: isSelected ? FontWeight.bold : FontWeight.normal,
),
),
SizedBox(width: 10),
],
),
);
}
//
Widget _searchBar() {
const double searchBarHeight = 40.0;
const double borderRadius = searchBarHeight / 2;
return Row(
children: [
Expanded(
child: SizedBox(
height: searchBarHeight,
child: TextField(
controller: _searchController,
decoration: InputDecoration(
filled: true,
fillColor: const Color(0xFFF5F5F5),
prefixIcon: const Icon(Icons.search_rounded, size: 20),
hintText: "请输入关键字",
hintStyle: const TextStyle(fontSize: 14),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(borderRadius),
borderSide: BorderSide.none,
),
isDense: true,
contentPadding: const EdgeInsets.symmetric(vertical: 0),
),
onChanged: (val) {
//
setState(() {
if (val.isEmpty) {
_displayData = _enterpriseData;
} else {
_displayData =
_enterpriseData.where((e) {
return e['name'].contains(val) ||
e['creditCode'].contains(val);
}).toList();
}
});
},
onSubmitted: (val) {
//
},
),
),
),
// const SizedBox(width: 10),
SizedBox(
height: searchBarHeight,
child: TextButton(
onPressed: () {
//
setState(() {
if (_searchController.text.isEmpty) {
_displayData = _enterpriseData;
} else {
_displayData =
_enterpriseData.where((e) {
return e['name'].contains(_searchController.text) ||
e['creditCode'].contains(_searchController.text);
}).toList();
}
});
},
style: TextButton.styleFrom(
padding: const EdgeInsets.symmetric(horizontal: 10),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(borderRadius),
),
),
child: const Text(
"搜索",
style: TextStyle(color: Colors.blue, fontSize: 14),
),
),
),
],
);
}
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: h_backGroundColor(),
appBar: MyAppbar(title: _navTitle),
body: SafeArea(
child: Column(
children: [
const SizedBox(height: 5),
Container(
padding: const EdgeInsets.all(10),
color: Colors.white,
child: Column(spacing: 10, children: [_searchBar()]),
),
const SizedBox(height: 5),
Expanded(child: _listViewWidget()),
],
),
),
);
}
}

27
lib/mock/mock_data.dart Normal file
View File

@ -0,0 +1,27 @@
// lib/mock/mock_data.dart
import '../models/department_category.dart';
///
final List<DepartmentCategory> mockCategories = [
DepartmentCategory(
id: '1',
title: '分类一1',
children: [
DepartmentCategory(id: '1-1', title: '子项 1-1'),
DepartmentCategory(id: '1-2', title: '子项 1-2'),
],
),
DepartmentCategory(id: '2', title: '分类二'),
DepartmentCategory(
id: '3',
title: '分类三',
children: [
DepartmentCategory(
id: '3-1',
title: '子项 3-1',
children: [DepartmentCategory(id: '3-1-1', title: '子项 3-1-1')],
),
],
),
];

View File

@ -0,0 +1,38 @@
// lib/models/department_category.dart
class DepartmentCategory {
final String id;
final String title;
///
final List<DepartmentCategory> children;
DepartmentCategory({
required this.id,
required this.title,
List<DepartmentCategory>? children,
}) : children = children ?? const [];
factory DepartmentCategory.fromJson(Map<String, dynamic> json) {
final raw = json['children'];
final List<DepartmentCategory> parsedChildren = (raw is List)
? raw
.whereType<Map<String, dynamic>>()
.map((e) => DepartmentCategory.fromJson(e))
.toList()
: [];
return DepartmentCategory(
id: json['id'] as String,
title: json['title'] as String,
children: parsedChildren,
);
}
Map<String, dynamic> toJson() => {
'id': id,
'title': title,
// children 使
'children': children.map((c) => c.toJson()).toList(),
};
}

View File

@ -105,6 +105,14 @@ packages:
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
source: hosted
version: "2.1.1"
dotted_border:
dependency: "direct main"
description:
name: dotted_border
sha256: "108837e11848ca776c53b30bc870086f84b62ed6e01c503ed976e8f8c7df9c04"
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
source: hosted
version: "2.1.0"
extended_image:
dependency: transitive
description:
@ -456,6 +464,22 @@ packages:
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
source: hosted
version: "1.9.1"
path_drawing:
dependency: transitive
description:
name: path_drawing
sha256: bbb1934c0cbb03091af082a6389ca2080345291ef07a5fa6d6e078ba8682f977
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
source: hosted
version: "1.0.1"
path_parsing:
dependency: transitive
description:
name: path_parsing
sha256: "883402936929eac138ee0a45da5b0f2c80f89913e6dc3bf77eb65b84b409c6ca"
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
source: hosted
version: "1.1.0"
path_provider:
dependency: transitive
description:

View File

@ -43,11 +43,10 @@ dependencies:
wechat_assets_picker: ^9.5.1
# 日历
table_calendar: ^3.2.0
#日期格式化
intl: ^0.20.0
#图片查看大图
photo_view: ^0.15.0
# 状态管理
provider: ^6.1.2
# 屏幕适配相关
@ -67,9 +66,8 @@ dependencies:
webview_flutter: ^4.2.2
# 显示html文本
flutter_html: ^3.0.0-alpha.6
# 虚线框
dotted_border: ^2.1.0
dev_dependencies: