|
@@ -0,0 +1,303 @@
|
|
|
+import 'package:fis_common/index.dart';
|
|
|
+import 'package:flutter/material.dart';
|
|
|
+import 'package:get/get.dart';
|
|
|
+import 'package:vitalapp/components/appbar.dart';
|
|
|
+import 'package:vitalapp/components/search_input.dart';
|
|
|
+import 'package:vitalapp/managers/interfaces/models/csv_data.dart';
|
|
|
+
|
|
|
+class CSVDatasView extends StatefulWidget {
|
|
|
+ final List<CsvRevord> records;
|
|
|
+
|
|
|
+ CSVDatasView(this.records);
|
|
|
+
|
|
|
+ @override
|
|
|
+ State<StatefulWidget> createState() {
|
|
|
+ return CSVDatasViewState();
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+class CSVDatasViewState extends State<CSVDatasView> {
|
|
|
+ final _searchController = TextEditingController();
|
|
|
+ List<CsvRevord> _pendingUploadDatas = [];
|
|
|
+ List<CsvRevord> _errorDatas = [];
|
|
|
+ List<String> _selectedCsvDataCodes = [];
|
|
|
+ bool _isShowErrorData = false;
|
|
|
+ bool _isSelectedAll = true;
|
|
|
+
|
|
|
+ @override
|
|
|
+ void initState() {
|
|
|
+ //sampleBarcode对应的就是体检系统的条码
|
|
|
+ _pendingUploadDatas = widget.records
|
|
|
+ .where((element) =>
|
|
|
+ element.csvDatas
|
|
|
+ .firstWhereOrNull((element) => element.key == "sampleBarcode")
|
|
|
+ ?.value
|
|
|
+ .isNotNullOrEmpty ??
|
|
|
+ false)
|
|
|
+ .toList();
|
|
|
+ _errorDatas = widget.records
|
|
|
+ .where((element) =>
|
|
|
+ element.csvDatas
|
|
|
+ .firstWhereOrNull((element) => element.key == "sampleBarcode")
|
|
|
+ ?.value
|
|
|
+ .isNullOrEmpty ??
|
|
|
+ false)
|
|
|
+ .toList();
|
|
|
+ _selectedAll();
|
|
|
+ super.initState();
|
|
|
+ }
|
|
|
+
|
|
|
+ @override
|
|
|
+ Widget build(BuildContext context) {
|
|
|
+ return Scaffold(
|
|
|
+ backgroundColor: Colors.white,
|
|
|
+ appBar: VAppBar(
|
|
|
+ titleWidget: Text(
|
|
|
+ "数据上传",
|
|
|
+ style: const TextStyle(fontSize: 24, color: Colors.white),
|
|
|
+ ),
|
|
|
+ actions: [
|
|
|
+ if (!_isShowErrorData)
|
|
|
+ TextButton.icon(
|
|
|
+ onPressed: () async {},
|
|
|
+ label: Text(
|
|
|
+ '提交',
|
|
|
+ style: TextStyle(fontSize: 20, color: Colors.white),
|
|
|
+ ),
|
|
|
+ icon: Icon(Icons.save, size: 32, color: Colors.white),
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ body: _buildBody(),
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ Widget _buildBody() {
|
|
|
+ return Column(
|
|
|
+ children: [
|
|
|
+ Container(
|
|
|
+ height: 40,
|
|
|
+ child: _buildHead(),
|
|
|
+ margin: EdgeInsets.symmetric(vertical: 10),
|
|
|
+ ),
|
|
|
+ Expanded(
|
|
|
+ child: ListView(
|
|
|
+ children: [
|
|
|
+ if (_isShowErrorData) ...[
|
|
|
+ ..._errorDatas.map((e) => _buildRecord(e)),
|
|
|
+ ] else ...[
|
|
|
+ ..._pendingUploadDatas.map((e) => _buildRecord(e)),
|
|
|
+ ],
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ Widget _buildRecord(CsvRevord e) {
|
|
|
+ List<CsvData> csvDatas = e.csvDatas;
|
|
|
+ List<Widget> children = [];
|
|
|
+ for (var c in csvDatas) {
|
|
|
+ children.add(_buildItem(c));
|
|
|
+ }
|
|
|
+ var code = csvDatas
|
|
|
+ .firstWhereOrNull((element) => element.key == "sampleBarcode")
|
|
|
+ ?.value ??
|
|
|
+ '';
|
|
|
+ return Container(
|
|
|
+ padding: EdgeInsets.only(left: 10),
|
|
|
+ width: MediaQuery.of(context).size.width,
|
|
|
+ child: Row(
|
|
|
+ children: [
|
|
|
+ if (!_isShowErrorData)
|
|
|
+ Checkbox(
|
|
|
+ value: _selectedCsvDataCodes.contains(code),
|
|
|
+ onChanged: (v) {
|
|
|
+ if (_selectedCsvDataCodes.contains(code)) {
|
|
|
+ _selectedCsvDataCodes.remove(code);
|
|
|
+ } else {
|
|
|
+ _selectedCsvDataCodes.add(code);
|
|
|
+ }
|
|
|
+ setState(() {});
|
|
|
+ },
|
|
|
+ ),
|
|
|
+ Expanded(
|
|
|
+ child: Wrap(
|
|
|
+ children: children,
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ decoration: BoxDecoration(
|
|
|
+ border: Border(
|
|
|
+ bottom: BorderSide(
|
|
|
+ color: Colors.grey[300]!, // 边框颜色
|
|
|
+ width: 1.0, // 边框宽度
|
|
|
+ ),
|
|
|
+ )),
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ Widget _buildItem(CsvData c) {
|
|
|
+ if (c.value.isEmpty) {
|
|
|
+ return SizedBox();
|
|
|
+ }
|
|
|
+ String value = c.value;
|
|
|
+ if (c.unit.isNotNullOrEmpty) {
|
|
|
+ value += " " + c.unit!;
|
|
|
+ }
|
|
|
+ String title = c.des ?? '';
|
|
|
+ if (title.isNotEmpty) {
|
|
|
+ title += ":";
|
|
|
+ }
|
|
|
+ return Row(
|
|
|
+ mainAxisSize: MainAxisSize.min,
|
|
|
+ children: [
|
|
|
+ Text(
|
|
|
+ title,
|
|
|
+ style: TextStyle(fontWeight: FontWeight.bold),
|
|
|
+ ),
|
|
|
+ SizedBox(
|
|
|
+ width: 3,
|
|
|
+ ),
|
|
|
+ Text(value),
|
|
|
+ SizedBox(
|
|
|
+ width: 15,
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ Widget _buildHead() {
|
|
|
+ return Row(
|
|
|
+ children: [
|
|
|
+ SizedBox(width: 10),
|
|
|
+ if (!_isShowErrorData)
|
|
|
+ Checkbox(
|
|
|
+ value: _isSelectedAll,
|
|
|
+ onChanged: (v) {
|
|
|
+ _isSelectedAll = !_isSelectedAll;
|
|
|
+ _selectedCsvDataCodes.clear();
|
|
|
+ if (_isSelectedAll) {
|
|
|
+ _selectedAll();
|
|
|
+ }
|
|
|
+ setState(() {});
|
|
|
+ },
|
|
|
+ ),
|
|
|
+ _buildSearchInput(),
|
|
|
+ SizedBox(width: 20),
|
|
|
+ _buildTips(),
|
|
|
+ SizedBox(width: 10),
|
|
|
+ _buildSwichButton(),
|
|
|
+ if (_isShowErrorData) ...[
|
|
|
+ SizedBox(width: 10),
|
|
|
+ Icon(
|
|
|
+ Icons.warning,
|
|
|
+ color: Colors.orange,
|
|
|
+ ),
|
|
|
+ Text(
|
|
|
+ "请检查样本条码是否正确!",
|
|
|
+ style: TextStyle(color: Colors.orange),
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ ],
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ void _selectedAll() {
|
|
|
+ for (var data in _pendingUploadDatas) {
|
|
|
+ var code = data.csvDatas
|
|
|
+ .firstWhereOrNull((element) => element.key == "sampleBarcode")
|
|
|
+ ?.value ??
|
|
|
+ '';
|
|
|
+ _selectedCsvDataCodes.add(code);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ Widget _buildSearchInput() {
|
|
|
+ return Container(
|
|
|
+ width: 400,
|
|
|
+ margin: EdgeInsets.only(left: 20),
|
|
|
+ child: VSearchInput(
|
|
|
+ textEditingController: _searchController,
|
|
|
+ placeholder: "请输入姓名",
|
|
|
+ onSearch: (v) {
|
|
|
+ if (_isShowErrorData) {
|
|
|
+ var errorDatas = widget.records
|
|
|
+ .where((element) =>
|
|
|
+ element.csvDatas
|
|
|
+ .firstWhereOrNull(
|
|
|
+ (element) => element.key == "sampleBarcode")
|
|
|
+ ?.value
|
|
|
+ .isNullOrEmpty ??
|
|
|
+ false)
|
|
|
+ .toList();
|
|
|
+ _errorDatas = errorDatas
|
|
|
+ .where((element) =>
|
|
|
+ element.csvDatas
|
|
|
+ .firstWhereOrNull(
|
|
|
+ (element) => element.key == "patientName")
|
|
|
+ ?.value
|
|
|
+ .contains(v) ??
|
|
|
+ false)
|
|
|
+ .toList();
|
|
|
+ } else {
|
|
|
+ var pendingUploadDatas = widget.records
|
|
|
+ .where((element) =>
|
|
|
+ element.csvDatas
|
|
|
+ .firstWhereOrNull(
|
|
|
+ (element) => element.key == "sampleBarcode")
|
|
|
+ ?.value
|
|
|
+ .isNotNullOrEmpty ??
|
|
|
+ false)
|
|
|
+ .toList();
|
|
|
+ _pendingUploadDatas = pendingUploadDatas
|
|
|
+ .where((element) =>
|
|
|
+ element.csvDatas
|
|
|
+ .firstWhereOrNull(
|
|
|
+ (element) => element.key == "patientName")
|
|
|
+ ?.value
|
|
|
+ .contains(v) ??
|
|
|
+ false)
|
|
|
+ .toList();
|
|
|
+ if (_isSelectedAll) {
|
|
|
+ _selectedCsvDataCodes.clear();
|
|
|
+ for (var data in _pendingUploadDatas) {
|
|
|
+ var code = data.csvDatas
|
|
|
+ .firstWhereOrNull(
|
|
|
+ (element) => element.key == "sampleBarcode")
|
|
|
+ ?.value ??
|
|
|
+ '';
|
|
|
+ _selectedCsvDataCodes.add(code);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ setState(() {});
|
|
|
+ },
|
|
|
+ ),
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ Widget _buildTips() {
|
|
|
+ var tips =
|
|
|
+ "共计 ${widget.records.length} 条数据,其中校验通过 ${_pendingUploadDatas.length} 条数据";
|
|
|
+ if (_errorDatas.length > 0) {
|
|
|
+ tips += ",校验不通过${_errorDatas.length}条数据";
|
|
|
+ }
|
|
|
+ return Text(tips);
|
|
|
+ }
|
|
|
+
|
|
|
+ Widget _buildSwichButton() {
|
|
|
+ return OutlinedButton(
|
|
|
+ onPressed: () {
|
|
|
+ setState(() {
|
|
|
+ _isShowErrorData = !_isShowErrorData;
|
|
|
+ });
|
|
|
+ },
|
|
|
+ child: Text(
|
|
|
+ _isShowErrorData ? "切换至校验通过数据" : "切换至校验不通过数据",
|
|
|
+ ),
|
|
|
+ );
|
|
|
+ }
|
|
|
+}
|