QinGang_interested/lib/pages/home/ai_page.dart

546 lines
16 KiB
Dart

import 'dart:convert';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:qhd_prevention/constants/app_enums.dart';
import 'package:qhd_prevention/customWidget/toast_util.dart';
import 'package:qhd_prevention/http/ApiService.dart';
import 'package:qhd_prevention/http/modules/file_api.dart';
import 'package:qhd_prevention/pages/my_appbar.dart';
// 单个隐患数据模型
class HiddenItem {
String? rectificationSuggestions;
String? hiddenDescr;
String? legalBasis;
bool isSelect=false;
HiddenItem({
this.rectificationSuggestions,
this.hiddenDescr,
this.legalBasis,
});
factory HiddenItem.fromJson(Map<String, dynamic> json) {
return HiddenItem(
rectificationSuggestions: json['rectificationSuggestions'] as String?,
hiddenDescr: json['hiddenDescr'] as String?,
legalBasis: json['legalBasis'] as String?,
);
}
Map<String, dynamic> toJson() {
return {
'rectificationSuggestions': rectificationSuggestions,
'hiddenDescr': hiddenDescr,
'legalBasis': legalBasis,
};
}
}
class AiPage extends StatefulWidget {
const AiPage(this.imagePath, {super.key});
final String imagePath;
@override
State<AiPage> createState() => _AiPageState();
}
class _AiPageState extends State<AiPage> with SingleTickerProviderStateMixin {
Image? _selectedImage;
bool _isScanning = false;
bool _showResult = false;
late AnimationController _animationController;
late Animation<double> _animation;
List<HiddenItem> hiddenItems = [];
// List<String> serialNumbers = [
// 'SN-001-2024',
// 'SN-002-2024',
// 'SN-003-2024',
// 'SN-004-2024',
// 'SN-005-2024',
// 'SN-006-2024',
// 'SN-007-2024',
// ];
// Map<String, bool> selectedItems = {};
@override
void initState() {
super.initState();
_animationController = AnimationController(
duration: Duration(seconds: 2), // 单次扫描时间改为2秒
vsync: this,
);
// 使用往复动画,让扫描线来回移动
_animation = Tween<double>(begin: 0.0, end: 1.0).animate(
CurvedAnimation(parent: _animationController, curve: Curves.easeInOut),
);
// // 初始化选择状态
// for (var serial in serialNumbers) {
// selectedItems[serial] = false;
// }
_pickImage();
_getAiRecognize();
}
/// 手动拍照并上传
Future<void> _getAiRecognize() async {
try {
final raw = await FileApi.uploadFile( widget.imagePath, UploadFileType.aiRecognitionImages,'');
if (raw['success'] ) {
// String imagePath= ApiService.baseImgPath+raw['data']['filePath'];
String imagePath= 'https://jpfz.qhdsafety.com/gbsFileTest/20251201171655.jpg';
final res = await HiddenDangerApi.aiRecognitionImages(imagePath);
if (res['success'] ) {
List<dynamic> data=res['data']['aiHiddens']??[];
if(data.isNotEmpty){
for(int i=0;i<data.length;i++){
if (data[i] is String) {
String item=data[i];
try {
// 尝试解析字符串格式的 JSON
final cleanedJson = item.replaceAll('\\"', '"').replaceAll('\\\\', '\\');
final itemJson = jsonDecode(cleanedJson) as Map<String, dynamic>;
hiddenItems.add(HiddenItem.fromJson(itemJson));
} catch (e) {
print('解析失败: $e, 原始数据: $item');
}
} else if (data[i] is Map<String, dynamic>) {
hiddenItems.add(HiddenItem.fromJson(data[i]));
}
}
_animationController.stop();
setState(() {
_isScanning = false;
_showResult = true;
});
}else {
ToastUtil.showError(context, '未获取到隐患,请重新拍照');
}
} else {
ToastUtil.showError(context, '识别失败,请重试');
}
}else{
// _showMessage('反馈提交失败');
ToastUtil.showError(context, '识别失败,请重试');
}
} catch (e, st) {
debugPrint('[FaceRecognition] manual capture error: $e\n$st');
ToastUtil.showError(context, '识别失败,请重试');
}
}
@override
void dispose() {
_animationController.dispose();
super.dispose();
}
Future<void> _pickImage() async {
// 这里使用 image_picker 包来选择图片
// final pickedFile = await ImagePicker().pickImage(source: ImageSource.gallery);
// 为了演示,我们使用一个网络图片
setState(() {
_selectedImage = Image.file(
File(widget.imagePath),//'https://picsum.photos/400/600',
fit: BoxFit.cover,
);
_isScanning = true;
});
// 重置动画到开始位置(顶部)
_animationController.value = 0.0;
// 开始往复扫描动画
_animationController.repeat(reverse: true);
// 5秒后结束扫描
// Future.delayed(Duration(seconds: 5), () {
// _animationController.stop();
// setState(() {
// _isScanning = false;
// _showResult = true;
// });
// });
}
Widget _buildImagePickerBox() {
return GestureDetector(
onTap: _pickImage,
child: Container(
width: 200,
height: 150,
decoration: BoxDecoration(
border: Border.all(color: Colors.grey, width: 2),
borderRadius: BorderRadius.circular(12),
),
child:
_selectedImage == null
? Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.photo_library, size: 40, color: Colors.grey),
SizedBox(height: 8),
Text('选择图片', style: TextStyle(color: Colors.grey)),
],
)
: ClipRRect(
borderRadius: BorderRadius.circular(10),
child: _selectedImage,
),
),
);
}
Widget _buildScanLine() {
return AnimatedBuilder(
animation: _animation,
builder: (context, child) {
// 计算扫描线位置,从顶部开始,确保不超出屏幕底部
double screenHeight = (MediaQuery.of(context).size.height)-100;
double scanLineHeight = 4.0; // 扫描线高度
// 计算扫描线顶部位置,确保扫描线底部不超出屏幕
double maxTopPosition = screenHeight - scanLineHeight;
double scanTopPosition = screenHeight * _animation.value;
// 限制扫描线位置在屏幕范围内
scanTopPosition = scanTopPosition.clamp(0.0, maxTopPosition);
return Positioned(
top: scanTopPosition,
child: Container(
width: MediaQuery.of(context).size.width,
height: 3,
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
Colors.transparent,
Colors.blue,
Colors.blue,
Colors.blue,
Colors.transparent,
],
stops: [0.0, 0.2, 0.5, 0.8, 1.0],
),
boxShadow: [
BoxShadow(
color: Colors.blue.withOpacity(0.5),
blurRadius: 8,
spreadRadius: 2,
),
],
),
),
);
},
);
}
Widget _buildScanOverlay() {
return AnimatedBuilder(
animation: _animation,
builder: (context, child) {
return Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Colors.black.withOpacity(0.7),
Colors.black.withOpacity(0.3),
Colors.transparent,
Colors.black.withOpacity(0.3),
Colors.black.withOpacity(0.7),
],
stops: [0.0, 0.2, 0.5, 0.8, 1.0],
transform: GradientRotation(_animation.value * 3.14159), // 旋转渐变方向
),
),
);
},
);
}
Widget _buildResultPanel() {
return Positioned(
top: 20,
left: 20,
child: AnimatedContainer(
duration: Duration(milliseconds: 500),
width: _showResult ? 80 : 200,
height: _showResult ? 60 : 150,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
border: Border.all(color: Colors.grey),
boxShadow: [
BoxShadow(
color: Colors.black26,
blurRadius: 8,
offset: Offset(2, 2),
),
],
),
child:
_selectedImage != null
? ClipRRect(
borderRadius: BorderRadius.circular(8),
child: _selectedImage,
)
: SizedBox(),
),
);
}
Widget _buildSerialNumberList() {
return AnimatedOpacity(
opacity: _showResult ? 1.0 : 0.0,
duration: Duration(milliseconds: 500),
child: Container(
margin: EdgeInsets.only(top: 40, left: 20, right: 20, bottom: 15),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'检测到的隐患:',
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
),
SizedBox(height: 10),
Expanded(
child: Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.grey),
borderRadius: BorderRadius.circular(8),
),
child: ListView.builder(
itemCount: hiddenItems.length,
itemBuilder: (context, index) {
final serial = hiddenItems[index].hiddenDescr;
return Container(
decoration: BoxDecoration(
border: Border(
bottom:
index < hiddenItems.length - 1
? BorderSide(color: Colors.grey.shade300)
: BorderSide.none,
),
),
child: CheckboxListTile(
title: Text(serial??'', style: TextStyle(fontSize: 14)),
value: hiddenItems[index].isSelect ,
onChanged: (bool? value) {
setState(() {
hiddenItems[index].isSelect=value ?? false;
// selectedItems[serial??''] = value ?? false;
});
},
controlAffinity: ListTileControlAffinity.leading,
dense: true,
),
);
},
),
),
),
SizedBox(height: 20),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Expanded(
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 10),
child: ElevatedButton(
onPressed: () {
// 取消操作
_resetPage();
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.grey.shade400,
foregroundColor: Colors.white,
minimumSize: Size(0, 50),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
child: Text('合并', style: TextStyle(fontSize: 16)),
),
),
),
Expanded(
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 10),
child: ElevatedButton(
onPressed: () {
// 确认操作
_showConfirmationDialog();
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue,
foregroundColor: Colors.white,
minimumSize: Size(0, 50),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
child: Text('处理', style: TextStyle(fontSize: 16)),
),
),
),
],
),
],
),
),
);
}
void _showConfirmationDialog() {
// final selectedSerials =
// selectedItems.entries
// .where((entry) => entry.value)
// .map((entry) => entry.key)
// .toList();
//
// showDialog(
// context: context,
// builder:
// (context) => AlertDialog(
// title: Text('确认选择'),
// content: Column(
// mainAxisSize: MainAxisSize.min,
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// Text('已选择 ${selectedSerials.length} 个序列号:'),
// SizedBox(height: 10),
// if (selectedSerials.isNotEmpty)
// ...selectedSerials
// .map((serial) => Text('• $serial'))
// .toList(),
// ],
// ),
// actions: [
// TextButton(
// onPressed: () => Navigator.of(context).pop(),
// child: Text('确定'),
// ),
// ],
// ),
// );
}
void _resetPage() {
// setState(() {
// _selectedImage = null;
// _isScanning = false;
// _showResult = false;
// _animationController.reset();
//
// // // 重置选择状态
// // for (var key in selectedItems.keys) {
// // selectedItems[key] = false;
// // }
// });
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: MyAppbar(title: 'Ai识别'),
body: Stack(
children: [
// 主内容
Column(
children: [
SizedBox(height: 50),
// if (!_showResult)
// Center(child: _buildImagePickerBox()),
if (_showResult) Expanded(child: _buildSerialNumberList()),
],
),
// 全屏扫描效果
if (_isScanning && _selectedImage != null)
Container(
color: Colors.black,
child: Stack(
children: [
// 全屏图片
SizedBox.expand(child: _selectedImage),
// 扫描遮罩效果
_buildScanOverlay(),
// 扫描线
_buildScanLine(),
// 扫描提示
Positioned(
top: 50,
left: 0,
right: 0,
child: Center(
child: Column(
children: [
Text(
'扫描中...',
style: TextStyle(
color: Colors.white,
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 10),
Container(
width: 30,
height: 30,
child: CircularProgressIndicator(
color: Colors.blue,
strokeWidth: 3,
),
),
],
),
),
),
],
),
),
// 结果面板
if (_showResult) _buildResultPanel(),
],
),
);
}
}