瀏覽代碼

报告模板页面雏形

loki.wu 2 年之前
父節點
當前提交
5066401524

+ 19 - 0
.vscode/launch.json

@@ -0,0 +1,19 @@
+{
+    // 使用 IntelliSense 了解相关属性。
+    // 悬停以查看现有属性的描述。
+    // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
+    "version": "0.2.0",
+    "configurations": [
+    
+        {
+            "name": "Flyinsono",
+            "request": "launch",
+            "type": "dart",
+            "program": "lib/main.dart",
+            "args": [
+                "--web-port=8081",
+                "--web-hostname=127.0.0.1",
+            ]
+        }
+    ]
+}

+ 0 - 0
lib/assets/Default.json → assets/default.json


+ 83 - 61
lib/main.dart

@@ -1,4 +1,11 @@
+import 'dart:convert';
+
+import 'package:fis_lib_report/pages/block_element_page.dart';
+import 'package:fis_lib_report/report/interfaces/block_element.dart';
+import 'package:fis_lib_report/report/report_template_document.dart';
+import 'package:fis_lib_report/report/rt_thickness.dart';
 import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
 
 void main() {
   runApp(const MyApp());
@@ -32,15 +39,6 @@ class MyApp extends StatelessWidget {
 class MyHomePage extends StatefulWidget {
   const MyHomePage({Key? key, required this.title}) : super(key: key);
 
-  // This widget is the home page of your application. It is stateful, meaning
-  // that it has a State object (defined below) that contains fields that affect
-  // how it looks.
-
-  // This class is the configuration for the state. It holds the values (in this
-  // case the title) provided by the parent (in this case the App widget) and
-  // used by the build method of the State. Fields in a Widget subclass are
-  // always marked "final".
-
   final String title;
 
   @override
@@ -48,68 +46,92 @@ class MyHomePage extends StatefulWidget {
 }
 
 class _MyHomePageState extends State<MyHomePage> {
-  int _counter = 0;
+  ReportTemplateDocument _reportTemplate = ReportTemplateDocument();
+  double _height = 0;
+  double _width = 0;
+  List<IBlockElement> _blocks = [];
+  List<IBlockElement> _header = [];
+  List<IBlockElement> _footer = [];
+  double _baseFontSize = 9.0;
+  double _footerDistance = 34.0;
+  double _footerHeight = 0;
+  EdgeInsetsGeometry _padding = const EdgeInsets.all(56.83);
+  @override
+  initState() {
+    _intitTemplate();
 
-  void _incrementCounter() {
-    setState(() {
-      // This call to setState tells the Flutter framework that something has
-      // changed in this State, which causes it to rerun the build method below
-      // so that the display can reflect the updated values. If we changed
-      // _counter without calling setState(), then the build method would not be
-      // called again, and so nothing would appear to happen.
-      _counter++;
-    });
+    super.initState();
   }
 
   @override
   Widget build(BuildContext context) {
-    // This method is rerun every time setState is called, for instance as done
-    // by the _incrementCounter method above.
-    //
-    // The Flutter framework has been optimized to make rerunning build methods
-    // fast, so that you can just rebuild anything that needs updating rather
-    // than having to individually change instances of widgets.
     return Scaffold(
-      appBar: AppBar(
-        // Here we take the value from the MyHomePage object that was created by
-        // the App.build method, and use it to set our appbar title.
-        title: Text(widget.title),
-      ),
-      body: Center(
-        // Center is a layout widget. It takes a single child and positions it
-        // in the middle of the parent.
+      body: Container(
+        decoration: _buildDecoration(),
+        padding: _padding,
+        height: _height * 2,
+        width: _width * 1.5,
         child: Column(
-          // Column is also a layout widget. It takes a list of children and
-          // arranges them vertically. By default, it sizes itself to fit its
-          // children horizontally, and tries to be as tall as its parent.
-          //
-          // Invoke "debug painting" (press "p" in the console, choose the
-          // "Toggle Debug Paint" action from the Flutter Inspector in Android
-          // Studio, or the "Toggle Debug Paint" command in Visual Studio Code)
-          // to see the wireframe for each widget.
-          //
-          // Column has various properties to control how it sizes itself and
-          // how it positions its children. Here we use mainAxisAlignment to
-          // center the children vertically; the main axis here is the vertical
-          // axis because Columns are vertical (the cross axis would be
-          // horizontal).
-          mainAxisAlignment: MainAxisAlignment.center,
-          children: <Widget>[
-            const Text(
-              'You have pushed the button this many times:',
-            ),
-            Text(
-              '$_counter',
-              style: Theme.of(context).textTheme.headline4,
-            ),
+          mainAxisSize: MainAxisSize.min,
+          mainAxisAlignment: MainAxisAlignment.start,
+          children: [
+            ..._header.map((head) {
+              return BlockElementPage(element: head);
+            }),
+            ..._blocks.map((block) {
+              return BlockElementPage(element: block);
+            }),
+            const SizedBox(height: 10),
+            ..._footer.map((footer) {
+              return BlockElementPage(element: footer);
+            }),
           ],
         ),
       ),
-      floatingActionButton: FloatingActionButton(
-        onPressed: _incrementCounter,
-        tooltip: 'Increment',
-        child: const Icon(Icons.add),
-      ), // This trailing comma makes auto-formatting nicer for build methods.
     );
   }
+
+  BoxDecoration _buildDecoration() {
+    return BoxDecoration(
+        border: Border.all(
+          width: 0.5,
+          color: const Color.fromARGB(255, 83, 83, 83),
+        ),
+        color: Colors.grey[200]);
+  }
+
+  void _intitTemplate() {
+    rootBundle.loadString('assets/default.json').then((jsonStr) {
+      final reportMap = jsonDecode(jsonStr);
+      final template = ReportTemplateDocument.fromJson(reportMap);
+      _reportTemplate = template;
+      setState(() {
+        _initPage();
+      });
+    });
+  }
+
+  void _initPage() {
+    try {
+      _height = _reportTemplate.pageSize!.height ?? 841;
+      _width = _reportTemplate.pageSize!.width ?? 595;
+      _baseFontSize = _reportTemplate.baseFontSize ?? 14;
+      _footerDistance = _reportTemplate.footerDistance ?? 0;
+      _footerHeight = _reportTemplate.footerHeight ?? 0;
+      final pagePadding =
+          _reportTemplate.pagePadding ?? RTThickness.uniform(56);
+      _padding = EdgeInsets.only(
+        left: pagePadding.left ?? 0,
+        right: pagePadding.right ?? 0,
+        top: pagePadding.top ?? 0,
+        bottom: pagePadding.bottom ?? 0,
+      );
+      _footer = _reportTemplate.footer ?? [];
+      _blocks = _reportTemplate.blocks ?? [];
+      _header = _reportTemplate.header ?? [];
+    } catch (e) {
+      _height = 841.8897637795275;
+      _width = 595.275590551181;
+    }
+  }
 }

+ 61 - 0
lib/pages/block_element_page.dart

@@ -0,0 +1,61 @@
+import 'package:fis_lib_report/pages/helpler.dart';
+import 'package:fis_lib_report/pages/paragraph_page.dart';
+import 'package:fis_lib_report/pages/rt_table.dart';
+import 'package:fis_lib_report/report/element_type.dart';
+import 'package:fis_lib_report/report/inputImageList.dart';
+import 'package:fis_lib_report/report/interfaces/block_element.dart';
+import 'package:fis_lib_report/report/paragraph.dart';
+import 'package:fis_lib_report/report/rt_table.dart';
+import 'package:flutter/material.dart';
+
+class BlockElementPage extends StatefulWidget {
+  const BlockElementPage({required this.element, Key? key}) : super(key: key);
+
+  final IBlockElement element;
+
+  @override
+  State<StatefulWidget> createState() {
+    return _BlockElementState();
+  }
+}
+
+class _BlockElementState extends State<BlockElementPage> {
+  ElementType? _type;
+  @override
+  initState() {
+    IBlockElement e = widget.element;
+    setState(() {
+      _type = e.elementType!;
+    });
+    super.initState();
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    if (_type != null && _type!.name == ElementType.rtTable!.name) {
+      RTTable t = widget.element as RTTable;
+      return RTTablePage(element: t);
+    } else if (_type != null && _type!.name == ElementType.paragraph!.name) {
+      final paragraph = widget.element as Paragraph;
+      return ParagraphPage(paragraph: paragraph);
+    } else if (_type != null && _type!.name == ElementType.imageList!.name) {
+      final inputImageList = widget.element as InputImageList;
+      return Container(
+        height: 200,
+        width: 700,
+        alignment: Alignment.center,
+        decoration: TestBoxDecoration.buildDecoration(),
+        child: const Text('图片选择框'),
+      );
+    }
+    return Container(
+      height: 20,
+      decoration: BoxDecoration(
+          border: Border.all(
+            width: 0.5,
+            color: const Color.fromARGB(255, 83, 83, 83),
+          ),
+          color: Colors.grey[200]),
+    );
+  }
+}

+ 67 - 0
lib/pages/components/multi_select.dart

@@ -0,0 +1,67 @@
+// Multi Select widget
+// This widget is reusable
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+
+class MultiSelect extends StatefulWidget {
+  final List<String> items;
+  const MultiSelect({Key? key, required this.items}) : super(key: key);
+
+  @override
+  State<StatefulWidget> createState() => _MultiSelectState();
+}
+
+class _MultiSelectState extends State<MultiSelect> {
+  // this variable holds the selected items
+  final List<String> _selectedItems = [];
+
+// This function is triggered when a checkbox is checked or unchecked
+  void _itemChange(String itemValue, bool isSelected) {
+    setState(() {
+      if (isSelected) {
+        _selectedItems.add(itemValue);
+      } else {
+        _selectedItems.remove(itemValue);
+      }
+    });
+  }
+
+// this function is called when the Cancel button is pressed
+  void _cancel() {
+    Navigator.pop(context);
+  }
+
+// this function is called when the Submit button is tapped
+  void _submit() {
+    Navigator.pop(context, _selectedItems);
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return AlertDialog(
+      title: const Text('Select Topics'),
+      content: SingleChildScrollView(
+        child: ListBody(
+          children: widget.items
+              .map((item) => CheckboxListTile(
+                    value: _selectedItems.contains(item),
+                    title: Text(item),
+                    controlAffinity: ListTileControlAffinity.leading,
+                    onChanged: (isChecked) => _itemChange(item, isChecked!),
+                  ))
+              .toList(),
+        ),
+      ),
+      actions: [
+        TextButton(
+          child: const Text('Cancel'),
+          onPressed: _cancel,
+        ),
+        ElevatedButton(
+          child: const Text('Submit'),
+          onPressed: _submit,
+        ),
+      ],
+    );
+  }
+}

+ 13 - 0
lib/pages/helpler.dart

@@ -0,0 +1,13 @@
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+
+class TestBoxDecoration {
+  static BoxDecoration buildDecoration() {
+    return BoxDecoration(
+        border: Border.all(
+          width: 0.5,
+          color: const Color.fromARGB(255, 83, 83, 83),
+        ),
+        color: Colors.grey[200]);
+  }
+}

+ 172 - 0
lib/pages/paragraph_page.dart

@@ -0,0 +1,172 @@
+import 'package:fis_lib_report/pages/components/multi_select.dart';
+import 'package:fis_lib_report/pages/helpler.dart';
+import 'package:fis_lib_report/report/dateTimeElement.dart';
+import 'package:fis_lib_report/report/element_type.dart';
+import 'package:fis_lib_report/report/inputText.dart';
+import 'package:fis_lib_report/report/interfaces/element.dart';
+import 'package:fis_lib_report/report/interfaces/position_layout.dart';
+import 'package:fis_lib_report/report/multiSelected.dart';
+import 'package:fis_lib_report/report/paragraph.dart';
+import 'package:fis_lib_report/report/singleSelected.dart';
+import 'package:fis_lib_report/report/staticText.dart';
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+
+class ParagraphPage extends StatefulWidget {
+  final Paragraph paragraph;
+
+  ParagraphPage({Key? key, required this.paragraph}) : super(key: key);
+
+  @override
+  State<StatefulWidget> createState() {
+    return _ParagraphState();
+  }
+}
+
+class _ParagraphState extends State<ParagraphPage> {
+  final _controller = TextEditingController();
+  List<IElement>? _elements = [];
+  int _itemCount = 0;
+
+  @override
+  initState() {
+    _elements = widget.paragraph.elements;
+    _itemCount = _elements!.length;
+    super.initState();
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    if (_itemCount == 0) {
+      return const SizedBox();
+    }
+    return Row(
+      mainAxisAlignment:
+          widget.paragraph.horizontalAlignment == HorizontalLayout.Center
+              ? MainAxisAlignment.center
+              : MainAxisAlignment.start,
+      crossAxisAlignment: CrossAxisAlignment.start,
+      children: [
+        ..._elements!.map((element) {
+          if (element.elementType!.name == ElementType.inputText!.name) {
+            InputText inputText = element as InputText;
+            return Container(
+              color: Colors.white,
+              width: inputText.lineWidth! * 1.5,
+              height: inputText.lineWidth! / 3.5,
+              child: TextField(
+                decoration: const InputDecoration(
+                  isCollapsed: true,
+                  contentPadding:
+                      EdgeInsets.symmetric(vertical: 5, horizontal: 5),
+                  hintText: '',
+                  border: OutlineInputBorder(
+                    borderRadius: BorderRadius.all(Radius.circular(4.0)),
+                  ),
+                ),
+                maxLines: 6,
+                controller: _controller,
+                textAlign: TextAlign.start,
+                style: const TextStyle(
+                  fontSize: 18,
+                ),
+              ),
+            );
+          } else if (element.elementType!.name ==
+              ElementType.staticText!.name) {
+            StaticText staticText = element as StaticText;
+            return SizedBox(
+              width: staticText.text!.length > 10 ? 300 : 110,
+              child: Text(staticText.text!),
+            );
+          } else if (element.elementType!.name ==
+              ElementType.singleSelected!.name) {
+            SingleSelected singleSelected = element as SingleSelected;
+
+            List<String>? values = singleSelected.items;
+
+            if (values != null && values.isNotEmpty) {
+              return SizedBox(
+                width: 104,
+                height: 30,
+                child: DropdownButton(
+                  onChanged: (Object? value) {},
+                  items: values.map<DropdownMenuItem<String>>((String value) {
+                    return DropdownMenuItem<String>(
+                      value: value,
+                      child: Text(value),
+                    );
+                  }).toList(),
+                ),
+              );
+            }
+          } else if (element.elementType!.name == ElementType.line!.name) {
+            return Container(
+              margin: const EdgeInsets.symmetric(vertical: 10),
+              padding: const EdgeInsets.symmetric(horizontal: 0),
+              width: 760,
+              child: const Divider(
+                height: 1,
+                thickness: 1,
+                color: Colors.black,
+              ),
+            );
+          } else if (element.elementType!.name == ElementType.dateTime!.name) {
+            final dateTime = element as DateTimeElement;
+            final currentDateTime = DateTime.now();
+            final text = currentDateTime.toString().substring(0, 10);
+            return Container(
+              width: 110,
+              child: Text(text),
+            );
+          } else if (element.elementType!.name ==
+              ElementType.multiSelected!.name) {
+            final multiSelected = element as MultiSelected;
+
+            List<String>? values = multiSelected.items;
+            return SizedBox(
+              width: 120,
+              height: 30,
+              child: TextField(
+                cursorWidth: 0,
+                mouseCursor: SystemMouseCursors.click,
+                decoration: const InputDecoration(
+                  suffixIcon: Icon(Icons.arrow_drop_down),
+                ),
+                onTap: () {
+                  _showMultiSelect(values);
+                },
+              ),
+            );
+          }
+          return Container(
+            height: 30,
+            width: 80,
+            decoration: BoxDecoration(
+                border: Border.all(
+                  width: 0.5,
+                  color: const Color.fromARGB(255, 83, 83, 83),
+                ),
+                color: Colors.grey[200]),
+            child: const Text('组件占位'),
+          );
+        }),
+      ],
+    );
+  }
+
+  void _showMultiSelect(List<String>? items) async {
+    // a list of selectable items
+    // these items can be hard-coded or dynamically fetched from a database/API
+    if (items!.isEmpty) {
+      return;
+    }
+
+    final List<String>? results = await showDialog(
+      context: context,
+      builder: (BuildContext context) {
+        return MultiSelect(items: items);
+      },
+    );
+  }
+}

+ 53 - 0
lib/pages/rt_cell.dart

@@ -0,0 +1,53 @@
+import 'package:fis_lib_report/pages/helpler.dart';
+import 'package:fis_lib_report/pages/paragraph_page.dart';
+import 'package:fis_lib_report/report/element_type.dart';
+import 'package:fis_lib_report/report/interfaces/block_element.dart';
+import 'package:fis_lib_report/report/paragraph.dart';
+import 'package:fis_lib_report/report/rt_Cell.dart';
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+
+class RTCellPage extends StatefulWidget {
+  const RTCellPage({required this.cell, Key? key}) : super(key: key);
+
+  final RTCell cell;
+
+  @override
+  State<StatefulWidget> createState() {
+    return _RTCellState();
+  }
+}
+
+class _RTCellState extends State<RTCellPage> {
+  List<IBlockElement>? _blocks = [];
+
+  @override
+  initState() {
+    _blocks = widget.cell.blocks;
+    super.initState();
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return _blocks!.isEmpty
+        ? Container(
+            height: 30,
+            decoration: TestBoxDecoration.buildDecoration(),
+          )
+        : Column(
+            mainAxisSize: MainAxisSize.min,
+            mainAxisAlignment: MainAxisAlignment.start,
+            children: [
+              ..._blocks!.map((e) {
+                if (e.elementType!.name == ElementType.paragraph!.name) {
+                  final paragraph = e as Paragraph;
+                  return ParagraphPage(paragraph: paragraph);
+                }
+                return Container(
+                    height: 30,
+                    decoration: TestBoxDecoration.buildDecoration());
+              }),
+            ],
+          );
+  }
+}

+ 65 - 0
lib/pages/rt_table.dart

@@ -0,0 +1,65 @@
+import 'dart:math';
+
+import 'package:fis_lib_report/pages/rt_cell.dart';
+import 'package:fis_lib_report/report/cellPostion.dart';
+import 'package:fis_lib_report/report/interfaces/block_element.dart';
+import 'package:fis_lib_report/report/interfaces/cell.dart';
+import 'package:fis_lib_report/report/interfaces/rt_table.dart';
+import 'package:fis_lib_report/report/rt_Cell.dart';
+import 'package:fis_lib_report/report/rt_table.dart';
+import 'package:flutter/cupertino.dart';
+
+class RTTablePage extends StatefulWidget {
+  const RTTablePage({required this.element, Key? key}) : super(key: key);
+
+  final RTTable element;
+
+  @override
+  State<StatefulWidget> createState() {
+    return _RTTableState();
+  }
+}
+
+class _RTTableState extends State<RTTablePage> {
+  Map<CellPostion, ICell>? _cells;
+  List<ICell>? _values = [];
+  int? _column;
+  int? _row;
+
+  @override
+  initState() {
+    super.initState();
+    _cells = widget.element.cells ?? {};
+    List<CellPostion> cellPostions = _cells!.keys.toList();
+    cellPostions.sort(((a, b) => (b.column!).compareTo(a.column!)));
+    // +1 为列数
+    _column = cellPostions.first.column! + 1;
+    _values = _cells!.values.toList();
+    _row = widget.element.rowDefinitions!.length;
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return SizedBox(
+      height: _row! * 40,
+      child: (_column == null || _column == 0 || _values!.isEmpty)
+          ? const SizedBox()
+          : GridView.builder(
+              controller: ScrollController(),
+              shrinkWrap: true,
+              itemCount: _values!.length,
+              gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
+                crossAxisCount: _column!,
+                childAspectRatio: _column == 3 ? 6.6 : 24,
+              ),
+              itemBuilder: (BuildContext context, int index) {
+                final cell = _values![index];
+                RTCell c = cell as RTCell;
+                return RTCellPage(
+                  cell: c,
+                );
+              },
+            ),
+    );
+  }
+}

+ 1 - 1
lib/report/interfaces/block_element.dart

@@ -1,5 +1,5 @@
 import 'package:fis_lib_report/report/interfaces/element.dart';
 
-abstract class IBlockElement {
+abstract class IBlockElement extends IElement {
   List<IElement>? elements;
 }

+ 3 - 2
pubspec.yaml

@@ -30,6 +30,7 @@ dependencies:
   flutter:
     sdk: flutter
 
+
   uuid: ^3.0.6
   dio: 4.0.6
   # The following adds the Cupertino Icons font to your application.
@@ -59,8 +60,8 @@ flutter:
   uses-material-design: true
 
   # To add assets to your application, add an assets section, like this:
-  # assets:
-  #   - images/a_dot_burr.jpeg
+  assets:
+     - assets/default.json
   #   - images/a_dot_ham.jpeg
 
   # An image asset can refer to one or more resolution-specific "variants", see