first
parent
2d2480d702
commit
7c529de846
Binary file not shown.
|
After Width: | Height: | Size: 480 B |
|
|
@ -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,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -19,6 +19,7 @@ class DashedLineText extends StatelessWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Row(
|
return Row(
|
||||||
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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)),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,21 +1,28 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:qhdkfq_regulatory_flutter/home/home_page.dart';
|
||||||
|
|
||||||
class AdaptiveListItem extends StatelessWidget {
|
class AdaptiveListItem extends StatelessWidget {
|
||||||
final String title;
|
final String title;
|
||||||
final List<Map<String, String>> items;
|
final List<Map<String, String>> items;
|
||||||
final List<Widget>? actions;
|
final List<Widget>? actions;
|
||||||
|
final HomeCategroyType type;
|
||||||
|
final int selectTabIndex;
|
||||||
|
|
||||||
const AdaptiveListItem({
|
const AdaptiveListItem({
|
||||||
super.key,
|
super.key,
|
||||||
required this.title,
|
required this.title,
|
||||||
required this.items,
|
required this.items,
|
||||||
required this.actions,
|
required this.actions,
|
||||||
|
required this.type,
|
||||||
|
required this.selectTabIndex,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
// 按 2 个一组切片
|
// 按 2 个一组切片
|
||||||
final List<List<Map<String, String>>> rows = [];
|
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) {
|
for (var i = 0; i < items.length; i += 2) {
|
||||||
rows.add(items.sublist(i, i + 2 > items.length ? 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,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
// 标题
|
// 标题
|
||||||
Text(
|
Row(
|
||||||
title,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 16),
|
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),
|
const SizedBox(height: 8),
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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()),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
)),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -1,19 +1,8 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import '../models/department_category.dart';
|
||||||
class Category {
|
|
||||||
final String id;
|
|
||||||
final String title;
|
|
||||||
final List<Category> children;
|
|
||||||
|
|
||||||
Category({
|
|
||||||
required this.id,
|
|
||||||
required this.title,
|
|
||||||
this.children = const [],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
class DepartmentPicker extends StatefulWidget {
|
class DepartmentPicker extends StatefulWidget {
|
||||||
final List<Category> data;
|
final List<DepartmentCategory> data;
|
||||||
final String? initialSelectedId;
|
final String? initialSelectedId;
|
||||||
final Set<String>? initialExpandedSet;
|
final Set<String>? initialExpandedSet;
|
||||||
final ValueChanged<String?> onSelected;
|
final ValueChanged<String?> onSelected;
|
||||||
|
|
@ -41,7 +30,7 @@ class _DepartmentPickerState extends State<DepartmentPicker> {
|
||||||
expandedSet = Set<String>.from(widget.initialExpandedSet ?? {});
|
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 hasChildren = cat.children.isNotEmpty;
|
||||||
final bool isExpanded = expandedSet.contains(cat.id);
|
final bool isExpanded = expandedSet.contains(cat.id);
|
||||||
final bool isSelected = cat.id == selectedId;
|
final bool isSelected = cat.id == selectedId;
|
||||||
|
|
@ -52,11 +41,9 @@ class _DepartmentPickerState extends State<DepartmentPicker> {
|
||||||
onTap: () {
|
onTap: () {
|
||||||
setState(() {
|
setState(() {
|
||||||
if (hasChildren) {
|
if (hasChildren) {
|
||||||
if (isExpanded) {
|
isExpanded
|
||||||
expandedSet.remove(cat.id);
|
? expandedSet.remove(cat.id)
|
||||||
} else {
|
: expandedSet.add(cat.id);
|
||||||
expandedSet.add(cat.id);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
selectedId = cat.id;
|
selectedId = cat.id;
|
||||||
});
|
});
|
||||||
|
|
@ -66,28 +53,26 @@ class _DepartmentPickerState extends State<DepartmentPicker> {
|
||||||
child: Row(
|
child: Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
// 左侧缩进
|
|
||||||
SizedBox(width: 16.0 * indent),
|
SizedBox(width: 16.0 * indent),
|
||||||
// 展开/占位图标
|
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: 24,
|
width: 24,
|
||||||
child: hasChildren
|
child: hasChildren
|
||||||
? Icon(
|
? Icon(
|
||||||
isExpanded ? Icons.arrow_drop_down : Icons.arrow_right_outlined,
|
isExpanded
|
||||||
|
? Icons.arrow_drop_down
|
||||||
|
: Icons.arrow_right_outlined,
|
||||||
size: 30,
|
size: 30,
|
||||||
color: Colors.grey[600],
|
color: Colors.grey[600],
|
||||||
)
|
)
|
||||||
: const SizedBox.shrink(),
|
: const SizedBox.shrink(),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
// 标题
|
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.symmetric(vertical: 12),
|
padding: const EdgeInsets.symmetric(vertical: 12),
|
||||||
child: Text(cat.title),
|
child: Text(cat.title),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
// 单选圈保持右侧对齐
|
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||||
child: Icon(
|
child: Icon(
|
||||||
|
|
@ -101,13 +86,16 @@ class _DepartmentPickerState extends State<DepartmentPicker> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
if (hasChildren && isExpanded)
|
if (hasChildren && isExpanded)
|
||||||
...cat.children.map((c) => _buildRow(c, indent + 1)),
|
...cat.children
|
||||||
// const Divider(height: 1),
|
.map((c) => _buildRow(c, indent + 1)),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Container(
|
return Container(
|
||||||
|
|
|
||||||
|
|
@ -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()),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,9 +1,10 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:qhdkfq_regulatory_flutter/Custom/dashed_line_text.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/h_colors.dart';
|
||||||
import 'package:qhdkfq_regulatory_flutter/tools/my_appbar.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 {
|
class HelpDetailPage extends StatefulWidget {
|
||||||
const HelpDetailPage({super.key});
|
const HelpDetailPage({super.key});
|
||||||
|
|
@ -54,7 +55,7 @@ class _HelpDetailPageState extends State<HelpDetailPage> {
|
||||||
itemCount: _infoList.length,
|
itemCount: _infoList.length,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
final InfoModel item = _infoList[index];
|
final InfoModel item = _infoList[index];
|
||||||
return _createRowSpaceBetweenItem(
|
return EditListItems.createRowSpaceBetweenItem(
|
||||||
horizontalPadding: 10,
|
horizontalPadding: 10,
|
||||||
verticalPadding: 15,
|
verticalPadding: 15,
|
||||||
leftText: item.title,
|
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)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -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()),
|
||||||
|
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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: [
|
|
||||||
|
|
||||||
],
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,13 +1,17 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:qhdkfq_regulatory_flutter/home/danger_edit_page.dart';
|
import 'package:qhdkfq_regulatory_flutter/home/action/action_task_page.dart';
|
||||||
import 'package:qhdkfq_regulatory_flutter/home/help_detail_page.dart';
|
import 'package:qhdkfq_regulatory_flutter/home/help/danger_detail_page.dart';
|
||||||
import 'package:qhdkfq_regulatory_flutter/home/help_record_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/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/custom_button.dart';
|
||||||
import 'package:qhdkfq_regulatory_flutter/tools/h_colors.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/my_appbar.dart';
|
||||||
import 'package:qhdkfq_regulatory_flutter/tools/tools.dart';
|
import 'package:qhdkfq_regulatory_flutter/tools/tools.dart';
|
||||||
|
|
||||||
|
import '../mock/mock_data.dart';
|
||||||
import '../tools/date_tool.dart';
|
import '../tools/date_tool.dart';
|
||||||
import 'adaptive_list_item.dart';
|
import 'adaptive_list_item.dart';
|
||||||
import 'department_picker.dart';
|
import 'department_picker.dart';
|
||||||
|
|
@ -47,30 +51,6 @@ class _HomeCategroyListPageState extends State<HomeCategroyListPage>
|
||||||
late TabController _tabController;
|
late TabController _tabController;
|
||||||
late List<String> _radioTitles = [];
|
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) {
|
final List<Map<String, dynamic>> _enterpriseData = List.generate(15, (index) {
|
||||||
return {
|
return {
|
||||||
|
|
@ -110,9 +90,10 @@ class _HomeCategroyListPageState extends State<HomeCategroyListPage>
|
||||||
}).toList();
|
}).toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
/// 列表
|
/// 列表
|
||||||
Widget _listViewWidget() {
|
Widget _listViewWidget() {
|
||||||
|
final bool isAllowSelect = (widget.type == HomeCategroyType.enterpriseInfo && _selectedTab == 2) ||
|
||||||
|
(widget.type == HomeCategroyType.keySafety && _selectedTab == 1);
|
||||||
return ListView.builder(
|
return ListView.builder(
|
||||||
itemCount: _displayData.length,
|
itemCount: _displayData.length,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
|
|
@ -122,33 +103,22 @@ class _HomeCategroyListPageState extends State<HomeCategroyListPage>
|
||||||
margin: const EdgeInsets.symmetric(horizontal: 0, vertical: 6),
|
margin: const EdgeInsets.symmetric(horizontal: 0, vertical: 6),
|
||||||
elevation: 0.5,
|
elevation: 0.5,
|
||||||
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.zero),
|
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.zero),
|
||||||
child: Padding(
|
child: GestureDetector(
|
||||||
padding: const EdgeInsets.all(15),
|
onTap: () {
|
||||||
child: AdaptiveListItem(
|
if (isAllowSelect) {
|
||||||
title: enterprise['name'],
|
pushPage(WorkListPage(type: widget.type), context);
|
||||||
items: _getEnterpriseProperties(enterprise),
|
}
|
||||||
actions:
|
/// 不能直接点击的item不处理
|
||||||
widget.type == HomeCategroyType.supervisionSupport
|
},
|
||||||
? [
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(15),
|
||||||
CustomButton(
|
child: AdaptiveListItem(
|
||||||
height: 40,
|
title: enterprise['name'],
|
||||||
text: "查看",
|
items: _getEnterpriseProperties(enterprise),
|
||||||
backgroundColor: Colors.blue,
|
actions: _listItemButtons(),
|
||||||
padding: EdgeInsets.symmetric(horizontal: 30),
|
type: widget.type,
|
||||||
onPressed: () {
|
selectTabIndex: _selectedTab,
|
||||||
pushPage(HelpRecordPage(), context);
|
),
|
||||||
},
|
|
||||||
),
|
|
||||||
CustomButton(
|
|
||||||
height: 40,
|
|
||||||
text: "监管帮扶",
|
|
||||||
backgroundColor: Colors.blue,
|
|
||||||
padding: EdgeInsets.symmetric(horizontal: 30),
|
|
||||||
onPressed: () {},
|
|
||||||
),
|
|
||||||
]
|
|
||||||
: [],
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
@ -156,6 +126,120 @@ class _HomeCategroyListPageState extends State<HomeCategroyListPage>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
List<Widget> _listItemButtons() {
|
||||||
|
List<Widget> 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<Map<String, String>> _getEnterpriseProperties(
|
List<Map<String, String>> _getEnterpriseProperties(
|
||||||
Map<String, dynamic> enterprise,
|
Map<String, dynamic> enterprise,
|
||||||
|
|
@ -280,7 +364,7 @@ class _HomeCategroyListPageState extends State<HomeCategroyListPage>
|
||||||
backgroundColor: Colors.transparent,
|
backgroundColor: Colors.transparent,
|
||||||
builder:
|
builder:
|
||||||
(ctx) => DepartmentPicker(
|
(ctx) => DepartmentPicker(
|
||||||
data: data,
|
data: mockCategories,
|
||||||
onSelected: (selectedId) {
|
onSelected: (selectedId) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_selectedCategoryId = selectedId;
|
_selectedCategoryId = selectedId;
|
||||||
|
|
@ -360,20 +444,20 @@ class _HomeCategroyListPageState extends State<HomeCategroyListPage>
|
||||||
isSelected
|
isSelected
|
||||||
? const Icon(
|
? const Icon(
|
||||||
Icons.check_circle,
|
Icons.check_circle,
|
||||||
size: 22,
|
size: 20,
|
||||||
color: Colors.blue,
|
color: Colors.blue,
|
||||||
)
|
)
|
||||||
: const Icon(
|
: const Icon(
|
||||||
Icons.circle_outlined,
|
Icons.circle_outlined,
|
||||||
size: 22,
|
size: 20,
|
||||||
color: Colors.grey,
|
color: Colors.grey,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
SizedBox(width: 10),
|
SizedBox(width: 5),
|
||||||
Text(
|
Text(
|
||||||
label,
|
label,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 15,
|
fontSize: 12,
|
||||||
color: isSelected ? Colors.blue : Colors.grey[700],
|
color: isSelected ? Colors.blue : Colors.grey[700],
|
||||||
fontWeight: isSelected ? FontWeight.bold : FontWeight.normal,
|
fontWeight: isSelected ? FontWeight.bold : FontWeight.normal,
|
||||||
),
|
),
|
||||||
|
|
@ -429,7 +513,7 @@ class _HomeCategroyListPageState extends State<HomeCategroyListPage>
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 10),
|
// const SizedBox(width: 10),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
height: searchBarHeight,
|
height: searchBarHeight,
|
||||||
child: TextButton(
|
child: TextButton(
|
||||||
|
|
@ -448,14 +532,14 @@ class _HomeCategroyListPageState extends State<HomeCategroyListPage>
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
style: TextButton.styleFrom(
|
style: TextButton.styleFrom(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
padding: const EdgeInsets.symmetric(horizontal: 10),
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.circular(borderRadius),
|
borderRadius: BorderRadius.circular(borderRadius),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: const Text(
|
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(
|
Container(
|
||||||
padding: const EdgeInsets.all(15),
|
padding: const EdgeInsets.all(10),
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
child: Column(
|
child: Column(
|
||||||
spacing: 15,
|
spacing: 10,
|
||||||
children: [
|
children: [
|
||||||
_searchBar(),
|
_searchBar(),
|
||||||
if (widget.type == HomeCategroyType.enterpriseInfo ||
|
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()),
|
Expanded(child: _listViewWidget()),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ enum HomeCategroyType {
|
||||||
supervisionSupport("监管帮扶", ["监管帮扶管理", "监管帮扶隐患管理"]), // 监管帮扶
|
supervisionSupport("监管帮扶", ["监管帮扶管理", "监管帮扶隐患管理"]), // 监管帮扶
|
||||||
specialInspection("专项检查", ["专项检查管理", "专项检查隐患管理"]), // 专项检查
|
specialInspection("专项检查", ["专项检查管理", "专项检查隐患管理"]), // 专项检查
|
||||||
disasterReduction("防灾减灾", []), // 防灾减灾;
|
disasterReduction("防灾减灾", []), // 防灾减灾;
|
||||||
supervisionRecord("帮扶记录", []); // 防灾减灾;
|
supervisionRecord("帮扶记录", []); // 帮扶记录;
|
||||||
|
|
||||||
final String title;
|
final String title;
|
||||||
final List<String> tabLists;
|
final List<String> tabLists;
|
||||||
|
|
|
||||||
|
|
@ -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()),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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')],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
@ -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(),
|
||||||
|
};
|
||||||
|
}
|
||||||
24
pubspec.lock
24
pubspec.lock
|
|
@ -105,6 +105,14 @@ packages:
|
||||||
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.1"
|
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:
|
extended_image:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -456,6 +464,22 @@ packages:
|
||||||
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.9.1"
|
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:
|
path_provider:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
||||||
|
|
@ -43,11 +43,10 @@ dependencies:
|
||||||
wechat_assets_picker: ^9.5.1
|
wechat_assets_picker: ^9.5.1
|
||||||
# 日历
|
# 日历
|
||||||
table_calendar: ^3.2.0
|
table_calendar: ^3.2.0
|
||||||
|
#日期格式化
|
||||||
intl: ^0.20.0
|
intl: ^0.20.0
|
||||||
#图片查看大图
|
#图片查看大图
|
||||||
photo_view: ^0.15.0
|
photo_view: ^0.15.0
|
||||||
|
|
||||||
|
|
||||||
# 状态管理
|
# 状态管理
|
||||||
provider: ^6.1.2
|
provider: ^6.1.2
|
||||||
# 屏幕适配相关
|
# 屏幕适配相关
|
||||||
|
|
@ -67,9 +66,8 @@ dependencies:
|
||||||
webview_flutter: ^4.2.2
|
webview_flutter: ^4.2.2
|
||||||
# 显示html文本
|
# 显示html文本
|
||||||
flutter_html: ^3.0.0-alpha.6
|
flutter_html: ^3.0.0-alpha.6
|
||||||
|
# 虚线框
|
||||||
|
dotted_border: ^2.1.0
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue