Compare commits

..

No commits in common. "b5c31a5afe2a1265deceef21c39437f560b0b560" and "755a15aa7b4a1c5d8d1df0ac688fb69da4956a8b" have entirely different histories.

14 changed files with 356 additions and 361 deletions

View File

@ -24,9 +24,9 @@ class ApiService {
/// ///
// static const String basePath = "https://qaaqwh.qhdsafety.com/integrated_whb"; // static const String basePath = "https://qaaqwh.qhdsafety.com/integrated_whb";
// static const String basePath = "http://192.168.20.240:8500/integrated_whb";// // static const String basePath = "http://192.168.20.240:8500/integrated_whb";//
static const String basePath = "http://192.168.20.240:8500/integrated_whb"; // static const String basePath = "http://192.168.20.240:8500/integrated_whb";
// static const String basePath = "http://192.168.0.25:28199";// // static const String basePath = "http://192.168.0.25:28199";//
// static const String basePath = "http://192.168.0.45:28199";// static const String basePath = "http://192.168.0.45:28199";//
/// ///
static const String baseImgPath = "https://file.zcloudchina.com/YTHFile"; static const String baseImgPath = "https://file.zcloudchina.com/YTHFile";

View File

@ -137,48 +137,70 @@ class _CheckRecordDetailPageState extends State<CheckRecordDetailPage> {
List<Map<String, dynamic>> buildCoversFromRes(Map res) { List<Map<String, dynamic>> buildCoversFromRes(Map res) {
final list = <Map<String, dynamic>>[]; final list = <Map<String, dynamic>>[];
const String defaultIconAsset = 'assets/map/50.png'; const String defaultIconAsset = 'assets/map/50.png';
final seen = <String>{}; // "lat|lng|type"
void tryAdd(double? lat, double? lng, String type, [dynamic data]) { final cinfo = res['cinfo'];
if (lat == null || lng == null) return; if (cinfo != null &&
final key = '${lat.toStringAsFixed(6)}|${lng.toStringAsFixed(6)}|$type'; cinfo['LONGITUDE'] != null &&
if (seen.contains(key)) return; cinfo['LATITUDE'] != null &&
seen.add(key); cinfo['LONGITUDE'].toString().isNotEmpty &&
cinfo['LATITUDE'].toString().isNotEmpty) {
final lat = double.tryParse(cinfo['LATITUDE'].toString());
final lng = double.tryParse(cinfo['LONGITUDE'].toString());
if (lat != null && lng != null) {
list.add({ list.add({
'latitude': lat, 'latitude': lat,
'longitude': lng, 'longitude': lng,
'icon': defaultIconAsset, 'icon': defaultIconAsset,
'data': {'type': type, 'item': data} 'data': {'type': 'cinfo'}
}); });
} }
final cinfo = res['cinfo'];
if (cinfo != null) {
final lat = double.tryParse(cinfo['LATITUDE']?.toString() ?? '');
final lng = double.tryParse(cinfo['LONGITUDE']?.toString() ?? '');
tryAdd(lat, lng, 'cinfo', cinfo);
} }
// final check = res['checkrecord']; final check = res['checkrecord'];
// if (check != null) { if (check != null &&
// final lat = double.tryParse(check['LATITUDE']?.toString() ?? ''); check['LONGITUDE'] != null &&
// final lng = double.tryParse(check['LONGITUDE']?.toString() ?? ''); check['LATITUDE'] != null &&
// tryAdd(lat, lng, 'checkrecord', check); check['LONGITUDE'].toString().isNotEmpty &&
// } check['LATITUDE'].toString().isNotEmpty) {
// final lat = double.tryParse(check['LATITUDE'].toString());
// final vlist = (res['varList'] as List?) ?? []; final lng = double.tryParse(check['LONGITUDE'].toString());
// for (final item in vlist) { if (lat != null && lng != null) {
// try { list.add({
// final lat = double.tryParse(item['LATITUDE']?.toString() ?? ''); 'latitude': lat,
// final lng = double.tryParse(item['LONGITUDE']?.toString() ?? ''); 'longitude': lng,
// tryAdd(lat, lng, 'varList', item); 'icon': defaultIconAsset,
// } catch (_) {} 'data': {'type': 'checkrecord'}
// } });
}
}
final vlist = (res['varList'] as List?) ?? [];
for (final item in vlist) {
try {
if (item != null &&
item['LONGITUDE'] != null &&
item['LATITUDE'] != null &&
item['LONGITUDE'].toString().isNotEmpty &&
item['LATITUDE'].toString().isNotEmpty) {
final lat = double.tryParse(item['LATITUDE'].toString());
final lng = double.tryParse(item['LONGITUDE'].toString());
if (lat != null && lng != null) {
list.add({
'latitude': lat,
'longitude': lng,
'icon': defaultIconAsset,
'data': {'type': 'varList', 'item': item}
});
}
}
} catch (_) {
//
}
}
return list; return list;
} }
// map // map
void _onBmfMapCreated(BMFMapController controller) async { void _onBmfMapCreated(BMFMapController controller) async {
_mapController = controller; _mapController = controller;
@ -233,18 +255,6 @@ class _CheckRecordDetailPageState extends State<CheckRecordDetailPage> {
} }
Future<void> _updateMarkers() async { Future<void> _updateMarkers() async {
bool cleared = false;
try {
final res = await _mapController!.cleanAllMarkers();
debugPrint('_updateMarkers: cleanAllMarkers returned: $res');
cleared = (res == true);
} catch (e) {
debugPrint('_updateMarkers: cleanAllMarkers exception: $e');
}
if (!cleared) {
debugPrint('_updateMarkers: warning - markers not cleared. proceeding but will guard duplicates.');
}
if (_mapController == null) { if (_mapController == null) {
debugPrint('_updateMarkers: mapController is null'); debugPrint('_updateMarkers: mapController is null');
return; return;

View File

@ -1,6 +1,5 @@
import 'dart:async';
import 'dart:io'; import 'dart:io';
import 'package:flutter/foundation.dart'; import 'dart:ui';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:qhd_prevention/customWidget/toast_util.dart'; import 'package:qhd_prevention/customWidget/toast_util.dart';
@ -50,7 +49,6 @@ class SignImageData {
@override @override
String toString() => 'SignImageData(key:$key, filePath:$filePath, SIGNER_TIME:$SIGNER_TIME)'; String toString() => 'SignImageData(key:$key, filePath:$filePath, SIGNER_TIME:$SIGNER_TIME)';
} }
class DangerousOptionsPage extends StatefulWidget { class DangerousOptionsPage extends StatefulWidget {
final int index; final int index;
final int status; final int status;
@ -88,21 +86,17 @@ class _DangerousOptionsPageState extends State<DangerousOptionsPage> {
status = widget.status; status = widget.status;
measures = widget.measures; measures = widget.measures;
imgList = List<ImageData>.from(widget.imgList); imgList = List<ImageData>.from(widget.imgList);
signImgList = widget.signImgList.map((map) => SignImageData.fromJson(map)).toList(); signImgList =
widget.signImgList.map((map) => SignImageData.fromJson(map)).toList();
} }
/// ///
Future<void> _onImageAdded(String localPath) async { Future<void> _onImageAdded(String localPath) async {
if (!mounted) return; //
LoadingDialogHelper.show(); LoadingDialogHelper.show();
final res = await ApiService.uploadSaveFile(localPath);
try {
//
final res = await ApiService.uploadSaveFile(localPath).timeout(const Duration(seconds: 30));
LoadingDialogHelper.hide(); LoadingDialogHelper.hide();
try {
if (!mounted) return;
if (res['result'] == 'success') { if (res['result'] == 'success') {
final url = res['FILE_PATH'] as String; final url = res['FILE_PATH'] as String;
setState(() { setState(() {
@ -110,41 +104,25 @@ class _DangerousOptionsPageState extends State<DangerousOptionsPage> {
}); });
} else { } else {
ToastUtil.showError(context, '上传失败,资源过大请重新选择'); ToastUtil.showError(context, '上传失败,资源过大请重新选择');
//
setState(() { setState(() {
imgList = []; imgList = [];
});
MediaBus().emit(MediaEvent.clear(kAcceptVideoSectionKey)); MediaBus().emit(MediaEvent.clear(kAcceptVideoSectionKey));
});
} }
} on TimeoutException { } catch (_) {
LoadingDialogHelper.hide();
if (!mounted) return;
ToastUtil.showError(context, '上传超时,请检查网络后重试');
} catch (e, st) {
LoadingDialogHelper.hide();
debugPrint('_onImageAdded error: $e\n$st');
if (!mounted) return;
ToastUtil.showError(context, '上传失败,资源过大请重新选择'); ToastUtil.showError(context, '上传失败,资源过大请重新选择');
setState(() { setState(() {
imgList = []; imgList = [];
});
MediaBus().emit(MediaEvent.clear(kAcceptVideoSectionKey)); MediaBus().emit(MediaEvent.clear(kAcceptVideoSectionKey));
});
} }
} }
/// ///
Future<void> _onImageRemoved(ImageData item) async { Future<void> _onImageRemoved(ImageData item) async {
try { if (item.serverPath != null) {
if (item.serverPath != null && item.serverPath.isNotEmpty) { await ApiService.deleteSaveFile(item.serverPath!);
//
await ApiService.deleteSaveFile(item.serverPath!).timeout(const Duration(seconds: 15));
} }
} catch (e, st) {
debugPrint('_onImageRemoved: delete api error: $e\n$st');
// 使 UI
}
if (!mounted) return;
setState(() { setState(() {
imgList.remove(item); imgList.remove(item);
}); });
@ -156,19 +134,15 @@ class _DangerousOptionsPageState extends State<DangerousOptionsPage> {
return; return;
} }
LoadingDialogHelper.show(); LoadingDialogHelper.show();
try { List<String> filePaths =
// signImgList.map((img) => img.filePath ?? '').toList();
List<String> filePaths = signImgList.map((img) => img.filePath ?? '').toList(); final result = await ApiService.saveDangerousOptionsFile(filePaths);
final List<dynamic> signList = result['FILE_PATH_LIST'];
// API
final result = await ApiService.saveDangerousOptionsFile(filePaths).timeout(const Duration(seconds: 30));
final List<dynamic> signList = result['FILE_PATH_LIST'] ?? [];
List<Map<String, dynamic>> sineImageList = []; List<Map<String, dynamic>> sineImageList = [];
for (SignImageData data in signImgList) { for (SignImageData data in signImgList) {
for (Map<String, dynamic> img in signList) { for (Map<String, dynamic> img in signList) {
String imgName = 'file${data.key}'; String imgName = 'file${data.key}';
if (data.filePath != null && data.filePath!.contains('uploadFiles')) { if (data.filePath!.contains('uploadFiles')) {
final idata = { final idata = {
'filePath': data.filePath, 'filePath': data.filePath,
'SIGNER_TIME': data.SIGNER_TIME, 'SIGNER_TIME': data.SIGNER_TIME,
@ -176,7 +150,7 @@ class _DangerousOptionsPageState extends State<DangerousOptionsPage> {
}; };
sineImageList.add(idata); sineImageList.add(idata);
} }
if (imgName == (img['key'] ?? '')) { if (imgName == img['key']) {
final idata = { final idata = {
'filePath': img['filePath'] ?? '', 'filePath': img['filePath'] ?? '',
'SIGNER_TIME': data.SIGNER_TIME, 'SIGNER_TIME': data.SIGNER_TIME,
@ -186,40 +160,27 @@ class _DangerousOptionsPageState extends State<DangerousOptionsPage> {
} }
} }
} }
setState(() => buttonLoading = true);
if (!mounted) return;
setState(() => buttonLoading = true); //
LoadingDialogHelper.hide(); LoadingDialogHelper.hide();
if (!mounted) return;
Navigator.pop(context, { Navigator.pop(context, {
'imgList': imgList.map((e) => {'local': e.localPath, 'remote': e.serverPath}).toList(), 'imgList':
imgList
.map((e) => {'local': e.localPath, 'remote': e.serverPath})
.toList(),
'signImgList': sineImageList, 'signImgList': sineImageList,
'index': index, 'index': index,
'status': status, 'status': status,
}); });
} on TimeoutException {
LoadingDialogHelper.hide();
if (!mounted) return;
ToastUtil.showError(context, '保存超时,请稍后重试');
} catch (e, st) {
LoadingDialogHelper.hide();
debugPrint('_submit error: $e\n$st');
if (!mounted) return;
ToastUtil.showError(context, '提交失败,请重试');
}
} }
Future<void> _sign() async { Future<void> _sign() async {
await NativeOrientation.setLandscape(); await NativeOrientation.setLandscape();
final String? path = await Navigator.push<String>( final String path = await Navigator.push(
context, context,
MaterialPageRoute(builder: (c) => MineSignPage()), MaterialPageRoute(builder: (c) => MineSignPage()),
); );
await NativeOrientation.setPortrait(); await NativeOrientation.setPortrait();
if (path != null) {
if (!mounted) return;
if (path != null && path.isNotEmpty) {
final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now());
setState(() { setState(() {
final imageData = SignImageData( final imageData = SignImageData(
@ -230,84 +191,15 @@ class _DangerousOptionsPageState extends State<DangerousOptionsPage> {
signImgList.add(imageData); signImgList.add(imageData);
signTimes.add(now); signTimes.add(now);
}); });
//FocusHelper.clearFocus(context);
} }
} }
Widget _signListWidget() { Widget _signListWidget() {
// 使 ListView Column 线使 ResizeImage
return Column( return Column(
children: signImgList.map((imgData) { children:
signImgList.map((imgData) {
final idx = signImgList.indexOf(imgData); final idx = signImgList.indexOf(imgData);
final rawPath = (imgData.filePath ?? '').toString();
final isNetwork = rawPath.startsWith('http://') || rawPath.startsWith('https://');
// UI
const targetWidth = 460; // * devicePixelRatio
const targetHeight = 300;
Widget imageWidget;
if (rawPath.isEmpty) {
imageWidget = Container(
width: 230,
height: 150,
color: Colors.grey.shade200,
child: const Center(child: Icon(Icons.broken_image, size: 28, color: Colors.grey)),
);
} else if (isNetwork) {
// width/height loadingBuilder
imageWidget = Image.network(
rawPath.startsWith('http') ? rawPath : ApiService.baseImgPath + rawPath,
width: 230,
height: 150,
fit: BoxFit.cover,
// loading
loadingBuilder: (context, child, loadingProgress) {
if (loadingProgress == null) return child;
return Container(
width: 230,
height: 150,
color: Colors.grey.shade200,
child: const Center(child: CircularProgressIndicator(strokeWidth: 2)),
);
},
errorBuilder: (_, __, ___) => Container(
width: 230,
height: 150,
color: Colors.grey.shade200,
child: const Center(child: Icon(Icons.broken_image)),
),
);
} else {
// 使 ResizeImage FileImage线
final file = File(rawPath);
if (file.existsSync()) {
imageWidget = Image(
image: ResizeImage(
FileImage(file),
// pixel ratio
width: targetWidth,
height: targetHeight,
),
width: 230,
height: 150,
fit: BoxFit.contain,
errorBuilder: (_, __, ___) => Container(
width: 230,
height: 150,
color: Colors.grey.shade200,
child: const Center(child: Icon(Icons.broken_image)),
),
);
} else {
imageWidget = Container(
width: 230,
height: 150,
color: Colors.grey.shade200,
child: const Center(child: Icon(Icons.broken_image)),
);
}
}
return Column( return Column(
children: [ children: [
const SizedBox(height: 10), const SizedBox(height: 10),
@ -321,9 +213,21 @@ class _DangerousOptionsPageState extends State<DangerousOptionsPage> {
maxWidth: 230, maxWidth: 230,
maxHeight: 150, maxHeight: 150,
), ),
child: imageWidget, child:
(imgData.filePath ?? '').contains('uploadFiles')
? Image.network(
'${ApiService.baseImgPath}${imgData.filePath}',
)
: Image.file(
File(imgData.filePath ?? ''),
fit: BoxFit.contain,
),
),
onTap:
() => presentOpaque(
SingleImageViewer(imageUrl: imgData.filePath ?? ''),
context,
), ),
onTap: () => presentOpaque(SingleImageViewer(imageUrl: rawPath), context),
), ),
Column( Column(
children: [ children: [
@ -333,7 +237,6 @@ class _DangerousOptionsPageState extends State<DangerousOptionsPage> {
padding: const EdgeInsets.symmetric(horizontal: 10), padding: const EdgeInsets.symmetric(horizontal: 10),
backgroundColor: Colors.red, backgroundColor: Colors.red,
onPressed: () { onPressed: () {
if (!mounted) return;
setState(() => signImgList.removeAt(idx)); setState(() => signImgList.removeAt(idx));
}, },
), ),
@ -355,19 +258,19 @@ class _DangerousOptionsPageState extends State<DangerousOptionsPage> {
body: Padding( body: Padding(
padding: const EdgeInsets.all(12.0), padding: const EdgeInsets.all(12.0),
child: Container( child: Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10), padding: EdgeInsets.symmetric(horizontal: 10, vertical: 10),
color: Colors.white, color: Colors.white,
child: ListView( child: ListView(
children: [ children: [
Table( Table(
border: TableBorder.all(color: Colors.grey.shade300), border: TableBorder.all(color: Colors.grey.shade300),
columnWidths: const {0: FlexColumnWidth(3), 1: FlexColumnWidth(2)}, columnWidths: {0: FlexColumnWidth(3), 1: FlexColumnWidth(2)},
children: [ children: [
TableRow( TableRow(
decoration: BoxDecoration(color: Colors.grey.shade200), decoration: BoxDecoration(color: Colors.grey.shade200),
children: [ children: [
Padding( Padding(
padding: const EdgeInsets.all(10), padding: EdgeInsets.all(10),
child: Center( child: Center(
child: Text( child: Text(
'主要安全措施', '主要安全措施',
@ -376,7 +279,7 @@ class _DangerousOptionsPageState extends State<DangerousOptionsPage> {
), ),
), ),
Padding( Padding(
padding: const EdgeInsets.all(10), padding: EdgeInsets.all(10),
child: Center( child: Center(
child: Text( child: Text(
'操作', '操作',
@ -402,12 +305,12 @@ class _DangerousOptionsPageState extends State<DangerousOptionsPage> {
RadioListTile<int>( RadioListTile<int>(
value: -1, value: -1,
groupValue: status, groupValue: status,
title: const Text('不涉及'), title: Text('不涉及'),
contentPadding: const EdgeInsets.symmetric( contentPadding: const EdgeInsets.symmetric(
vertical: 0, vertical: 0,
horizontal: 8.0, horizontal: 8.0,
), ),
visualDensity: const VisualDensity( visualDensity: VisualDensity(
vertical: -4, vertical: -4,
horizontal: 0, horizontal: 0,
), ),
@ -416,12 +319,12 @@ class _DangerousOptionsPageState extends State<DangerousOptionsPage> {
RadioListTile<int>( RadioListTile<int>(
value: 1, value: 1,
groupValue: status, groupValue: status,
title: const Text('涉及'), title: Text('涉及'),
contentPadding: const EdgeInsets.symmetric( contentPadding: const EdgeInsets.symmetric(
vertical: 4.0, vertical: 4.0,
horizontal: 8.0, horizontal: 8.0,
), ),
visualDensity: const VisualDensity( visualDensity: VisualDensity(
vertical: -4, vertical: -4,
horizontal: 0, horizontal: 0,
), ),
@ -440,17 +343,15 @@ class _DangerousOptionsPageState extends State<DangerousOptionsPage> {
maxCount: 2, maxCount: 2,
mediaType: MediaType.image, mediaType: MediaType.image,
initialMediaPaths: initialMediaPaths:
imgList.map((e) => '${ApiService.baseImgPath}${e.serverPath}').toList(), imgList
.map((e) => '${ApiService.baseImgPath}${e.serverPath}')
.toList(),
onChanged: (paths) {}, onChanged: (paths) {},
onMediaAdded: _onImageAdded, onMediaAdded: _onImageAdded,
onMediaRemoved: (path) { onMediaRemoved: (path) {
// localPath print(path);
try {
final item = imgList.firstWhere((e) => path.contains(e.localPath) ); final item = imgList.firstWhere((e) => path.contains(e.localPath) );
_onImageRemoved(item); _onImageRemoved(item);
} catch (e) {
debugPrint('onMediaRemoved: find item error: $e');
}
}, },
onAiIdentify: () {}, onAiIdentify: () {},
), ),
@ -458,7 +359,7 @@ class _DangerousOptionsPageState extends State<DangerousOptionsPage> {
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
const Text('签字:', style: TextStyle(fontSize: 16)), Text('签字:', style: TextStyle(fontSize: 16)),
CustomButton( CustomButton(
text: '新增手写签字', text: '新增手写签字',
height: 36, height: 36,

View File

@ -114,7 +114,7 @@ class _HotworkApplyDetailState extends State<HotworkApplyDetail> {
_getHotWorkNameList(); _getHotWorkNameList();
_getVideoList(); _getVideoList();
_getUnitListAll(); _getUnitListAll();
_getPlsList();
_contentController.addListener(() { _contentController.addListener(() {
setState(() { setState(() {
@ -333,6 +333,15 @@ class _HotworkApplyDetailState extends State<HotworkApplyDetail> {
}); });
} }
///
Future<void> _getPlsList() async {
final result = await ApiService.getWorkAreaList();
setState(() {
final String zTreeNodes = result['zTreeNodes'] ?? '';
workAreaList = jsonDecode(zTreeNodes);
});
}
/// ------------------------------------------------------------ /// ------------------------------------------------------------
Widget _card(Widget child) { Widget _card(Widget child) {

View File

@ -103,7 +103,7 @@ class _CutroadApplyDetailState extends State<CutroadApplyDetail> {
_getVideoList(); _getVideoList();
_getUnitListAll(); _getUnitListAll();
_getPlsList();
_contentController.addListener(() { _contentController.addListener(() {
setState(() { setState(() {
pd['WORK_REASON'] = _contentController.text.trim(); pd['WORK_REASON'] = _contentController.text.trim();
@ -504,7 +504,14 @@ class _CutroadApplyDetailState extends State<CutroadApplyDetail> {
}); });
} }
///
Future<void> _getPlsList() async {
final result = await ApiService.getWorkAreaList();
setState(() {
final String zTreeNodes = result['zTreeNodes'] ?? '';
workAreaList = jsonDecode(zTreeNodes);
});
}
/// ------------------------------------------------------------ /// ------------------------------------------------------------

View File

@ -109,7 +109,7 @@ class _BreakgroundApplyDetailState extends State<BreakgroundApplyDetail> {
} }
_getVideoList(); _getVideoList();
_getUnitListAll(); _getUnitListAll();
_getPlsList();
_contentController.addListener(() { _contentController.addListener(() {
setState(() { setState(() {
pd['JOB_CONTENT'] = _contentController.text.trim(); pd['JOB_CONTENT'] = _contentController.text.trim();
@ -275,7 +275,14 @@ class _BreakgroundApplyDetailState extends State<BreakgroundApplyDetail> {
}); });
} }
///
Future<void> _getPlsList() async {
final result = await ApiService.getWorkAreaList();
setState(() {
final String zTreeNodes = result['zTreeNodes'] ?? '';
workAreaList = jsonDecode(zTreeNodes);
});
}
/// ------------------------------------------------------------ /// ------------------------------------------------------------
Future<void> _chooseLevel() async { Future<void> _chooseLevel() async {

View File

@ -105,7 +105,7 @@ class _HoistworkApplyDetailState extends State<HoistworkApplyDetail> {
} }
_getVideoList(); _getVideoList();
_getUnitListAll(); _getUnitListAll();
_getPlsList();
_contentController.addListener(() { _contentController.addListener(() {
setState(() { setState(() {
pd['WORK_CONTENT'] = _contentController.text.trim(); pd['WORK_CONTENT'] = _contentController.text.trim();
@ -248,7 +248,14 @@ class _HoistworkApplyDetailState extends State<HoistworkApplyDetail> {
}); });
} }
///
Future<void> _getPlsList() async {
final result = await ApiService.getWorkAreaList();
setState(() {
final String zTreeNodes = result['zTreeNodes'] ?? '';
workAreaList = jsonDecode(zTreeNodes);
});
}
/// ------------------------------------------------------------ /// ------------------------------------------------------------
void set_pd_DEPARTMENT_ID(EditUserType type, String id) { void set_pd_DEPARTMENT_ID(EditUserType type, String id) {

View File

@ -104,7 +104,7 @@ class _HighworkApplyDetailState extends State<HighworkApplyDetail> {
_getVideoList(); _getVideoList();
_getUnitListAll(); _getUnitListAll();
_getPlsList();
_contentController.addListener(() { _contentController.addListener(() {
setState(() { setState(() {
pd['WORK_CONTENT'] = _contentController.text.trim(); pd['WORK_CONTENT'] = _contentController.text.trim();
@ -245,7 +245,14 @@ class _HighworkApplyDetailState extends State<HighworkApplyDetail> {
}); });
} }
///
Future<void> _getPlsList() async {
final result = await ApiService.getWorkAreaList();
setState(() {
final String zTreeNodes = result['zTreeNodes'] ?? '';
workAreaList = jsonDecode(zTreeNodes);
});
}
/// ------------------------------------------------------------ /// ------------------------------------------------------------
void set_pd_DEPARTMENT_ID(EditUserType type, String id) { void set_pd_DEPARTMENT_ID(EditUserType type, String id) {

View File

@ -111,7 +111,7 @@ class _ElectricityApplyDetailState extends State<ElectricityApplyDetail> {
} }
_getVideoList(); _getVideoList();
_getUnitListAll(); _getUnitListAll();
_getPlsList();
_contentController.addListener(() { _contentController.addListener(() {
pd['WORK_CONTENT'] = _contentController.text.trim(); pd['WORK_CONTENT'] = _contentController.text.trim();
@ -244,7 +244,14 @@ class _ElectricityApplyDetailState extends State<ElectricityApplyDetail> {
unitAllList = result['varList'] ?? []; unitAllList = result['varList'] ?? [];
}); });
} }
///
Future<void> _getPlsList() async {
final result = await ApiService.getWorkAreaList();
setState(() {
final String zTreeNodes = result['zTreeNodes'] ?? '';
workAreaList = jsonDecode(zTreeNodes);
});
}
/// ------------------------------------------------------------ /// ------------------------------------------------------------
void set_pd_DEPARTMENT_ID(EditUserType type, String id) { void set_pd_DEPARTMENT_ID(EditUserType type, String id) {
pd['${type.name}_DEPARTMENT_ID'] = id; pd['${type.name}_DEPARTMENT_ID'] = id;

View File

@ -180,7 +180,7 @@ class _BlindboardCjryDetailState extends State<BlindboardCjryDetail> {
// formData['WORK_CONTENT'] = _contentController.text.trim(); // formData['WORK_CONTENT'] = _contentController.text.trim();
formData['CONIMG_PATH'] = serverPathString; formData['CONIMG_PATH'] = serverPathString;
formData['DESCR'] = ''; formData['DESCR'] = FormUtils.hasValue(pd, 'DESCR') ? pd['DESCR'] : '';
formData['BLINDBOARD_ID'] = pd['BLINDBOARD_ID'] ?? widget.BLINDBOARD_ID; formData['BLINDBOARD_ID'] = pd['BLINDBOARD_ID'] ?? widget.BLINDBOARD_ID;
formData['SIGNTIME'] = signTimes.join(','); formData['SIGNTIME'] = signTimes.join(',');
formData['USER_ID'] = SessionService.instance.loginUserId; formData['USER_ID'] = SessionService.instance.loginUserId;

View File

@ -110,7 +110,7 @@ class _BlindboardApplyDetailState extends State<BlindboardApplyDetail> {
_getVideoList(); _getVideoList();
_getUnitListAll(); _getUnitListAll();
_getPlsList();
_nameController.addListener(() { _nameController.addListener(() {
setState(() { setState(() {
pd['NAME'] = _nameController.text.trim(); pd['NAME'] = _nameController.text.trim();
@ -234,7 +234,14 @@ class _BlindboardApplyDetailState extends State<BlindboardApplyDetail> {
unitAllList = result['varList'] ?? []; unitAllList = result['varList'] ?? [];
}); });
} }
///
Future<void> _getPlsList() async {
final result = await ApiService.getWorkAreaList();
setState(() {
final String zTreeNodes = result['zTreeNodes'] ?? '';
workAreaList = jsonDecode(zTreeNodes);
});
}
/// ------------------------------------------------------------ /// ------------------------------------------------------------
void set_pd_DEPARTMENT_ID(EditUserType type, String id) { void set_pd_DEPARTMENT_ID(EditUserType type, String id) {
pd['${type.name}_DEPARTMENT_ID'] = id; pd['${type.name}_DEPARTMENT_ID'] = id;

View File

@ -112,7 +112,7 @@ class _SpaceworkApplyDetailState extends State<SpaceworkApplyDetail> {
_getSpaceWorkNameList(); _getSpaceWorkNameList();
_getVideoList(); _getVideoList();
_getUnitListAll(); _getUnitListAll();
_getPlsList();
_contentController.addListener(() { _contentController.addListener(() {
setState(() { setState(() {
pd['WORK_CONTENT'] = _contentController.text.trim(); pd['WORK_CONTENT'] = _contentController.text.trim();
@ -245,7 +245,14 @@ class _SpaceworkApplyDetailState extends State<SpaceworkApplyDetail> {
}); });
} }
///
Future<void> _getPlsList() async {
final result = await ApiService.getWorkAreaList();
setState(() {
final String zTreeNodes = result['zTreeNodes'] ?? '';
workAreaList = jsonDecode(zTreeNodes);
});
}
/// ------------------------------------------------------------ /// ------------------------------------------------------------
void set_pd_DEPARTMENT_ID(EditUserType type, String id) { void set_pd_DEPARTMENT_ID(EditUserType type, String id) {

View File

@ -24,18 +24,20 @@ class Category {
final name = (json['name'] ?? '').toString(); final name = (json['name'] ?? '').toString();
final positions = (json['POSITIONS'] ?? '').toString(); final positions = (json['POSITIONS'] ?? '').toString();
// children 使 // children null List
final rawChildren = json['children']; final rawChildren = json['children'];
List<Category> childrenList = []; List<Category> childrenList = [];
if (rawChildren is List) { if (rawChildren is List) {
childrenList = rawChildren childrenList = rawChildren
.where((e) => e != null) .where((e) => e != null)
.map((e) { .map((e) {
// e Map<String,dynamic> dynamic
if (e is Map<String, dynamic>) { if (e is Map<String, dynamic>) {
return Category.fromJson(e); return Category.fromJson(e);
} else if (e is Map) { } else if (e is Map) {
return Category.fromJson(Map<String, dynamic>.from(e)); return Category.fromJson(Map<String, dynamic>.from(e));
} else { } else {
//
return null; return null;
} }
}) })
@ -52,7 +54,9 @@ class Category {
} }
} }
typedef DeptSelectCallback = void Function(String id, String POSITIONS, String name);
typedef DeptSelectCallback =
void Function(String id, String POSITIONS, String name);
class WorkAreaPicker extends StatefulWidget { class WorkAreaPicker extends StatefulWidget {
final DeptSelectCallback onSelected; final DeptSelectCallback onSelected;
@ -68,6 +72,8 @@ class _WorkAreaPickerState extends State<WorkAreaPicker> {
String selectedName = ''; String selectedName = '';
String selected_POSITIONS = ''; String selected_POSITIONS = '';
Set<String> expandedSet = {};
List<Category> original = []; List<Category> original = [];
List<Category> filtered = []; List<Category> filtered = [];
bool loading = true; bool loading = true;
@ -77,9 +83,11 @@ class _WorkAreaPickerState extends State<WorkAreaPicker> {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
//
selectedId = ''; selectedId = '';
selectedName = ''; selectedName = '';
selected_POSITIONS = ''; selected_POSITIONS = '';
expandedSet = {};
_searchController.addListener(_onSearchChanged); _searchController.addListener(_onSearchChanged);
_loadData(); _loadData();
} }
@ -94,10 +102,23 @@ class _WorkAreaPickerState extends State<WorkAreaPicker> {
Future<void> _loadData() async { Future<void> _loadData() async {
try { try {
final result = await ApiService.getWorkAreaList(); final result = await ApiService.getWorkAreaList();
List<dynamic> raw = result['varList'] ?? []; final dynamic nodesField = result['zTreeNodes'];
// Category children // nodesField List StringJSON
final parsed = raw List<dynamic> raw;
if (nodesField is String) {
raw = json.decode(nodesField) as List<dynamic>;
} else if (nodesField is List) {
raw = nodesField;
} else {
raw = [];
}
// debug
// print('raw length = ${raw.length}');
setState(() {
original = raw
.map((e) { .map((e) {
if (e is Map<String, dynamic>) return Category.fromJson(e); if (e is Map<String, dynamic>) return Category.fromJson(e);
if (e is Map) return Category.fromJson(Map<String, dynamic>.from(e)); if (e is Map) return Category.fromJson(Map<String, dynamic>.from(e));
@ -105,49 +126,12 @@ class _WorkAreaPickerState extends State<WorkAreaPicker> {
}) })
.whereType<Category>() .whereType<Category>()
.toList(); .toList();
//
final List<Category> flat = [];
for (final c in parsed) {
flat.add(c);
if (c.children.isNotEmpty) {
flat.addAll(c.children);
}
}
// id ID id
// selectedId = ''
final List<Category> normalized = [];
for (var i = 0; i < flat.length; i++) {
final c = flat[i];
final rawId = (c.ELECTRONIC_FENCE_AREA_ID ?? '').toString().trim();
if (rawId.isEmpty) {
// id name hash
final generatedId = '__generated_${i}_${c.name.hashCode}';
normalized.add(Category(
ELECTRONIC_FENCE_AREA_ID: generatedId,
name: c.name,
POSITIONS: c.POSITIONS,
children: const [],
));
} else {
// id
normalized.add(Category(
ELECTRONIC_FENCE_AREA_ID: rawId,
name: c.name,
POSITIONS: c.POSITIONS,
children: const [],
));
}
}
setState(() {
original = normalized;
filtered = original; filtered = original;
loading = false; loading = false;
}); });
} catch (e, st) { } catch (e, st) {
debugPrint('WorkAreaPicker._loadData error: $e\n$st'); // 便
// print('loadData error: $e\n$st');
setState(() => loading = false); setState(() => loading = false);
} }
} }
@ -159,22 +143,37 @@ class _WorkAreaPickerState extends State<WorkAreaPicker> {
}); });
} }
// name POSITIONS
List<Category> _filterCategories(List<Category> list, String query) { List<Category> _filterCategories(List<Category> list, String query) {
if (query.isEmpty) return list; List<Category> result = [];
final q = query.toLowerCase(); for (var cat in list) {
return list.where((c) { final children = _filterCategories(cat.children, query);
final name = (c.name ?? '').toLowerCase(); if (cat.name.toLowerCase().contains(query) || children.isNotEmpty) {
final pos = (c.POSITIONS ?? '').toLowerCase(); result.add(
return name.contains(q) || pos.contains(q); Category(
}).toList(); ELECTRONIC_FENCE_AREA_ID: cat.ELECTRONIC_FENCE_AREA_ID,
name: cat.name,
children: children,
),
);
}
}
return result;
} }
Widget _buildRow(Category cat) { Widget _buildRow(Category cat, int indent) {
final isSelected = selectedId == cat.ELECTRONIC_FENCE_AREA_ID; final hasChildren = cat.children.isNotEmpty;
return InkWell( final isExpanded = expandedSet.contains(cat.ELECTRONIC_FENCE_AREA_ID);
final isSelected = cat.ELECTRONIC_FENCE_AREA_ID == selectedId;
return Column(
children: [
InkWell(
onTap: () { onTap: () {
setState(() { setState(() {
if (hasChildren) {
isExpanded
? expandedSet.remove(cat.ELECTRONIC_FENCE_AREA_ID)
: expandedSet.add(cat.ELECTRONIC_FENCE_AREA_ID);
}
selectedId = cat.ELECTRONIC_FENCE_AREA_ID; selectedId = cat.ELECTRONIC_FENCE_AREA_ID;
selectedName = cat.name; selectedName = cat.name;
selected_POSITIONS = cat.POSITIONS; selected_POSITIONS = cat.POSITIONS;
@ -182,17 +181,45 @@ class _WorkAreaPickerState extends State<WorkAreaPicker> {
}, },
child: Container( child: Container(
color: Colors.white, color: Colors.white,
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 12),
child: Row( child: Row(
children: [ children: [
Expanded(child: Text(cat.name)), SizedBox(width: 16.0 * indent),
Icon( SizedBox(
isSelected ? Icons.radio_button_checked : Icons.radio_button_unchecked, width: 24,
color: isSelected ? Colors.green : Colors.grey, child:
hasChildren
? Icon(
isExpanded
? Icons.arrow_drop_down_rounded
: Icons.arrow_right_rounded,
size: 35,
color: Colors.grey[600],
)
: const SizedBox.shrink(),
),
const SizedBox(width: 5),
Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 12),
child: Text(cat.name),
),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Icon(
isSelected
? Icons.radio_button_checked
: Icons.radio_button_unchecked,
color: Colors.green,
),
), ),
], ],
), ),
), ),
),
if (hasChildren && isExpanded)
...cat.children.map((c) => _buildRow(c, indent + 1)),
],
); );
} }
@ -240,19 +267,18 @@ class _WorkAreaPickerState extends State<WorkAreaPicker> {
], ],
), ),
), ),
const Divider(height: 1), Divider(),
Expanded( Expanded(
child: loading child:
loading
? const Center(child: CircularProgressIndicator()) ? const Center(child: CircularProgressIndicator())
: (filtered.isEmpty
? const Center(child: Text('没有找到匹配的工作区域'))
: Container( : Container(
color: Colors.white, color: Colors.white,
child: ListView.builder( child: ListView.builder(
itemCount: filtered.length, itemCount: filtered.length,
itemBuilder: (ctx, idx) => _buildRow(filtered[idx]), itemBuilder: (ctx, idx) => _buildRow(filtered[idx], 0),
),
), ),
)),
), ),
], ],
), ),

View File

@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
# In Windows, build-name is used as the major, minor, and patch parts # In Windows, build-name is used as the major, minor, and patch parts
# of the product and file versions while build-number is used as the build suffix. # of the product and file versions while build-number is used as the build suffix.
version: 2.1.2+9 version: 2.1.2+8
environment: environment:
sdk: ^3.7.0 sdk: ^3.7.0