diff --git a/assets/images/close.png b/assets/images/close.png new file mode 100644 index 0000000..033f2de Binary files /dev/null and b/assets/images/close.png differ diff --git a/lib/Custom/custom_button.dart b/lib/Custom/custom_button.dart new file mode 100644 index 0000000..4706ac5 --- /dev/null +++ b/lib/Custom/custom_button.dart @@ -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, + ), + ), + ), + ), + ); + } +} \ No newline at end of file diff --git a/lib/Custom/dashed_line_text.dart b/lib/Custom/dashed_line_text.dart index 2eeb25c..319ee0c 100644 --- a/lib/Custom/dashed_line_text.dart +++ b/lib/Custom/dashed_line_text.dart @@ -19,6 +19,7 @@ class DashedLineText extends StatelessWidget { @override Widget build(BuildContext context) { return Row( + mainAxisAlignment: MainAxisAlignment.center, children: [ Expanded( diff --git a/lib/Custom/dateTime_picker_bottom_sheet.dart b/lib/Custom/dateTime_picker_bottom_sheet.dart new file mode 100644 index 0000000..a514119 --- /dev/null +++ b/lib/Custom/dateTime_picker_bottom_sheet.dart @@ -0,0 +1,72 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; + +class DateTimePickerBottomSheet extends StatefulWidget { + final DateTime? initialDateTime; + final ValueChanged onDateTimeSelected; + + const DateTimePickerBottomSheet({ + super.key, + this.initialDateTime, + required this.onDateTimeSelected, + }); + + @override + State createState() => + _DateTimePickerBottomSheetState(); +} + +class _DateTimePickerBottomSheetState extends State { + 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, + ), + ), + ], + ), + ); + } +} diff --git a/lib/Custom/edit_list_items.dart b/lib/Custom/edit_list_items.dart new file mode 100644 index 0000000..17c6c81 --- /dev/null +++ b/lib/Custom/edit_list_items.dart @@ -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 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 options, + required int selectedValue, + required ValueChanged 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 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( + activeColor: Colors.blue, + value: true, + groupValue: groupValue, + onChanged: (val) => onChanged(val!), + ), + Text(yesLabel), + ], + ), + const SizedBox(width: 16), + Row( + children: [ + Radio( + 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 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)), + ], + ); + } +} diff --git a/lib/Custom/help_department_person_item.dart b/lib/Custom/help_department_person_item.dart new file mode 100644 index 0000000..69094a1 --- /dev/null +++ b/lib/Custom/help_department_person_item.dart @@ -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 { + final List> _entries = [ + {'dept': '请选择', 'person': '请选择'}, + ]; +// 已选择的分类 id + String? _selectedCategoryId; + /// 外部调用:新增一条 + void addEntry() { + setState(() => _entries.add({'dept': '', 'person': ''})); + } + + /// 外部调用:获取当前所有条目 + List> 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), + ], + ], + ); + } +} diff --git a/lib/Custom/photo_picker_row.dart b/lib/Custom/photo_picker_row.dart new file mode 100644 index 0000000..29c107f --- /dev/null +++ b/lib/Custom/photo_picker_row.dart @@ -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> 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 { + final ImagePicker _picker = ImagePicker(); + final List _files = []; + + Future _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 _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 _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? 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 = [ + 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> 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, + ), + ), + ], + ), + ), + ], + ), + ); + } +} diff --git a/lib/Custom/single_image_viewer.dart b/lib/Custom/single_image_viewer.dart new file mode 100644 index 0000000..ddf7386 --- /dev/null +++ b/lib/Custom/single_image_viewer.dart @@ -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(); + }, + ), + ), + ); + } +} diff --git a/lib/home/action/action_task_page.dart b/lib/home/action/action_task_page.dart new file mode 100644 index 0000000..77c927d --- /dev/null +++ b/lib/home/action/action_task_page.dart @@ -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(); + } +} diff --git a/lib/home/adaptive_list_item.dart b/lib/home/adaptive_list_item.dart index 1e68334..d263f83 100644 --- a/lib/home/adaptive_list_item.dart +++ b/lib/home/adaptive_list_item.dart @@ -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> items; final List? 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>> 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,9 +31,16 @@ class AdaptiveListItem extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ // 标题 - Text( - title, - style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 16), + 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), diff --git a/lib/home/danger_edit_page.dart b/lib/home/danger_edit_page.dart deleted file mode 100644 index 1aaf1f4..0000000 --- a/lib/home/danger_edit_page.dart +++ /dev/null @@ -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 createState() => _DangerEditPageState(); -} - -class _DangerEditPageState extends State { - @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()), - ), - ], - )), - ); - } -} - diff --git a/lib/home/department_picker.dart b/lib/home/department_picker.dart index 98851c3..80b42a3 100644 --- a/lib/home/department_picker.dart +++ b/lib/home/department_picker.dart @@ -1,19 +1,8 @@ import 'package:flutter/material.dart'; - -class Category { - final String id; - final String title; - final List children; - - Category({ - required this.id, - required this.title, - this.children = const [], - }); -} +import '../models/department_category.dart'; class DepartmentPicker extends StatefulWidget { - final List data; + final List data; final String? initialSelectedId; final Set? initialExpandedSet; final ValueChanged onSelected; @@ -41,7 +30,7 @@ class _DepartmentPickerState extends State { expandedSet = Set.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 { 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 { 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 { ), ), ), + 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( diff --git a/lib/home/help/danger_detail_page.dart b/lib/home/help/danger_detail_page.dart new file mode 100644 index 0000000..0bfb334 --- /dev/null +++ b/lib/home/help/danger_detail_page.dart @@ -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 createState() => _DangerDetailPageState(); +} + +class _DangerDetailPageState extends State { + 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 _buildSectionItems(List 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 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 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 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 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()), + ), + ); + } +} diff --git a/lib/home/help/danger_edit_page.dart b/lib/home/help/danger_edit_page.dart new file mode 100644 index 0000000..314b94a --- /dev/null +++ b/lib/home/help/danger_edit_page.dart @@ -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 createState() => _DangerEditPageState(); +} + +class _DangerEditPageState extends State { + // 控制器列表 + final _companyController = TextEditingController(text: '公司'); + final _addressController = TextEditingController(); + final _nameController = TextEditingController(text: '韩双'); + final _phoneController = TextEditingController(); + final _codeController = TextEditingController(); + final _placeController = TextEditingController(); + + // 单选选项 + final List _radioTitles = ['日常检查', '专项检查', '总和检查', '执法检查', '举报检查']; + int _selectedType = 0; +// 创建一个 GlobalKey 来拿到 State + final _sectionKey = GlobalKey(); + + /// 帮扶时间 + 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 = [ + 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 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 = [ + // 基本信息标题 + 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(), + ], + ), + ), + ); + } +} diff --git a/lib/home/help_detail_page.dart b/lib/home/help/help_detail_page.dart similarity index 71% rename from lib/home/help_detail_page.dart rename to lib/home/help/help_detail_page.dart index c501613..ea08c39 100644 --- a/lib/home/help_detail_page.dart +++ b/lib/home/help/help_detail_page.dart @@ -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 { 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 { ); } - 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)), - ], - ), - ); - } + } diff --git a/lib/home/help/help_record_page.dart b/lib/home/help/help_record_page.dart new file mode 100644 index 0000000..61cc81a --- /dev/null +++ b/lib/home/help/help_record_page.dart @@ -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 createState() => _HelpRecordPageState(); +} + +class _HelpRecordPageState extends State { + int _selectRadioIndex = 0; + + // 选项列表也可以放到 State 里,或者在 build 里声明都行 + final List _radioTitles = [ + "日常检查", + "专项检查", + "总和检查", + "执法检查", + "举报检查", + ]; + /// 模拟企业数据 + final List> _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 options, // 选项文本数组 + required int? selectedValue, // 当前选中的值(索引) + required ValueChanged 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 _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 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> _getEnterpriseProperties( + Map 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 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()), + + ], + ), + ), + ); + } + + +} diff --git a/lib/home/help/help_standard_page.dart b/lib/home/help/help_standard_page.dart new file mode 100644 index 0000000..3541918 --- /dev/null +++ b/lib/home/help/help_standard_page.dart @@ -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 createState() => _HelpStandardPageState(); +} + +class _HelpStandardPageState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: MyAppbar(title: "帮扶标准"), + body: Column( + children: [ + Expanded(child: ListView()), + CustomButton(text: "保存", backgroundColor: Colors.blue) + ], + ), + ); + } +} diff --git a/lib/home/help_record_page.dart b/lib/home/help_record_page.dart deleted file mode 100644 index fa0d2ea..0000000 --- a/lib/home/help_record_page.dart +++ /dev/null @@ -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 createState() => _HelpRecordPageState(); -} - -class _HelpRecordPageState extends State { - Widget _buildDynamicRadioRow({ - required List options, // 选项文本数组 - required int? selectedValue, // 当前选中的值(索引) - required ValueChanged 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 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 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: [ - - ], - ) - ], - ), - ), - ); - } -} diff --git a/lib/home/home_categroy_list_page.dart b/lib/home/home_categroy_list_page.dart index 655a3c5..d375915 100644 --- a/lib/home/home_categroy_list_page.dart +++ b/lib/home/home_categroy_list_page.dart @@ -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 late TabController _tabController; late List _radioTitles = []; - /// 模拟数据 - final List 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> _enterpriseData = List.generate(15, (index) { return { @@ -110,9 +90,10 @@ class _HomeCategroyListPageState extends State }).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,33 +103,22 @@ class _HomeCategroyListPageState extends State 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: - widget.type == HomeCategroyType.supervisionSupport - ? [ - - CustomButton( - height: 40, - text: "查看", - backgroundColor: Colors.blue, - padding: EdgeInsets.symmetric(horizontal: 30), - onPressed: () { - pushPage(HelpRecordPage(), context); - }, - ), - CustomButton( - height: 40, - text: "监管帮扶", - backgroundColor: Colors.blue, - padding: EdgeInsets.symmetric(horizontal: 30), - onPressed: () {}, - ), - ] - : [], + 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: _listItemButtons(), + type: widget.type, + selectTabIndex: _selectedTab, + ), ), ), ); @@ -156,6 +126,120 @@ class _HomeCategroyListPageState extends State ); } + + List _listItemButtons() { + List buttons = []; + if (widget.type == HomeCategroyType.supervisionSupport) { + if (_selectedTab == 0) { // 监管帮扶管理 + buttons = [ + CustomButton( + height: 40, + text: "查看", + backgroundColor: Colors.blue, + padding: EdgeInsets.symmetric(horizontal: 30), + onPressed: () { + pushPage(HelpRecordPage(), context); + }, + ), + CustomButton( + height: 40, + text: "监管帮扶", + backgroundColor: Colors.blue, + padding: EdgeInsets.symmetric(horizontal: 30), + 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; + } + /// 测试数据 List> _getEnterpriseProperties( Map enterprise, @@ -280,7 +364,7 @@ class _HomeCategroyListPageState extends State backgroundColor: Colors.transparent, builder: (ctx) => DepartmentPicker( - data: data, + data: mockCategories, onSelected: (selectedId) { setState(() { _selectedCategoryId = selectedId; @@ -360,20 +444,20 @@ class _HomeCategroyListPageState extends State 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 ), ), ), - const SizedBox(width: 10), + // const SizedBox(width: 10), SizedBox( height: searchBarHeight, child: TextButton( @@ -448,14 +532,14 @@ class _HomeCategroyListPageState extends State }); }, 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 }, ), ), - 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 ], ), ), - const SizedBox(height: 10), + const SizedBox(height: 5), Expanded(child: _listViewWidget()), ], ), diff --git a/lib/home/home_page.dart b/lib/home/home_page.dart index 90c194f..86d6026 100644 --- a/lib/home/home_page.dart +++ b/lib/home/home_page.dart @@ -13,7 +13,7 @@ enum HomeCategroyType { supervisionSupport("监管帮扶", ["监管帮扶管理", "监管帮扶隐患管理"]), // 监管帮扶 specialInspection("专项检查", ["专项检查管理", "专项检查隐患管理"]), // 专项检查 disasterReduction("防灾减灾", []), // 防灾减灾; - supervisionRecord("帮扶记录", []); // 防灾减灾; + supervisionRecord("帮扶记录", []); // 帮扶记录; final String title; final List tabLists; diff --git a/lib/home/work_list_page.dart b/lib/home/work_list_page.dart new file mode 100644 index 0000000..0667062 --- /dev/null +++ b/lib/home/work_list_page.dart @@ -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 createState() => _WorkListPageState(); +} + +class _WorkListPageState extends State { + final _searchController = TextEditingController(); + var _navTitle = ""; + + /// 当前显示的企业数据(过滤后) + List> _displayData = []; + + /// 模拟企业数据 + final List> _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 _listItemButtons() { + List buttons = []; + if (widget.type == HomeCategroyType.supervisionSupport) {} + return buttons; + } + + /// 测试数据 + List> _getEnterpriseProperties( + Map 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 options, // 选项文本数组 + required int? selectedValue, // 当前选中的值(索引) + required ValueChanged 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 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()), + ], + ), + ), + ); + } +} diff --git a/lib/mock/mock_data.dart b/lib/mock/mock_data.dart new file mode 100644 index 0000000..5d4817c --- /dev/null +++ b/lib/mock/mock_data.dart @@ -0,0 +1,27 @@ +// lib/mock/mock_data.dart + +import '../models/department_category.dart'; + +/// 模拟数据,后续可直接替换成接口返回 +final List 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')], + ), + ], + ), +]; diff --git a/lib/models/department_category.dart b/lib/models/department_category.dart new file mode 100644 index 0000000..fbeab98 --- /dev/null +++ b/lib/models/department_category.dart @@ -0,0 +1,38 @@ +// lib/models/department_category.dart + +class DepartmentCategory { + final String id; + final String title; + + /// 始终是一个列表,可能为空 + final List children; + + DepartmentCategory({ + required this.id, + required this.title, + List? children, + }) : children = children ?? const []; + + factory DepartmentCategory.fromJson(Map json) { + + final raw = json['children']; + final List parsedChildren = (raw is List) + ? raw + .whereType>() + .map((e) => DepartmentCategory.fromJson(e)) + .toList() + : []; + return DepartmentCategory( + id: json['id'] as String, + title: json['title'] as String, + children: parsedChildren, + ); + } + + Map toJson() => { + 'id': id, + 'title': title, + // 始终输出 children 数组,即使为空 + 'children': children.map((c) => c.toJson()).toList(), + }; +} diff --git a/pubspec.lock b/pubspec.lock index 39b0b10..33a2e3f 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -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: diff --git a/pubspec.yaml b/pubspec.yaml index c6cb455..6a9d654 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -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: